<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/papers/340

            我們可以把OGNL作為一個底層產品,它在功能實現中的設計缺陷,是如何能夠被利用并遠程執行惡意代碼的,而不是完全在struts2這個產品的功能設計層面去討論漏洞原由!

            什么是OGNL?

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

            Ognl.setValue("department.name", user2, "dev");
            
            System.out.println(user2.getDepartment().getName());
            
            Ognl.setValue(Ognl.parseexpression_r("department.name"), context, user2, "otherDev");
            
            System.out.println(user2.getDepartment().getName());
            
            Ognl.setValue("department.name", user2, "dev");
            
            System.out.println(user2.getDepartment().getName());
            
            Ognl.setValue(Ognl.parseexpression_r("department.name"), context, user2, "otherDev");
            
            System.out.println(user2.getDepartment().getName());
            

            1. 表達式(Expression)

            表達式是整個OGNL的核心,所有的OGNL操作都是針對表達式的解析后進行的。表達式會規定此次OGNL操作到底要干什么。我們可以看到,在上面的測試中,name、department.name等都是表達式,表示取name或者department中的name的值。OGNL支持很多類型的表達式,之后我們會看到更多。

            2. 根對象(Root Object)

            根對象可以理解為OGNL的操作對象。在表達式規定了“干什么”以后,你還需要指定到底“對誰干”。在上面的測試代碼中,user就是根對象。這就意味著,我們需要對user這個對象去取name這個屬性的值(對user這個對象去設置其中的department中的name屬性值)。

            3. 上下文環境(Context)

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

            Struts 2中的OGNL Context實現者為ActionContext,它結構示意圖如下:

            ?2013071900034178341.png

            當Struts2接受一個請求時,會迅速創建ActionContext,ValueStack,action 。然后把action存放進ValueStack,所以action的實例變量可以被OGNL訪問

            那struts2引入OGNL到底用來干什么?

            我們知道在MVC中,其實所有的工作就是在各層間做數據流轉.

            在View層的數據是單一的,只有不帶數據類型的字符串.在沒有框架的時代我們使用的是手動寫代碼或者像struts1一樣利用反射,填充表單數據并轉換到Controller層的對象中,反射轉換成java數據類型的commons組件偽代碼,如:

            import javax.servlet.ServletException;
            import javax.servlet.http.HttpServletRequest;
            import javax.servlet.http.HttpServletResponse;
            import org.apache.commons.beanutils.ConvertUtils;
            import org.apache.commons.beanutils.PropertyUtils;
            
            //自動裝載表單及驗證
            public class LoadForm {
                //表單裝載
                public static Object parseRequest(HttpServletRequest request,HttpServletResponse response,Object bean) throws ServletException, IOException{
                       //取得所有參數列表
                       Enumeration<?> enums = request.getParameterNames();
                       //遍歷所有參數列表
                       while(enums.hasMoreElements()){
                        Object obj = enums.nextElement();
                        try {
                         //取得這個參數在Bean中的數據類開
                         Class<?> cls = PropertyUtils.getPropertyType(bean, obj.toString());
                         //把相應的數據轉換成對應的數據類型
                         Object beanValue = ConvertUtils.convert(request.getParameter(obj.toString()), cls);
                         //填充Bean值
                         PropertyUtils.setProperty(bean, obj.toString(), beanValue);
                        } catch (Exception e) {
                            //不顯示異常 e.printStackTrace();
                        }
                       }
                       return bean;
                    }   
                }
            

            從Controller層到View層,還是手動寫代碼然后到頁面上取,如偽代碼:

            request.setAttribute("xxx", "xxx");
            

            而Struts2采納了XWork的一套完美方案(Xwork提供了很多核心功能:前端攔截機(interceptor),運行時表單屬性驗證,類型轉換,強大的表達式語言(OGNL – the Object Graph Navigation Language),IoC(Inversion of Control反轉控制)容器等). 并在此基礎上構建一套所謂完美的機制,OGNL方案和OGNLValueStack機制.

            View層到Controller層自動轉儲;然后是Controller層到View層,我們可以使用簡易的表達式取對象數據顯示到頁面,如: ${對象.屬性},節省不少代碼時間且使用方便.而它的存儲結構就是一棵對象,這里我們可以把對象樹當成一個java對象寄存器,可以方便添加、訪問對象等。 但是OGNL的這些功能或機制是危險的.

            我們列舉一下表達式功能操作清單:

            1. 基本對象樹的訪問
            對象樹的訪問就是通過使用點號將對象的引用串聯起來進行。
            例如:xxxx,xxxx.xxxx,xxxx. xxxx. xxxx. xxxx. xxxx
            
            2. 對容器變量的訪問
            對容器變量的訪問,通過#符號加上表達式進行。
            例如:#xxxx,#xxxx. xxxx,#xxxx.xxxxx. xxxx. xxxx. xxxx
            
            3. 使用操作符號
            OGNL表達式中能使用的操作符基本跟Java里的操作符一樣,除了能使用 +, -, *, /, ++, --, ==, !=, = 等操作符之外,還能使用 mod, in, not in等。
            
            4. 容器、數組、對象
            OGNL支持對數組和ArrayList等容器的順序訪問:例如:group.users[0]
            同時,OGNL支持對Map的按鍵值查找:
            例如:#session['mySessionPropKey']
            不僅如此,OGNL還支持容器的構造的表達式:
            例如:{"green", "red", "blue"}構造一個List,#{"key1" : "value1", "key2" : "value2", "key3" : "value3"}構造一個Map
            你也可以通過任意類對象的構造函數進行對象新建:
            例如:new Java.net.URL("xxxxxx/")
            
            5. 對靜態方法或變量的訪問
            要引用類的靜態方法和字段,[email protected]@[email protected]@method(args):
            例如:@[email protected],@[email protected]
            
            6. 方法調用
            直接通過類似Java的方法調用方式進行,你甚至可以傳遞參數:
            例如:user.getName(),group.users.size(),group.containsUser(#requestUser)
            
            7. 投影和選擇
            OGNL支持類似數據庫中的投影(projection) 和選擇(selection)。
            投影就是選出集合中每個元素的相同屬性組成新的集合,類似于關系數據庫的字段操作。投影操作語法為 collection.{XXX},其中XXX 是這個集合中每個元素的公共屬性。
            例如:group.userList.{username}將獲得某個group中的所有user的name的列表。
            選擇就是過濾滿足selection 條件的集合元素,類似于關系數據庫的紀錄操作。選擇操作的語法為:collection.{X YYY},其中X 是一個選擇操作符,后面則是選擇用的邏輯表達式。而選擇操作符有三種:
            ? 選擇滿足條件的所有元素
            ^ 選擇滿足條件的第一個元素
            $ 選擇滿足條件的最后一個元素
            例如:group.userList.{? #txxx.xxx != null}將獲得某個group中user的name不為空的user的列表。
            

            結合之前的漏洞POC,只舉兩例漏洞說明本質問題所在(其他類似,如:安全限制繞過,非必要使用OGNL或奇葩地利用OGNL實現設計功能等).那么只要struts2的某些功能使用了OGNL功能,且外部參數傳入OGNL流程的,理論上都是能夠執行惡意代碼的.

            參照之前的PoC從“表達式功能操作清單”中選取“危險項清單”,一些危險的功能操作,問題就出現在它們身上,提供了比較有危害PoC的構造條件:

            1. 基本對象樹的訪問
            對象樹的訪問就是通過使用點號將對象的引用串聯起來進行。
            例如:xxxx,xxxx.xxxx,xxxx. xxxx. xxxx. xxxx. xxxx
            
            2. 對容器變量的訪問
            對容器變量的訪問,通過#符號加上表達式進行。
            例如:#xxxx,#xxxx. xxxx,#xxxx.xxxxx. xxxx. xxxx. xxxx
            
            3. 容器、數組、對象
            OGNL支持對數組和ArrayList等容器的順序訪問:例如:group.users[0]
            同時,OGNL支持對Map的按鍵值查找:
            例如:#session['mySessionPropKey']
            不僅如此,OGNL還支持容器的構造的表達式:
            例如:{"green", "red", "blue"}構造一個List,#{"key1" : "value1", "key2" : "value2", "key3" : "value3"}構造一個Map
            你也可以通過任意類對象的構造函數進行對象新建:
            例如:new Java.net.URL("xxxxxx/")
            
            4. 對靜態方法或變量的訪問
            要引用類的靜態方法和字段,[email protected]@[email protected]@method(args):
            例如:@[email protected],@[email protected]
            
            5. 方法調用
            直接通過類似Java的方法調用方式進行,你甚至可以傳遞參數:
            例如:user.getName(),group.users.size(),group.containsUser(#requestUser)
            

            以及上下文環境和這個struts2設計,當Struts2接受一個請求時,會迅速創建ActionContext,ValueStack,action 。然后把action存放進ValueStack,所以action的實例變量可以被OGNL訪問。

            第一個,是2010年7月14號(亮點1:烏云好象就是這天出生的吧?),"Struts2/XWork < 2.2.0遠程執行任意代碼漏洞",POC:

            ?('\u0023_memberAccess[\'allowStaticMethodAccess\']')(meh)=true&amp;(aaa)(('\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003d\u0023foo')(\u0023foo\u003dnew%20java.lang.Boolean("false")))&amp;(asdf)(('\u0023rt.exit(1)')(\u0023rt\[email protected]@getRuntime()))=1
            

            也就是這個經典的POC,大家開始第一次認識struts2漏洞(之前也有,只是那時很少有人去關注,或許很容易就能找到一個0day(而且是永遠的0day,回溯一下框架歷史,我不能再提示了!)。 myibatis框架也有引入OGNL的,親!

            由于ONGL的調用可以通過http傳參來執行,為了防止攻擊者以此來調用任意方法,Xwork設置了兩個參數來進行防護:

            OgnlContext的屬性 'xwork.MethodAccessor.denyMethodExecution'(默認為真)
            SecurityMemberAccess私有字段'allowStaticMethodAccess'(默認為假)
            

            (這里我現在還沒想明白,既然都有這步限制了?為什么后面的那些還會出現,難道官方只會看著公布的PoC打補丁?)

            這里大家都知道,是使用#限制OgnlContext 'xwork.MethodAccessor.denyMethodExecution'和'allowStaticMethodAccess'上下文訪問以及靜態方法調用的值設置.而漏洞作者使用十六進制編碼繞過了限制,[email protected]@getRuntime()這個靜態方法執行命令.

            java.lang.Runtime.getRuntime().exit(1) (終止當前正在運行的 Java 虛擬機)
            

            在某些struts2漏洞中已經開始改變這個觀念,因為我們很難再繞過上面的安全限制了.去調用上下文的屬性及靜態方法執行惡意java代碼.

            但是"危險清單"中還有一個可以利用,OGNL表達式中居然可以去new一個java對象(見危險項清單 3.),對于構造PoC足夠用了,而不需要上面那些條件.(之前也有類似的相關漏洞,我發現官方并不喜歡做代碼審計的)

            Apache Struts CVE-2013-2251 Multiple Remote Command Execution Vulnerabilities

            這里漏洞原理大致是這樣,作者一共提供了三個PoC:

            http://www.example.com/struts2-blank/example/X.action?action:%25{(new+java.lang.ProcessBuilder(new+java.lang.String[]{'command','goes','here'})).start()} (這個和后面兩個是有點區別的,多測試目標時你會發現!)
            
            http://www.example.com/struts2-showcase/employee/save.action?redirect:%25{(new+java.lang.ProcessBuilder(new+java.lang.String[]{'command','goes','here'})).start()}
            
            http://www.example.com/struts2-showcase/employee/save.action?redirectAction:%25{(new+java.lang.ProcessBuilder(new+java.lang.String[]{'command','goes','here'})).start()} 
            
            
            
            
            
            action:
            redirect:
            redirectAction:
            

            這三關鍵字是struts2設計出來做短地址導航的,但它奇葩地方在于,如:redirectAction:${惡意代碼}后面可以跟OGNL表達式執行,找這種相關的漏洞很好找(如果還有),查看struts2源代碼${},%{}等(struts2只認定這些特征的代碼進入OGNL表達式執行流程),struts2執行ognl表達式的實現功能的地方.

            而java.lang.ProcessBuilder是另外一個可以執行命令的java基礎類,還有后面大家手中的PoC(new文件操作及輸入輸出流相關危險類等),此時我們發現只要new對象然后調用其方法就可以了.不再需要上面的一些靜態方法等.

            這里只能將OGNL和struts2各打50大板了!

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线