阿里先知安全社区:【struts2 命令/代码执行漏洞分析系列】S2-003和S3-005
S2-003
漏洞信息
漏洞信息页面: https://cwiki.apache.org/confluence/display/WW/S2-003
漏洞成因官方概述:XWork ParameterInterceptors bypass allows OGNL statement execution
漏洞影响:
环境搭建
s2-003漏洞的payload用到了特殊字符,在高版本tomcat中会失败,需要使用tomcat6来测试。我使用的是6.0.9版本。此外导入的struts版本为2.0.11.2
漏洞利用
POC:
回显:
漏洞分析
在struts/xwork-2.0.5-sources.jar!/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java:177 获取到我们传入的参数
在getValueStack
之前,执行了一些初始化操作,比如:
将xwork.MethodAccessor.denyMethodExecution
设置为true
。为了能够调用方法,需要在poc中的第一部分将denyMethodExecution
设置为false
,之后才能任意代码执行。
跟入setParameters(action, stack, parameters);
至 struts/struts/xwork-2.0.5-sources.jar!/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java:201。此部分开始通过迭代器取出一个个传入的参数,并进行处理。
假设此时我传入的参数如下,注意这个与poc的不同在于,我将第一个\u0023
替换成了#
。:
跟入acceptableName
至 struts/xwork-2.0.5-sources.jar!/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java:271
跟入isAccepted(name)
这里通过简单的正则表达式[\p{Graph}&&[^,#:=]]*
来检测,防止传入恶意特殊字符开头如#
等。因此acceptableName
返回false,接下来的ognl表达式自然也不会执行了。
但如果传入经过编码后的payload。#
对应的unicode为\u0023
,八进制为\43
,则可以绕过上述的检测,也即导致acceptableName
为true,从而进一步执行。
在设置denyMethodExecution
为false后,poc的第二部分就是通过方法调用来执行任意命令了:
S2-005
漏洞信息
漏洞信息页面: https://cwiki.apache.org/confluence/display/WW/S2-005
漏洞成因官方概述:XWork ParameterInterceptors bypass allows remote command execution
漏洞影响:
漏洞分析
S2-005的出现时因为官方对S2-003的修补的不完全导致的。官方通过增加安全配置禁止静态方法调用(allowStaticMethodAcces)和类方法执行(MethodAccessor.den
yMethodExecution)等来修补。但同样的直接使用上面的技巧,更改poc为:
即可设置allowStaticMethodAccess
为true,和denyMethodExecution
为false,从而导致任意命令执行。可以参考Struts2漏洞分析与研究之S2-005漏洞分析和CVE-2010-1870: Struts2/XWork remote command execution。其余的代码调用等,与S2-003相同,分析见上。
ognl的解析
一个问题,为什么\u0023
形式的poc能够被解析呢?
https://github.com/CHYbeta/chybeta.github.io/blob/master/images/pic/20180507/8.png?raw=true
跟入setValue
至 struts/xwork-2.0.5-sources.jar!/com/opensymphony/xwork2/util/OgnlValueStack.java:170
跟入OgnlUtil.setValue
,struts/xwork-2.0.5-sources.jar!/com/opensymphony/xwork2/util/OgnlUtil.java:185
|
|
此处name即我们传入的参数(\u0023...
,跟入compile
中的o = Ognl.parseExpression(expression);
:
|
|
从topLevelExpression
就开始了进行语法分析工作。在获得(
的token为44后,接着进行expression();
的解析。
在其中会调用到 ognl/JavaCharStream.java 的readChar
。其中代码摘取部分如下:
读取\
,并在之后如果遇到了u
则进一步处理:
从而把\u0023
转换成了#
。之后执行ognl表达式时即执行"#context[\'xwork.MethodAccessor.denyMethodExecution\']=false"