0. 前言
上一篇我们介绍了 SQL 手工注入的流程以及步骤, 但在实际的安全问题以及 CTF 题目中, 查询语句多种多样, 而且是肯定会对用户的输入进行一个安全过滤的, 而这些过滤并不一定是百分百安全的, 如何利用一些技巧绕过一些安全过滤, 这就是我们这一篇要介绍的事情.
如果你还不熟悉 SQL 注入的流程以及步骤, 请先阅读我的上一篇博文.
文中有误之处, 还请各位师傅指出.
1. 各种 select 语句绕过
(1)select xxx from xxx limit $num;
上篇我们讲解了 where 条件查询的注入方法, 那么如果不是 where 而是其他语句呢, 例如 limit 限制, 这可能会出现在限制每页展示多少行中用到, 例如对于如下语句
select * from student limit $num;
$num 是我们上传的变量, 假如我们依然使用 order 进行查列数将于返回如下结果
MySQL 会提示语法错误, 因为排序需要在分页的前面使用, 那么这个时候我们该如何查列数呢, 答案是上次提到的利用 into @,@,@,@就是'@'字符, 它代表 MySQL 的一个临时变量.
例如
必须保证变量数等于列数, 利用这个特性我们就可以查出列数.
(2)updata,insert,delete 相关
和 select 类似, 还是先找到参数的位置, 再判断注入类型, 最后构造表达式进行 SQL 注入.
(3)order by 注入
前面已经讲了利用 order by 查列数, 实际上 order by 可以通过位运算来执行表达式
select * from student order by 1|(sleep(5));
2. 时间函数进行盲注
有些时候当网页没有错误回显时, 可以考虑使用时间函数进行盲注. 原理是利用条件判断进行睡眠, 我们只需要观察响应时间即可.
例如
- select * from student where id=1 and if(user()='root@localhost',sleep(5),null);# 判断用户
- select * from student where id=1 and if(substr(user(),1,1)='r',sleep(5),null);# 逐字判断
我们可以先用一个永真条件判断 sleep() 是否可用, 当 sleep() 被禁用的时候, 我们可以用下面其他几种延时方法代替.
(1) 延时方法
sleep() 函数.
benchmark(count,expr), 重复 expr 函数 count 次, 我们可以利用一些 MySQL 自带的加密函数作为 expr 执行多次达到睡眠的效果. 具体执行次数可以根据 CPU 来进行变动.
select * from student where id=1 and if(true,benchmark(10000000,sha(1)),null);
笛卡尔积, 利用计算笛卡尔积达到延时;
select count(*) from information_schema.columns A,information_schema.columns B;#count(*) 返回行数
get_lock(str,timeout), 这个需要开启两次会话, 第一次给 str 进行上锁, 第二次再执行就会等待 timeout 的时间, 若 timeout 为负, 则无限等待. get_lock() 只会在执行 release_lock() 或隐式的会话中止时显式释放锁, 事务提交或回滚不会释放锁.
length(str), 利用 rpad 构造长字符串, 再用 length 返回一列.
select LENGTH(concat(rpad(1,9999999,'a'),rpad(1,9999999,'a'),rpad(1,9999999,'a'),rpad(1,9999999,'a'),rpad(1,9999999,'a'),rpad(1,9999999,'a'),rpad(1,9999999,'a')));
rlike,regexp, 先利用 rpad 或者式 repeat 构造长字符串再利用 rlike 正则匹配返回一列, 通过控制构造的字符串长度控制时间.
select concat(rpad(1,9999999,'a'),rpad(1,9999999,'a'),rpad(1,9999999,'a'),rpad(1,9999999,'a'),rpad(1,9999999,'a'),rpad(1,9999999,'a'),rpad(1,9999999,'a')) rlike '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)';
p.s. 构造字符串长度不能超出 MySQL 内存限制, 否则会报错.
(2) 条件判断
if(expr,expr1,expr2).
case when xx then xx; 利用 case when 语法达到判断条件的方法.
(3) 字符串截取
substr(str,pos,len), 同 Python 的 substr() 函数, 但 MySQL 中首位置为 1 或 - len.
mid(str,pos,len), 基本同上
substring_index(str,delim,count), 返回第 count 个 delim 串左边的所有内容
select substring_index('a.b.c.d','.',3);# 返回 a.b.c
left(str,len), 返回 str 左边 len 个字符.
right(str,len), 返回 str 右边 len 个字符.
本质上时间盲注就是 bool 盲注的一种, 利用回显消息不同, 只是这里的回显没有显式显示在屏幕上罢了.
3.bool 盲注
利用回显的不同来猜测数据库的信息, 例如 order by 就是一种 bool 盲注, 一般可以利用二分或者逐位拆解的方式进行盲注. 可以利用一些位运算的短路机制进行连接表达式.
4. 多行注入
当调用数据库函数支持多行 sql 语句才能使用, 原理就是利用';'结束语句插入自己的 sql 语句.
5.MySQL 编码注入
(1) 弱类型转换
先来看这样一个查询语句 select * from student where name=1;
为什么可以被查询呢, 因为 name 会被转换成数字和 1 比较, 而 MySQL 的默认转换和 PHP 的转换类似, 找到第一个不为数字的字符就结束. 利用这个特性我们可以进行一些查询判断.
(2) 宽字节注入
当当前数据库使用了 GBK 编码的时候, 会把两个字符转化为汉字 (前一个字符大于 128), 利用这个特性再配合后端的过滤实现注入.
例如当后端过滤'将其变成 \'的时候, 如果式 gbk 编码, 则传入的就是 %5C%27, 这时候我们提交 %df'则会被编码成 %df%5C%27, 而前面的 %df%5c 则会被编码成中文, 达到了注入效果.
(3)SQL 字符集特性
对于 utf8_unicode_ci 字符集, 不区分大小写, 而且 Ä=A,Ö=O,Ü=U 等条件都成立, 且 ß=ss,
对于 utf8_general_ci 字符集,ß=s.
更多条件等式可以自行测试.
(4) 进制转换
如果表名和列名过滤了字符可以将其转换为 16 进制实现, 前面带上 0x
6. 总结
来源: https://www.cnblogs.com/xenny/p/11568139.html