"踩坑"S2-061复现与思考
字数 1318 2025-08-19 12:42:02
Struts2 S2-061漏洞分析与复现指南
漏洞概述
S2-061是Apache Struts2框架中的一个远程代码执行漏洞(CVE-2020-17530),是S2-059漏洞的补丁绕过版本。该漏洞源于Struts2框架对OGNL表达式的不当处理,允许攻击者构造恶意OGNL表达式实现远程代码执行。
漏洞原理
- 漏洞根源:Struts2框架在处理某些标签属性时,会对OGNL表达式进行二次解析
- 补丁绕过:S2-059的补丁通过黑名单机制限制某些包和类的访问,但可以通过特定方式绕过
- 关键点:利用
%{...}语法构造恶意OGNL表达式,通过二次解析触发漏洞
环境搭建
快速搭建方法
使用IntelliJ IDEA的Maven archetype快速创建Struts2项目:
- 选择
Create from archetype - 选择Struts2相关archetype
- 自动生成项目结构
必要代码修改
index.jsp修改:
<%@taglib prefix="s" uri="/struts-tags" %>
<%
String payload = request.getParameter("payload");
request.setAttribute("payload",payload);
%>
<html>
<body>
<s:a id="%{#request.payload}">testurl</s:a>
<s:form action="helloWorld">
<s:textfield label="What is your name?" name="name" />
<s:textfield label="What is the date?" name="dateNow" />
<s:submit />
</s:form>
</body>
</html>
IndexAction.java:
@Conversion()
public class IndexAction extends ActionSupport {
private String payload;
private Date now = new Date(System.currentTimeMillis());
@TypeConversion(converter = "Struts2.DateConverter")
public Date getDateNow() { return now; }
public String getPayload(String payload){
return payload;
}
public void setPayload(String payload){
this.payload=payload;
}
public String execute() throws Exception {
now = new Date(System.currentTimeMillis());
return SUCCESS;
}
}
漏洞分析
补丁分析
Struts2在S2-059中增加了以下检测逻辑:
public static boolean containsExpression(String expr) {
return expr != null && expr.contains("%{") && expr.contains("}");
}
当检测到%{和}时会返回true,阻止对表达式的嵌套解析。
漏洞利用链
- 获取ValueStack:通过request全局变量获取struts.valueStack
- 绕过限制:使用application根变量获取
org.apache.tomcat.InstanceManager到DefaultInstanceManager类 - 关键方法调用:利用
DefaultInstanceManager.newInstance()方法 - 获取Context对象:通过
org.apache.commons.collections.BeanMap的setBean和get方法 - 绕过黑名单:操作context对象覆盖Struts2的黑名单机制
- 代码执行:调用黑名单中的类实现RCE
利用过程中的关键点
- 获取InstanceManager:
public Object newInstance(String className) throws IllegalAccessException,
InvocationTargetException, NamingException, InstantiationException,
ClassNotFoundException {
Class<?> clazz = this.loadClassMaybePrivileged(className, this.classLoader);
return this.newInstance(clazz.newInstance(), clazz);
}
-
避免死循环:直接获取context会导致root对象循环引用,需要通过Map中转
-
黑名单覆盖:获取并操作Struts2的黑名单类,解除安全限制
漏洞复现
POC构造步骤
- 构造初始OGNL表达式获取ValueStack
- 通过application变量获取InstanceManager
- 利用BeanMap获取并操作context对象
- 覆盖安全沙箱设置
- 执行任意代码
注意事项
- 需要正确处理context对象的获取,避免死循环导致内存溢出
- 注意Struts2版本差异,不同版本可能需要调整利用方式
- 测试环境建议使用Struts 2.5.26版本
防御措施
- 升级到Struts2最新版本
- 禁用不必要的Struts2标签
- 实施严格的输入验证
- 使用WAF等防护设备拦截恶意请求
参考资源
总结
S2-061漏洞展示了补丁绕过的一种典型模式,通过深入分析框架机制和补丁实现,可以发现新的攻击面。理解该漏洞有助于安全研究人员更好地评估Web框架的安全性,并为防御类似漏洞提供思路。