<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/1151

            0x00 摘要


            2012年,我在《攻擊JAVA WEB》,文中提多關于“classLoader導致特定環境下的DOS漏洞”,當時并沒有更加深入的說明,這幾天struts官方修補了這個漏洞,本文是對這個漏洞的深入研究。

            0x01 正文


            這一切,得從我們控制了classLoader說起,曾經寫過一篇文章,提到了一個小小的技術細節,非常不起眼的一個雞肋。 引用《Spring framework(cve-2010-1622)漏洞利用指南》

            Struts2其實也本該是個導致遠程代碼執行漏洞才對,只是因為它的字段映射問題,只映射基礎類型,默認不負責映射其他類型,所以當攻擊者直接提交URLs[0]=xxx時,直接爆字段類型轉換錯誤,結果才僥幸逃過一劫罷了。

            tomcat8.0出來后,這個問題爆發了,這是一個雞肋漏洞的逆襲。

            在struts2任何一個action運行之前,一旦接受到用戶提交參數xx=zzzzz時,就由Ognl負責調用對應的當前action的setXxx方法,至于set方法到底是干什么的,其實不重要,里面的邏輯也不重要,我們只關注這個方法調用了,參數傳遞了。這種對屬性的改變,有時候是可以很大程度的影響后續復雜邏輯。

            普及一點基礎

            Object是java的基礎類,所有的class生成的對象,都會繼承Object的所有屬性和方法,因此當前action無論是什么代碼,必須有Object自帶的getClass方法,這個方法會返回一個Class對象,Class對象又一定會有getClassLoader方法,最終在每個action都可以

            #!java
            getClass().getClassLoader()
            

            拿到當前的ClassLoader。

            我研究這個問題,在幾年前了,這個東西理解起來不容易,尤其是各個web容器不一致,剛巧當時有個阿里巴巴內部《tomcat等容器的classLoader加載原理》培訓,收獲匪淺。本文篇幅有限,簡單的講一下。

            在JRE啟動中,每個Class都會有自己的ClassLoader。web容器,為了方便的管理啟動過程,通常都有實現自定義的ClassLoader。《Spring framework》的漏洞的利用場景真的非常幸運,利用了web容器的特性getURLs方法,所有容器的servlet的ClassLoader都會通過繼承父類UrlClassLoader得到getURLs這個方法,所以這個漏洞可以不受容器影響。事實上,每個容器的ClassLoader都是自己實現的,環境必然會有所不同,那次struts2僥幸逃過一劫,所以我的一個關注點,一都放在幾大web容器的ClassLoader代碼變化上,哪天看到tomcat8居然把resources放進ClassLoader上,而ServletContext剛巧掛在resources上,頓時知道肉戲來了。

            上傳webshell的可能性研究

            多次的遠程代碼執行漏洞洗禮,我一直在腦海里模擬“ServletContext被控制了,這次能干什么”,究竟有哪些路線,可以通往代碼執行的領域。

            比如:Struts2會去servletContext里取到一個值,然后把它作為Ognl執行掉。這個太簡單了,我自己都不信。

            Ognl的Context樹形結構:

            enter image description here

            servletContext被轉換成Map,變成了圖中的application子項,這個位址很尷尬,如果是上一層Node,從上到下找到value Stack,確實有實現這個思路的可能,但現在看來,這條路斷了,它不支持找到父節點。經過多次找尋后,確認Ognl出局,只能從web容器本身入手。

            運行在Tomcat8下的struts,在隨便哪個action代碼中,插入這段,下斷點,

            #!java
            this.getClass().getClassLoader();
            

            enter image description here

            任何一個Action的classLoader都是org.apache.catalina.loader.WebappClassLoader,這是漏洞的源頭。

            我的思路,是給context賦予初始化參數readOnly=false。因為在tomcat上,所有的請求,都會默認由defaultServlet上處理,或者由jspServet處理。只要在context初始化時,readOnly=false,接下來defaultServlet就會真的處理PUT請求DELETE請求等等大威力請求,于是我們可以直接PUT文件上來。

            這個思路的前提,是defaultservlet再被初始化一次。現實很殘酷,這個思路最終沒有得到執行,原因是servlet只會初始化一次,而readOnly屬性只有在servlet初始化時,才會真的給servet的readOnly屬性賦值。這次突破,宣告失敗。

            幾個這個漏洞的調試小技巧:

            1. 僅僅從debug上查看ognl的賦值情況,是不準確的,debug只能看到這個類定義好的變量。

            如果有一個代碼是這樣的:

            #!java
            public void setName(String name){…}
            

            但是并沒有定義過這個屬性,這時debug無法看到這個東西,但是其實ognl可以直接通過name=zzzzz調用,并且能把zzzz傳遞過去。

            2. 或者只有一個私有屬性,但是沒有set方法,其實也是ognl不能賦值的。

            這個debug,觀察這個漏洞時,僅僅是個參考,要真正深入進去看代碼才能和ognl的視線保持一致。

            3. 一個final的對象,或者只是get方法返回一個對象,看起來像是只讀的,其實對象的屬性還是可以改的,這個只是對象的引用。

            你可以理解為指針指向的地址不能變,但是指向的那個對象的屬性,是可以修改的。

            舉例:

            #!java
            public User getUser()
            {
                 return this.user;
            }
            public final User user;
            

            這兩處代碼,其實真正返回給OGNL的都是user對象,對象的屬性只要還有set方法,也都是可以被修改的。依然可以通過

            url?user.name=xxx
            

            對user的name賦值。

            struts2運行在tomcat8.0.1rc5(2013,11月前)的任意文件讀取漏洞

            在tomcat的環境下,classLoader會掛載一個resources,類名叫做“StandardRoot”,這個恐怖的東西,和tomcat的資源文件管理有關,debug看到的第一個屬性就是非常危險的屬性“allowLinking”。

            enter image description here

            這個事情,要從很久很久以前,struts修補的一個任意文件讀取漏洞說起。

            http://struts.apache.org/release/2.3.x/docs/s2-004.html

            這是一個目錄列表+文件讀取漏洞,修補方案非常陰險,沒有采用正規的手段,在框架層解決漏洞,而是利用了web容器的一個公約特性,jsp的web容器都遵守一個規則。

            當一個路徑叫做“/var/www/tomcat/webapps/demo/struts/../”時,調用

            #!java
            getClassLoader().getResource(Path)
            

            返回路徑為:

            /var/www/tomcat/webapps/demo/
            

            會把/../去掉,并且直接到達目的目錄。

            這個叫做web容器特性,由web容器說了算,哪天web容器生氣了,想變了,struts沒有話語權。事實上,我一直喜歡講框架安全,其中一條準則,就是“框架不要依靠web容器的特性做防御”,當然,今天不討論這個話題,只是稍微做個鋪墊。

            當時修補代碼為:

            enter image description here

            用戶提交struts/../時,pathEnding="struts/../"

            但是

            resourceUrl="/var/www/tomcat/webapps/demo/"
            

            所以并不以pathEnding結尾。這種猥瑣的做法,當時確實有效。

            tomcat8這個版本突然抽風了,重寫了這個方法,還真的返回了

            /var/www/tomcat/webapps/demo/struts/../
            

            宣告淪陷。但是代碼實際運行中,有個要求,就是“StandardRoot.allowLinking”必須是true(默認肯定是false)。

            機會來了。首先提交:

            http://localhost:8080/demo/t1.action?class.classLoader.resources.allowLinking=true
            

            debug可以看到已經是true。

            然后按照以前的攻擊方法:

            enter image description here

            就可以輕易讀取任意文件,拿到數據庫密碼等等。

            這是兩個漏洞結合的成果,非常遺憾的是,在RC5這個版本之后,有人給tomcat提交了一個需求,大概在2013年11月左右,tomcat的一個不重要的需求中(剛好這個需求涉及到資源文件相關代碼),tomcat維護人員也許并沒有意識到了這里存在讀取資源文件的威脅,僅僅是把讀取資源功能重新抽象規劃了一次,結果順帶修補了這個漏洞,這問題產生的理由非常冤屈,修補的理由非常冤屈,最郁悶的是我,活生生的,0day沒了。原本沾沾自喜以為可以大殺四方,結果大神打了個噴嚏。

            最后順帶說一句,這個漏洞只在windows下正常,linux下未知原因抽風。

            tomcat8下黑掉struts2應用的首頁

            但是不要緊,tomcat是不可能給struts解決根本問題的,standardroot暴露出來,可以順帶影響很多東西。這個算DDOS么?其實我可以把“Hacked by kxlzx”寫到你的應用首頁去

            成因非常的“正常”,因為這個context屬性代表了tomcat的/conf/context.xml文件的配置,我現在把path給你改了,那么struts去處理result時,會用路徑拼接讀取文件,現在目錄名被改掉了,自然就讀不到文件了,可惜這里不能00截斷,否則又是一個任意文件讀取漏洞。 下面是被干掉的網站,訪問任何一個action,都會有如下效果:

            enter image description here

            看看debug的情況:

            enter image description here

            當前action叫做T1,會找到T1.jsp,但是現在目錄名已經被修改了,所以報錯。

            enter image description here

            這個問題可以影響tomcat8所有版本下運行的struts2,對了,你們得自己設計EXP哈,不要亂入。

            jetty7關機指令

            既然提到了web容器,只有研究tomcat,肯定不能覆蓋大家關心的地方,于是我選擇了另一個開源免費并且使用量大的輕量級web容器:jetty。

            現在先看看jetty是否有突破的口子。這次講解路線反過來,先找個影響“不大”,各位“不是”很關心的漏洞。

            還是先看看web結構,使用老辦法斷點:

            #!java
            this.getClass().getClassLoader();
            

            看到一個class:

            #!java
            org.eclipse.jetty.webapp.WebAppClassLoader
            

            jetty的漏洞,沒有tomcat那么含蓄,非常直接的,context就掛載在classLoader上。

            enter image description here

            jetty在運行過程中,會去實時查看ContextHandler中的shutdown屬性(webappcontext通過幾層繼承關系,繼承了這個類,其實親戚關系有八丈遠),一旦發現true,直接就不工作了,這是典型的,使用一個狀態判斷業務是否同行。基于這個原理,只要如下訪問,以后這個應用,就只剩下404了。

            enter image description here

            無論是什么action,都只會返回這個錯誤,后續的執行,jetty都以為真的shutdown了。并且這個過程沒有任何補救措施,只能管理員手工重啟了,各位SA親們,你們準備好了么?

            jetty任意文件下載

            我們讓404為這個漏洞服務到底。

            事實上,下面說的這個問題發生在jetty上,tomcat真的是巧合的逃過一劫。

            我們看看jetty對于自定義錯誤文件的配置過程:

            enter image description here

            這段配置文件,可以自定義404錯誤文件,這里可以指定/WEB-INF/目錄下的文件,一旦配置之后,由ErrorPageErrorHandler負責給errorPages(這也是個map)添加一個對應關系。這個類最終會被掛在到context中,那么依照這個漏洞的原理,我們可以層層調用,最終制定一種錯誤,比如404錯誤。

            Jetty把errorHandler掛載到context上,errorHander有個errorPages屬性,這其實是個map,代表錯誤頁面,key是一個返回碼數字,value就是錯誤后顯示的文件。 所以打開:

            enter image description here

            訪問圖片中這條URL后,效果如下,任何一個不存在的頁面都會顯示web.xml的內容:

            enter image description here

            有了這個問題,就可以讀取數據庫文件,查看數據庫密碼,讀取代碼文件,查找隱藏的業務邏輯漏洞。注意,是任何人遇到404都可以看到這個頁面,最好等夜深人靜的時候再使用,用完了還得恢復原樣。

            0x02 漏洞修補


            這漏洞已經被官方修補了,2012年發出來的老問題,只是沒有單獨提交官方而已,居然也能拖到現在,建議各位下定決心換個框架。

            from:http://security.alibaba.com/blog/blog_3.htm

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

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

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

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

                      亚洲欧美在线