Apache Struts2 文件上传漏洞分析(CVE-2023-50164)

1. 前言

官方公告
https://cwiki.apache.org/confluence/display/WW/S2-066

漏洞描述:
攻击者可以操纵文件上传参数以启用路径遍历,在某些情况下,这可能导致上传可用于执行远程代码执行的恶意文件。

影响版本:
Struts 2.0.0 – Struts 2.3.37 (EOL)
Struts 2.5.0 – Struts 2.5.32
Struts 6.0.0 – Struts 6.3.0

2. 环境搭建

新建一个项目,Archetype 选择org.apache.maven.archetypes:maven-archetype-webapp
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
pom.xml 中添加 Struts2 依赖,以 6.3.0 版本为例,tomcat 也添加上,以便获取当前路径;
<dependency>  <groupId>org.apache.struts</groupId>  <artifactId>struts2-core</artifactId>  <version>6.3.0</version></dependency><dependency>  <groupId>org.apache.tomcat</groupId>  <artifactId>tomcat-catalina</artifactId>  <version>8.5.81</version></dependency>
web.xml 中配置过滤器:
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app>  <display-name>Archetype Created Web Application</display-name>  <filter>    <filter-name>struts2</filter-name>    <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>  </filter>  <filter-mapping>    <filter-name>struts2</filter-name>    <url-pattern>*.action</url-pattern>  </filter-mapping></web-app>
文件上传的 action:
package blckder02.struts2.action;import com.opensymphony.xwork2.ActionSupport;import org.apache.commons.io.FileUtils;import java.io.File;import java.io.IOException;publicclass UploadAction extends ActionSupport {    private File myfile;    private String myfileContentType;    private String myfileFileName;    private String destpath;    public String execute() {        destpath = ServletActionContext.getServletContext().getRealPath("/")+"uploads\upload\";        try{            System.out.println("Src File name: " + myfile);            System.out.println("Dst File name: " + myfileFileName);            File destFile = new File(destpath, myfileFileName);            FileUtils.copyFile(myfile, destFile);            return SUCCESS;        } catch (IOException | NullPointerException e) {            e.printStackTrace();            return ERROR;        }    }    public File getMyfile() {        return myfile;    }    public void setMyfile(File myfile) {        this.myfile = myfile;    }    public String getMyfileContentType() {        return myfileContentType;    }    public void setMyfileContentType(String myfileContentType) {        this.myfileContentType = myfileContentType;    }    public String getMyfileFileName() {        return myfileFileName;    }    public void setMyfileFileName(String myfileFileName) {        this.myfileFileName = myfileFileName;    }}
uopload.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %><%@ taglib prefix="s" uri="/struts-tags"%><html><head>    <title>Upload</title></head><body><h2>Upload</h2><br/><form action="upload.action" method="post" enctype="multipart/form-data">    <s:label for="myfile">Please upload your file</s:label><br/>    <input type="file" name="myfile"/>    <input type="submit" value="Upload"/></form></body></html>
success.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %><%@ taglib prefix="s" uri="/struts-tags"%><html><head>    <title>Success</title></head><body>You have successfully uploaded <s:property value="myfileFileName"/></body></html>
struts.xml 中配置 action 的解析,继承了 struts-default 包;
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE struts PUBLIC        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"        "http://struts.apache.org/dtds/struts-2.0.dtd"><struts>    <package name="default" namespace="/" extends="struts-default">        <action name="upload" class="blckder02.struts2.action.UploadAction" method="execute">            <result name="success">/success.jsp</result>            <result name="error">/upload.jsp</result>        </action>    </package></struts>
struts-default 包中使用了FileUploadInterceptor和ParametersInterceptor,在这里也是继承使用的,会对上传的文件以及参数进行拦截校验;
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
最后配置上 Tomcat 就可以运行了。

3. 漏洞复现

准备一个含有简单 jsp 木马的文件;
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
选择文件,上传抓包;
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
发送到 Repeater,修改请求包,将myfile参数名的第一个字母改为大写,再构造一个myfileFileName参数,参数值为想要文件保存后所在的路径及文件名。构造的参数名必须是第一个参数名+”FileName,不能随便构造。
上传成功后可以看到文件名为自定义的参数值;
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
访问 jsp,能成功执行命令,文件保存到了 /uploads 目录下,且文件名保存为构造传入的exec.jsp
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)

4. 漏洞分析

断点跟踪一下文件上传的流程,可以从org.apache.struts2.dispatcher.Dispatcher#serviceAction()断点,开始处理文件上传的 action;
可以看到 request 中含有一个文件类型参数
files和一个字符串类型的参数params
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
跟进,将myfileFileName参数封装成了 HttpParameters 对象,添加到了context中;
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
回到serviceAction()中,extraContext是前面createContextMap()创建的 context 对象,这里生成了一个 action 的代理对象,开始执行 action 流程。
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
接着进入 FileUploadInterceptor,从上传的请求中提取了文件、Content-Type 和文件名;可以看到保存文件名的键名是inputName + “FileName”,所以请求包中构造的参数名也得是这种形式,才能完成覆盖。
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
在调用multiWrapper.getFileNames()的时候,对文件名中/符号前的字符串进行了过滤,所以直接在文件名中进行路径穿越是不行的;
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
把这三个值添加到 HttpParameters 对象中后,actioncontext 里的参数就有四个了。
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
进入 ParametersInterceptor,在setParameters()中,将 HttpParameters 对象中的参数依次放入 TreeMap 对象中;
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
可以看到 TreeMap 对象中保存的参数顺序变了,是因为 TreeMap 对象会按参数名的大小顺序从小到大保存值;
TreeMap.put()方法中,会将新添入的参数名与已保存的参数名做比较;
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
比较逻辑就是逐字符比较,遇到不相同的字符就返回新增参数名与已保存参数名 ASCII 的差值;
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
差值为负,则说明新增参数名比已保存参数名的 ASCII 值小,将新增参数插入到已保存参数的前面,所以要保证构造的文件名比inputName + “FileName”生成的文件名的 ASCII 值大。
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
newStack.setParameter()进行参数绑定,遍历后myfileFileName的值已经被赋为S2-66.txt
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
跟进,调用task.execute()执行表达式;
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
一直跟进,会再次调用 setter 方法为myfileFileName赋值,所以第一次赋值的S2-66.txt就被覆盖为../exec.jsp了。
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
最后在 UploadAction 中保存文件,路径中拼接的文件名包含路径穿越符,便会解析保存到上一目录。
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)

5. 补丁分析

在 6.3.0.2版本中,HttpParameters 类的appendAll()中添加调用了remove()方法;遍历检查参数是否需要删除,remove()方法中添加使用了equalsIgnoreCase()方法来忽略大小写进行比较。

Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
断点进入,将MyfileFileNamemyfileFileName带入校验;
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
跟进,在StringLatin1.regionMatchesCI()中,将两个参数名进行了统一的大小写转换再进行比较,这里的比较结果相同,返回 true;
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)
于是将已经存在的同名参数(忽略大小写)删除了,后面就不存在二次调用 setter 方法来覆盖了。
Apache Struts2 文件上传漏洞分析(CVE-2023-50164)

参考链接:
https://y4tacker.github.io/2023/12/09/year/2023/12/Apache-Struts2-文件上传分析-S2-066/
https://xz.aliyun.com/t/13172


原文始发于微信公众号(中孚安全技术研究):Apache Struts2 文件上传漏洞分析(CVE-2023-50164)

版权声明:admin 发表于 2023年12月22日 上午8:41。
转载请注明:Apache Struts2 文件上传漏洞分析(CVE-2023-50164) | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...