Struts2漏洞利用原理及OGNL機制

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

 

概述

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

View層—表現爲字符串展現

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

Controller層—表現爲java對象

在控制層,數據模型遵循java的語法和數據結構,全部的數據載體在Java世界中能夠表現爲豐富的數據結構和數據類型,你能夠自行定義你喜歡的類,在類與類之間進行繼承、嵌套。咱們一般會把這種模型稱之爲複雜的對象樹。數據在傳遞時,將以對象的形式進行。express

能夠看到,數據在不一樣的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 技術內幕)。dom

關於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的屬性值
@com.example.core.Resource@img
//調用com.example.core.Resource類中名爲get的方法
@com.example.core.Resource@get()

訪問調用:

//調用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]u003d@java.lang.Runt[/email]ime@getRuntime()))=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’)執行任意命令。

相關文章
相關標籤/搜索