概述
感谢朋友竹子的邀请。 SQLi漏洞是一个老生常谈的话题,这里仅分享一下笔者自己对SQLi漏洞挖掘的一些感悟及总结,送给有需要的人。 本文只讨论如何去发现SQLi漏洞,而不讨论发现漏洞后面的利用。 黑盒测试的角度,笔者觉得: 当你不确定漏洞是否真实存在时,就像大海上的一叶孤舟; 而当确定漏洞真实存在后,则就像大海上的一叶孤舟,多了一个灯塔。 本文主要从两个方面进行讨论,一方面将会介绍易出现SQLi漏洞的场景,另一方面将主要介绍MySQL中不同位置的注入点如何去快速证明漏洞的存在。 本文假设你已经对MySQL数据库和SQLi漏洞有所了解。易出现问题的场景
排序功能 预编译对ORDER BY子句、表名、列名不生效。 这一点很多研发不清楚,可能会一股脑的使用预编译认为万事大吉。 因此在遇到这类的参数时,一定要格外留意,经常会出现SQLi漏洞。 持久性框架 企业系统的研发中,大部分都是调用现成的持久层框架来对数据库进行操作,但是每个框架都可能因为研发的误使用导致SQLi漏洞的产生。 像现在Java Web中最流行的SSM框架中的MyBatis,#{param}
表示对param这个参数使用预编译,而
${param}
则表示对param这个参数不使用预编译,直接拼接。
研发本着
省事省力
的原则写着代码,写出了MyBatis框架最常见的漏洞点。
模糊查询
错误的写法![90f195a205f6061bde4c552219c5b9f0.png](https://img-blog.csdnimg.cn/img_convert/90f195a205f6061bde4c552219c5b9f0.png)
![b4f9696e8a49c9721215d6d213c1ed78.png](https://img-blog.csdnimg.cn/img_convert/b4f9696e8a49c9721215d6d213c1ed78.png)
批量处理
错误的写法,直接传入一个字符串类型的ids
,例如
ids = 1,2,3,4
![59ae1d3d1c5cee9c93d0b2f20010787e.png](https://img-blog.csdnimg.cn/img_convert/59ae1d3d1c5cee9c93d0b2f20010787e.png)
ids
,可见,这个比错误的写法多写更多的字符。
![febc358060ae243c2176ec0d3682e019.png](https://img-blog.csdnimg.cn/img_convert/febc358060ae243c2176ec0d3682e019.png)
接口a
,这个
接口a
可能被系统B、C、D调用,但是就是不被系统A直接使用。
因此当这个
接口a
未作任何防御,存在SQLi漏洞的风险时,较难被公司安全部发现。
当安全部代码审计系统A,发现了
接口a
未作任何防御,多半是存在SQLi漏洞;
但是安全部没证据啊,没法构造数据包进行证明,鬼知道这个
接口a
到底被哪个系统调用了。
因此对于系统A提供的这个
接口a
可能要在安全部黑盒测试系统B的时候,才能证明
接口a
存在SQLi漏洞;然后通知系统A的研发进行修改。
所以在测试时发现某些功能突然调用了其他系统的接口时,需要格外留意。
仿废弃接口
接口已经废弃了,但是研发人员为了图省事,只在前端限制了接口的使用,通过构造数据包仍旧可以正常使用,因此我称它为
伪废弃接口
。
以前在挖洞的时候遇到很多次,后来在企业中与研发的同学沟通多了,才真正领悟到一些内涵。
场面一:
系统下线了,直接在登录页面把相关JS注释掉
场面二:
模块下线了,直接把这个模块在HTML删了
场面三:
系统下线了,把首页所有代码全删了
研发本着
少做少错,多做多错
的原则小心翼翼的下线了。
而且研发还怀着侥幸心理,说不定啥时候这个系统再用或者优化呢,到时候岂不是可以省大把时间。
像下面这个场景,在正常登录时各种参数缺失没法通过点击登录按钮来进行登录
![43e47227444b88a11f3c966d9626b6e3.png](https://img-blog.csdnimg.cn/img_convert/43e47227444b88a11f3c966d9626b6e3.png)
JAVA
为例,声明变量时便确定了数据类型,如果给的类型不一致那么便会导致错误。
因此对于像
id
、
number
、
pageNo
、
pageSize
等这类数字类型非常明显的参数,存在
SQLi
漏洞及
XSS
漏洞(暂不考虑
DOM型XSS
)的可能性非常小。
例如下面这个,
userId
参数是
Long
类型的,
act
参数是
String
类型的。
因此这个
userId
参数通常不会存在
SQLi
漏洞跟
XSS
漏洞,而
act
参数确有测试的必要。
![3f5385746aa69744444c0a23b0573110.png](https://img-blog.csdnimg.cn/img_convert/3f5385746aa69744444c0a23b0573110.png)
PHP
、
Python
这种弱编译型语言,则仍旧需要对数字类型非常明显的参数做测试。
但是也不能一概而论,这类语言的一些开发框架会强制参数的类型。
例如
Python
里面的
Django
框架
在接收参数时限定了参数
question_id
必须是
int
类型的,当给出的字符串时,便会异常,无法匹配这个接口。
![7fa36e3670de6d1eeafcb8b15a8c1434.png](https://img-blog.csdnimg.cn/img_convert/7fa36e3670de6d1eeafcb8b15a8c1434.png)
隐藏参数
ORM框架会为数据库中的字段与实体对象一一映射,即数据库中有哪些字段,那么建立的实体对象中便有那些属性。 例如下面这个SQL模板,接收的是一个User对象,返回的也是一个User对象 必须的参数是startIndex
、
limitSize
参数,而
username
、
email
、
dep
参数都是可有可无的。
当然这三个参数也很可能在你测试的功能点上找不到。
也就是说常见的黑盒测试、扫描器扫描等不容易发现,因此在像这种ORM框架中隐藏参数出现SQLi漏洞的概率会大一些。
![8ab37388ce82c822c25d1d6a1429ab74.png](https://img-blog.csdnimg.cn/img_convert/8ab37388ce82c822c25d1d6a1429ab74.png)
RPC
那个场景所说的问题
在黑盒测试时,较难测试到系统中的隐藏参数;
在白盒测试时,较难证明系统提供的RPC接口的安全性。
前端固定参数
前端中固定的一些参数,例如下拉列表、readonly
的参数、
disabled
的参数等,研发认为只能是这几个值,肯定不会有什么问题,因此认为参数可控,可以不对参数做任何处理。
这种情况在企业中特别常见,因此这类参数出现问题的概率会更大。
例如下面这个真实片段
楼兰:你这个漏洞没修复啊,还是存在啊
研发:不会吧,这次我在前端设置了一个下拉框,这个参数只能是固定的几个值
楼兰:这不行啊,你这个漏洞在前端做的任何限制都没用啊,攻击者直接在数据包中进行修改,不通过前端的
研发:...
HTTP Header头
Header
头中最常写入数据库的字段:
Client-ip
与
X-Forwarded-For
,用来记录客户端IP。
研发认为客户端IP,不都是固定的那个格式么,安全,没问题。
但是研发的同学却不知道获取客户端IP的方式有很多种,很多个字段都可以在客户端进行任意伪造,因此在测试时可以判断一下目标功能点是否可能记录客户端IP,如果可能那么便可以对记录客户端IP的字段进行SQLi漏洞的测试。
例如下面这段PHP代码,研发的先通过
Client-ip
来获取IP,获取不到时,再尝试通过
X-Forwarded-For
来获取,最后才通过不可伪造的
Remote-Addr
来获取。
![a9a729c1ac6aa5ef7877372ee8d4b819.png](https://img-blog.csdnimg.cn/img_convert/a9a729c1ac6aa5ef7877372ee8d4b819.png)
时间参数
在笔者与研发线沟通漏洞修复的问题时,发现有些研发竟然会对时间型的参数保持怀疑态度,可能是因为时间型的参数在插入时要保持格式的一致的原因,导致有些研发不敢去处理这个时间型的参数。 总觉得使用预编译的话可能会引起异常,因此便直接拼接了。![c5a3c8b8d139f68624c436fc425c56ac.png](https://img-blog.csdnimg.cn/img_convert/c5a3c8b8d139f68624c436fc425c56ac.png)
MySQL中各位置的测试
在具体测试每个参数的时候需要先判断该参数位于SQL语句中的什么位置,然后有针对性的进行测试。 在开始之前,先看一下MySQL中SELECT语句的语法,因为查询功能是最为常见的功能。![e8ea65b78198d67c7365cf768f52e014.png](https://img-blog.csdnimg.cn/img_convert/e8ea65b78198d67c7365cf768f52e014.png)
表名字段名
这里仅说一下注意点,闭合字符的使用。 像下面这个情况,你在闭合的时候则需要考虑使用反单引号,而不是单引号、或者双引号SELECT * FROM `bsrc` WHERE `{输入点}` = '';
一般来说表名字段名,要么没有闭合字符,要么便是反单引号。
where子句
普通条件
假设输入点在下面这个位置SELECT * FROM `bsrc` WHERE `bug_name` = '{输入点}';
因此可以用最简单的代码去发现漏洞
index.php?bug_name=x
与
index.php?bug_name=x'-'x
看一下数据库的执行结果
![3df961e73466b95e78d13e618779f87e.png](https://img-blog.csdnimg.cn/img_convert/3df961e73466b95e78d13e618779f87e.png)
![dea023025a8e2bd5a3f90b9c44b65470.png](https://img-blog.csdnimg.cn/img_convert/dea023025a8e2bd5a3f90b9c44b65470.png)
index.php?id=4
与
index.php?id=5-1
来查看回显的数据是否一致
![5a518b664a79bb5dba7f1da30b1675e4.png](https://img-blog.csdnimg.cn/img_convert/5a518b664a79bb5dba7f1da30b1675e4.png)
模糊查询
假设输入点在下面这个位置SELECT * FROM `bsrc` WHERE `bug_name` like '%输入点%';
再去用刚才这个方法去测试,发现结局却大相径庭
![b3641600f4aed908a658ff0a607ccf3b.png](https://img-blog.csdnimg.cn/img_convert/b3641600f4aed908a658ff0a607ccf3b.png)
index.php?bug_name=' and '%'='
和
index.php?bug_name=' and 'x'='
数据库查询效果图如下
![647a3aa30c8049e97bd6056458901b8e.png](https://img-blog.csdnimg.cn/img_convert/647a3aa30c8049e97bd6056458901b8e.png)
order by子句
上面已经说过了,order by
子句出现SQLi漏洞的概率非常大。
假设输入点在下面这个位置
SELECT * FROM `bsrc` WHERE `bug_name` like '%' ORDER BY {输入点1} {输入点2}
先来看一个正常的使用
先对bug_name字段进行降序,再对bug_rank字段进行升序(默认),再对第3列进行升序(默认)。
![f2e5b34f05c5fc6a6e786fdeddcebb66.png](https://img-blog.csdnimg.cn/img_convert/f2e5b34f05c5fc6a6e786fdeddcebb66.png)
index.php?order=1,1
页面异常
index.php?order=1,0
MySQL效果图如下
当列数为0或者超出所有列数时便会报错。
![8ba5a7c664d53bb1c4eece4d964e4a3b.png](https://img-blog.csdnimg.cn/img_convert/8ba5a7c664d53bb1c4eece4d964e4a3b.png)
limit子句
通过上面所说的MySQL中的SELECT语句的语法规定可以得知Limit子句后面只能跟PROCEDURE
子句、
INTO
子句;
因此当注入点是limit位置的时候,你可以使用
INTO
子句来写入文件进而证明;
但是在真实情况中,拥有可以通过
INTO
子句来写入文件的并且读取该文件的条件很少;
因此通过情况下是使用
PROCEDURE
子句。
关于
PROCEDURE
子句的详情可以看官方文档中给出的介绍。
https://dev.mysql.com/doc/refman/5.7/en/procedure-analyse.html
PROCEDURE
syntax is deprecated as of MySQL 5.7.18, and is removed in MySQL 8.0.
Payload
SELECT * FROM `bug_name` limit 10 procedure analyse(updatexml(1,concat(0x7e, user()),1), 1);
效果图
![9ec35d34d0537227a3667e9a66b4b2d6.png](https://img-blog.csdnimg.cn/img_convert/9ec35d34d0537227a3667e9a66b4b2d6.png)
order by
子句时,可以在limit后面直接使用联合查询。
如果limit前面使用了
order by
子句时,那么便只能考虑前面的Payload了。
![0daf736804aea57a0abe833aadb4ebb2.png](https://img-blog.csdnimg.cn/img_convert/0daf736804aea57a0abe833aadb4ebb2.png)
其他情况
仅语法错误时 在真实情况中,在测试时发现很多时候只有语法错误时,页面才会跟语法正确时有所区别。 先看一下最简单的证明方法 语法正常,页面正常index.php?bug_name=1' and if(1=2,(select 1 union select 2),1) and '
语法错误,页面出现差别
index.php?bug_name=1' and if(1=1,(select 1 union select 2),1) and '
语法错误,页面出现差别
index.php?bug_name=1' and if(1=1,(select 1,2),1) and '
再来看一下数据库中的执行效果
![843a0350e226a59710592dee1aedc5b5.png](https://img-blog.csdnimg.cn/img_convert/843a0350e226a59710592dee1aedc5b5.png)
一行一列
。
时间型盲注
SELECT * FROM `bsrc` WHERE `id` >= 5 AND sleep(1)
此时如果符合条件
id>=5
的有2行,那么便会执行2次
sleep(1)
。
SELECT * FROM `bsrc` WHERE `id` >= 5 OR sleep(1)
此时如果不符合条件
id>=5
的有4行,便会执行4次
sleep(1)
假设符合条件或不符合条件的数据行数过多时,那么便会休眠太久会影响数据库的运行;
因此在进行时间型盲注时,需要注意的时尽量不要直接使用sleep函数。
可以使用子查询的方式来执行睡眠。
SELECT * FROM `bsrc` WHERE `id` >= 5 AND (SELECT 1 FROM (SELECT sleep(1))a);
![3ec1ea5240eddd03f1ff8e2e76955969.png](https://img-blog.csdnimg.cn/img_convert/3ec1ea5240eddd03f1ff8e2e76955969.png)
短路原则
SELECT * FROM `bsrc` WHERE 条件1 and 条件2
当条件1查询出info数据表中的数据是0行时,即没有任何符合条件1的数据时,条
件2将不会执行。
SELECT * FROM `bsrc` WHERE 条件1 or 条件2
当条件1能查询出info数据表中的所有数据时,即所有数据均符合条件1时,条件2将不会执行。
假设输入点在下面这个位置
SELECT * FROM `bsrc` WHERE `id` = {插入点};
假设id=1的数据是不存在的,那么当你在页面输入
index.php?id=1 and sleep(1)
或者输入
index.php?id=1 and sleep(0)
这两种输入的现象将会一模一样,因为and后面的语句MySQL将不会再执行。
![271de0e3846288a7ec25f11daff524f7.png](https://img-blog.csdnimg.cn/img_convert/271de0e3846288a7ec25f11daff524f7.png)
结语
遇到一个问题解决一个,好似永远的无底洞,不如去理解它的所有构造;去思考这些东西怎么被创造的,然后再学会创造,破坏、发现Bug就非常容易了。 感谢TEag1e@米斯特的精彩分享, 知识因分享而更具价值,欢迎大家惠赐作品~ *SQL注入漏洞专项活百度安全应急响应中心
百度安全应急响应中心,简称BSRC,是百度致力于维护互联网健康生态环境,保障百度产品和业务线的信息安全,促进安全专家的合作与交流,而建立的漏洞收集以及应急响应平台。地址:https://bsrc.baidu.com
![b250be1df0becfce617aca66fe621a74.png](https://img-blog.csdnimg.cn/img_convert/b250be1df0becfce617aca66fe621a74.png)
![4f0d8e70ec7b75eeb8c20d0fd8f1416c.png](https://img-blog.csdnimg.cn/img_convert/4f0d8e70ec7b75eeb8c20d0fd8f1416c.png)
长按关注