0x00 漏洞概述
S2-008涉及多个漏洞,其中有着前面所存在的漏洞比如S2-007,还有S2-003&S2-005中通过参数执行OGNL表达式的方式,这里引出了一种新的执行方式,即通过Cookie的方式传参,最后一种则是devMode下支持直接执行OGNL表达式,本文着重记录最后一个漏洞的细节。
影响版本:2.0.0 – 2.3.17
复现版本:2.2.3
官方issue地址:https://cwiki.apache.org/confluence/display/WW/S2-008
0x01 环境搭建
由于此漏洞涉及到Struts2的执行流程,因此需要编写一个Action,内容无所谓,只是方便整个对应的路由出来,这样才能走到Struts2的执行流程里。
import com.opensymphony.xwork2.ActionSupport;
public class TestAction extends ActionSupport {
public String execute() throws Exception {
return "success";
}
public static void main(String[] args) {
}
}
接着编写一个struts.xml用于配置路由:
<?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>
<constant name="struts.devMode" value="true" />
<package name="st2-demo" extends="struts-default">
<action name="test" class="TestAction">
<result name="success">index.jsp</result>
</action>
</package>
</struts>
0x02 漏洞分析
处理DebugMode的拦截器为DebuggingInterceptor
,我们在其intercept方法下个断点,随后访问index.action触发拦截,相关代码:
org.apache.struts2.interceptor.debugging.DebuggingInterceptor#intercept
public String intercept(ActionInvocation inv) throws Exception {
boolean cont = true;
if (this.devMode) {
ActionContext ctx = ActionContext.getContext();
String type = this.getParameter("debug");
ctx.getParameters().remove("debug");
if ("xml".equals(type)) {
inv.addPreResultListener(new PreResultListener() {
public void beforeResult(ActionInvocation inv, String result) {
DebuggingInterceptor.this.printContext();
}
});
} else if ("console".equals(type)) {
this.consoleEnabled = true;
inv.addPreResultListener(new PreResultListener() {
public void beforeResult(ActionInvocation inv, String actionResult) {
String xml = "";
if (DebuggingInterceptor.this.enableXmlWithConsole) {
StringWriter writer = new StringWriter();
DebuggingInterceptor.this.printContext(new PrettyPrintWriter(writer));
xml = writer.toString();
xml = xml.replaceAll("&", "&");
xml = xml.replaceAll(">", ">");
xml = xml.replaceAll("<", "<");
}
ActionContext.getContext().put("debugXML", xml);
FreemarkerResult result = new FreemarkerResult();
result.setFreemarkerManager(DebuggingInterceptor.this.freemarkerManager);
result.setContentType("text/html");
result.setLocation("/org/apache/struts2/interceptor/debugging/console.ftl");
result.setParse(false);
try {
result.execute(inv);
} catch (Exception var6) {
DebuggingInterceptor.log.error("Unable to create debugging console", var6);
}
}
});
} else if ("command".equals(type)) {
ValueStack stack = (ValueStack)ctx.getSession().get("org.apache.struts2.interceptor.debugging.VALUE_STACK");
String cmd = this.getParameter("expression");
ServletActionContext.getRequest().setAttribute("decorator", "none");
HttpServletResponse res = ServletActionContext.getResponse();
res.setContentType("text/plain");
try {
PrintWriter writer = ServletActionContext.getResponse().getWriter();
writer.print(stack.findValue(cmd));
writer.close();
} catch (IOException var14) {
var14.printStackTrace();
}
cont = false;
}
}
if (cont) {
boolean var13 = false;
String var16;
try {
var13 = true;
var16 = inv.invoke();
var13 = false;
} finally {
if (var13) {
if (this.devMode && this.consoleEnabled) {
ActionContext ctx = ActionContext.getContext();
ctx.getSession().put("org.apache.struts2.interceptor.debugging.VALUE_STACK", ctx.get("com.opensymphony.xwork2.util.ValueStack.ValueStack"));
}
}
}
if (this.devMode && this.consoleEnabled) {
ActionContext ctx = ActionContext.getContext();
ctx.getSession().put("org.apache.struts2.interceptor.debugging.VALUE_STACK", ctx.get("com.opensymphony.xwork2.util.ValueStack.ValueStack"));
}
return var16;
} else {
return null;
}
}
从代码中可以看到,这里首先通过getParameter方法获取请求中debug参数对应的值,这里会根据debug的值进行不同的调用,分别为:
- xml
- console
- command
其中当type为command时,会从请求中继续读取expression参数对应的值,并通过stack.findValue执行它,最后写入返回页面中,因此可以利用之前S2-003&S2-005部分的Payload完成后续的RCE。
比较奇怪的是,在2.0.5版本的Struts2中,我发现这里是无法获取到stack的,因此stack为null,此时后续的调用会触发空指针异常,自然也就没办法执行OGNL表达式了,所以大家如果复现的话最好还是用稳定复现的版本进行复现。
利用效果:
0x03 修复方案
由于debug模式本身就不该开放在生产模式,因此由debug模式引发的漏洞并没有对应的修复方案(DIFF了一下Struts相关Jar得出的结论),最新的DIFF结果如下:
从上述DIFF的结果可以看出,实际上并没有修复此漏洞,在开Debug模式下同样会造成OGNL表达式的解析,如果要修,只能从根源上修复,那就是不在生产环节中开debug模式:P
发表评论
您还未登录,请先登录。
登录