1. 程式人生 > >Struts2漏洞利用原理及OGNL機制

Struts2漏洞利用原理及OGNL機制

基本 conf 數據集 fig 然而 example 所有 def 字符串類型

Struts2漏洞利用原理及OGNL機制研究

概述

在MVC開發框架中,數據會在MVC各個模塊中進行流轉。而這種流轉,也就會面臨一些困境,就是由於數據在不同MVC層次中表現出不同的形式和狀態而造成的:

View層—表現為字符串展示

數據在頁面上是一個扁平的、不帶數據類型的字符串,無論數據結構有多復雜,數據類型有多豐富,到了展示的時候,全都一視同仁的成為字符串在頁面上展現出來。數據在傳遞時,任何數據都都被當作字符串或字符串數組來進行。

Controller層—表現為java對象

在控制層,數據模型遵循java的語法和數據結構,所有的數據載體在Java世界中可以表現為豐富的數據結構和數據類型,你可以自行定義你喜歡的類,在類與類之間進行繼承、嵌套。我們通常會把這種模型稱之為復雜的對象樹。數據在傳遞時,將以對象的形式進行。

可以看到,數據在不同的MVC層次上,扮演的角色和表現形式不同,這是由於HTTP協議與java的面向對象性之間的不匹配造成的。如果數據在頁面和Java世界中互相傳遞,就會顯得不匹配。所以也就引出了幾個需要解決的問題:

1.當數據從View層傳遞到Controller層時,我們應該保證一個扁平而分散在各處的數據集合能以一定的規則設置到Java世界中的對象樹中去。同時,能夠靈活的進行由字符串類型到Java中各個類型的轉化。

2.當數據從Controller層傳遞到View層時,我們應該保證在View層能夠以某些簡易的規則對對象樹進行訪問。同時,在一定程度上控制對象樹中的數據的顯示格式。

我們稍微深入思考這個問題就會發現,解決數據由於表現形式的不同而發生流轉不匹配的問題對我們來說其實並不陌生。同樣的問題會發生在Java世界與數據庫世界中,面對這種對象與關系模型的不匹配,我們采用的解決方法是使用如hibernate,iBatis等框架來處理java對象與關系數據庫的匹配。

現在在Web層同樣也發生了不匹配,所以我們也需要使用一些工具來幫助我們解決問題。為了解決數據從View層傳遞到Controller層時的不匹配性,Struts2采納了XWork的一套完美方案。並且在此的基礎上,構建了一個完美的機制,從而比較完美的解決了數據流轉中的不匹配性。就是表達式引擎OGNL。它的作用就是幫助我們完成這種規則化的表達式與java對象的互相轉化(以上內容參考自Struts2 技術內幕)。

關於OGNL

OGNL(Object Graph Navigation Language)即對象圖形導航語言,是一個開源的表達式引擎。使用OGNL,你可以通過某種表達式語法,存取Java對象樹中的任意屬性、調用Java對象樹的方法、同時能夠自動實現必要的類型轉化。如果我們把表達式看做是一個帶有語義的字符串,那麽OGNL無疑成為了這個語義字符串與Java對象之間溝通的橋梁。我們可以輕松解決在數據流轉過程中所遇到的各種問題。
OGNL進行對象存取操作的API在Ognl.java文件中,分別是getValue、setValue兩個方法。getValue通過傳入的OGNL表達式,在給定的上下文環境中,從root對象裏取值:

技術分享



setValue通過傳入的OGNL表達式,在給定的上下文環境中,往root對象裏寫值:

技術分享



OGNL同時編寫了許多其它的方法來實現相同的功能,詳細可參考Ognl.java代碼。OGNL的API很簡單,無論何種復雜的功能,OGNL會將其最終映射到OGNL的三要素中通過調用底層引擎完成計算,OGNL的三要素即上述方法的三個參數expression、root、context。

OGNL三要素

Expression(表達式):

Expression規定OGNL要做什麽,其本質是一個帶有語法含義的字符串,這個字符串將規定操作的類型和操作的內容。OGNL支持的語法非常強大,從對象屬性、方法的訪問到簡單計算,甚至支持復雜的lambda表達式。

Root(根對象):

OGNL的root對象可以理解為OGNL要操作的對象,表達式規定OGNL要幹什麽,root則指定對誰進行操作。OGNL的root對象實際上是一個java對象,是所有OGNL操作的實際載體。

Context(上下文):

有了表達式和根對象,已經可以使用OGNL的基本功能了。例如,根據表達式對root對象進行getvalue、setvalue操作。
不過事實上在OGNL內部,所有的操作都會在一個特定的數據環境中運行,這個數據環境就是OGNL的上下文。簡單說就是上下文將規定OGNL的操作在哪裏進行。
OGNL的上下文環境是一個MAP結構,定義為OgnlContext,root對象也會被添加到上下文環境中,作為一個特殊的變量進行處理。

OGNL基本操作

對root對象的訪問:

針對OGNL的root對象的對象樹的訪問是通過使用‘點號’將對象的引用串聯起來實現的。通過這種方式,OGNL實際上將一個樹形的對象結構轉化為一個鏈式結構的字符串來表達語義,如下:
//獲取root對象中的name屬性值
name
//獲取root對象department屬性中的name屬性值
department.name
//獲取root對象department屬性中的manager屬性中的name屬性值
department.manager.name

