什么是 SQL 注入

    ● 服务器没有严格的校验客户端发送的数据,导致将用户精心构造的数据作为 SQL 语句的一部分执行,从而使数据库信息泄露或被修改。

    SQL 注入的发现流程

    1. 确认目标网页是动态的,网页能够接收用户的参数,并作出不同的反应。
      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 注入漏洞查找工具

    1. 爬取目标网站的所有页面(站内链接、站外的链接)
      ○ 使用 re 模块:re.findall(html, ‘href=”.*?”‘),
      ○ 使用 bs 模块:bs.select(‘a’)

      2. 爬取路径的过程中,需要注意
      ○ 去重,将所有的重复链接去除掉,可以使用 set
      ○ 注意绝对路径(直接访问)和相对路径(拼接),才能够正常访问

      3. 取出每一个链接,进行 FUZZING(枚举)
      ○ 是否存在布尔注入漏洞,?id=1 and 1 ?id=1 and 0
      ○ 判断是否有返回内容: 联合查询注入
      ○ 判断是否有报错信息: 报错注入
      ○ 进行盲注: 布尔盲注和时间盲注