對上下文環境的訪問:

Context上下文是一個map結構,訪問上下文參數時需要通過#符號加上鏈式表達式來進行,從而表示與訪問root對象的區別,如下:
//獲取上下文環境中名為introduction的對象的值
introduction
//獲取上下文環境中名為parameters的對象中的user對象中名為name屬性的值
#parameters.user.name

對靜態變量、方法的訪問:

在OGNL中,對於靜態變量、方法的訪問通過@[class]@[field/method]訪問,這裏的類名要帶著包名。如下
//訪問com.example.core.Resource類中名為img的屬性值
@[email protected]
//調用com.example.core.Resource類中名為get的方法
@[email protected]()

訪問調用:

//調用root對象中的group屬性中users的size()方法
group.users.size()
//調用root對象中group中的containsUser方法,並傳遞上下文環境中名為user值作為參數
group.containsUser(#user)

構造對象

OGNL支持直接通過表達式來構造對象,構造的方式主要包括三種:

1.構造List:使用{},中間使用逗號隔開元素的方式表達列表

2.構造map:使用#{},中間使用逗號隔開鍵值對,並使用冒號隔開key和value

3.構造對象:直接使用已知的對象構造函數來構造對象

可以看到OGNL在語法層面上所表現出來的強大之處,不僅能夠直接對容器對象構造提供語法層面支持,還能夠對任意java對象提供支持。然而正因為OGNL過於強大,它也造成了諸多安全問題。惡意攻擊者通過構造特定數據帶入OGNL表達式解析並執行,而OGNL可以用來獲取和設置Java對象的屬性,同時也可以對服務端對象進行修改,所以只要繞過沙盒惡意攻擊者甚至可以執行系統命令進行系統攻擊。

深入OGNL實現類

在OGNL三要素中,context上下文環境為OGNL提供計算運行的環境,這個運行環境在OGNL內部由OgnlContext類實現,它是一個map結構。在OGNL.java中可以看到OgnlContext會在OGNL計算時動態創建,同時傳入map結構的context,並根據傳入的參數設定OGNL計算的一些默認行為以及設置root對象。
技術分享


這裏看到MemberAccess屬性,在struts2漏洞利用中經常看到。轉到OgnlContext類中查看。
技術分享


MemberAccess指定OGNL的對象屬性、方法訪問策略,這裏初始默認情況下是flase,即默認不允許訪問。在struts2中SecurityMemberAccess類封裝了DefaultMeberAccess類並進行了擴展,指定是否支持訪問靜態方法以及通過指定正則表達式來規定某些屬性是否能夠被訪問。其中allowStaticMethodAccess默認為flase,即默認禁止訪問靜態方法。

技術分享



另外在OGNL實現了MethodAccessor及PropertyAccessor類規定OGNL在訪問方法和屬性時的實現方式,詳細可參考代碼文件。

技術分享


在struts2中,XWorkMethodAccessor類對MethodAccessor進行了封裝,其中在callStaticMethod方法中可以看到,程序首先從上下文獲取到DENY_METHOD_EXECUTION值並轉換為布爾型,如果為false則可以執行靜態方法,如果為true則不執行靜態發放並返回null。

技術分享

Struts2漏洞利用原理

上文已經詳細介紹了OGNL引擎,因為OGNL過於強大,它也造成了諸多安全問題。惡意攻擊者通過構造特定數據帶入OGNL表達式即可能被解析並執行,而OGNL可以用來獲取和設置Java對象的屬性,同時也可以對服務端對象進行修改,所以只要繞過Struts2的一些安全策略,惡意攻擊者甚至可以執行系統命令進行系統攻擊。如struts2遠程代碼執行漏洞s2-005。

雖然Struts2出於安全考慮,在SecurityMemberAccess類中通過設置禁止靜態方法訪問及默認禁止執行靜態方法來阻止代碼執行。即上面提及的denyMethodExecution為true,MemberAccess為false。但這兩個屬性都可以被修改從而繞過安全限制執行任意命令。s2-005 POC如下:

http://mydomain/MyStruts.action?(‘\u0023_memberAccess[\‘allowStaticMethodAccess\‘]‘)(meh)=true&(aaa)((‘\u0023context[\‘xwork.MethodAccessor.denyMethodExecution\‘]\u003d\u0023foo‘)(\u0023foo\u003dnew%20java.lang.Boolean("false")))&(asdf)((‘\u0023rt.exec(‘ipconfig’)‘)(\u0023rt\[email][email protected][/email][email protected]()))=1

在POC中\u0023是因為S2-003對#號進行了過濾,但沒有考慮到unicode編碼情況,而通過#_memberAccess[\‘allowStaticMethodAccess\‘]‘)(meh)=true,可以設置允許訪問靜態方法,因為getRuntime方法是靜態的,通過context[\‘xwork.MethodAccessor.denyMethodExecution\‘]=#foo‘)(#foo=new%20java.lang.Boolean("false")設置拒絕執行方法為false,也就是允許執行靜態方法。最後調用rt.exec(‘ipconfig’)執行任意命令。

Struts2漏洞利用原理及OGNL機制