sql注入漏洞原理
什么是 SQL 注入
SQL 注入类型
常见 SQL 注入类型有7种,分别是:联合注入、布尔盲注、时间盲注、宽字节注入、报错注入(floor 报错注入、updatexml报错注入、extractvalue报错注入)、堆叠注入、二次注入。
MySQL 基础知识
SQL语句中的特殊字符
注释
单行注释:
单行注释可以使用 # 注释符,# 注释符后直接加注释内容
单行注释可以使用--注释符,--注释符后需要加一个空格,注释才能生效
#从结果中删除重复行
SELECT DISTINCT product_id, purchase_price FROM Product;
-- 从结果中删除重复行
SELECT DISTINCT product_id, purchase_price FROM Product;多行注释
多行注释使用 /* */ 注释符。/* 用于注释内容的开头,*/ 用于注释内容的结尾。多行注释格式如下
小知识
如果参数从 url 传入,url 中的参数值出现 +,空格,/,?,%,#,& 等特殊符号的时候就自动变成空格。所以 sql 注入常见的单行注释用 --+ ,如果想使用 # 做注释,就需要使用 url 编码,# 的 url 编码是 %23
举例
MySQL 内置函数
发现 SQL 注入点之后,可以利用数据库的默认函数和自带的数据库,进一步收集数据库相关信息,包括数据库类型、版本号,数据库表等。利于后续漏洞利用
version()
获取操作系统 版本号
database()
获取当前使用的数据库
user()
获取当前使用数据库的用户
@@version_compile_os
获取操作系统类型
@@datadir
获取数据库路径
样例如下
MySQL 内置数据库
MySQL 默认数据库 information_schema 有三个关键数据表:schemata,tables, columns
schemata 表中有字段叫 schema_name,存放的数据是数据库中所有数据库的名称
tables 表中有字段 table_name(表名),table_schema(表所在的数据库名)
columns 表中有字段 column_name(字段名), table_name(字段所在的表)
如此注入流程就如下:
select schema_name from information_schema.schemata
查询所有数据库
select table_name from information_schema.tables where table_schema='xxx'
查询 xxx 数据库的所有表
select column_name from information_schema.columns where table_name='xxx'
查询 xxx 表的列名
select xxx from xxx
获取数据
样例如下
联合注入
顾名思义,联合注入是使用联合查询进行注入的一种方式。联合注入应用的常见一般是有查询结果返回,结合联合查询,可以查询到自己想要的结果。
查询语句以 union 关键字连接起来,返回多条查询语句的并集。
每条查询语句的返回结果列数需要一致。
联合查询举例
联合注入示例
联合注入关键在于确定数据库查询结果的列数,查询结果的列数,可以使用 使用 union select 或 order by 语句探测查询结果的字段。
如 sqli-labs-php7 的 Less-1
使用 union select 语句爆破,payload 如下
前两个 payload 都是如下报错,union 前后两个语句列数不一样
直到第三个 payload 才回显正确结果,说明原始查询结果有三列 
使用 order by 指数(order by数字以2的n次方指数增加) 和 二分法爆破,payload 如下
前两个 payload 都是能够正确回显
直到第三个 payload,说明查询结果没有 4 列那么多,这时候就可以知道列数有三列。如果第三个payload是正常的,第四个payload是异常的,那么取中间值,也就是6((4+8)/2 = 6)继续爆破。 
联合注入常用函数
联合注入常使用如下字符串拼接函数
concat(str1, str2, ...)
拼接字符串
concat("1", "2", "3") --> "123"
concat_ws(separator, str1, str2, ..)
拼接字符串,各个字符之间插入separator
concat("/", "1", "2", "3") --> "1/2/3"
group_concat(column_name)
将查询结果用,拼接成一个字符串
select group_concat(username) from users; --> "user1,user2,user3 .."
样例
布尔盲注
布尔盲注的常见一般是存在注入的 sql 语句的执行结果只返回布尔值 True或False,那么布尔盲注就是根据页面返回的True或者是False来得到数据库中的相关信息。
布尔注入常用函数
length(str)
返回值为字符串的字节长度
length("123") --> 3
ascii(char)
把字符转换成ascii码值的函数
ascii('0') --> 48
substr(str, pos, len)
在str中从pos开始的位置(起始位置为1),截取len个字符
count()
统计表中记录的一个函数,返回匹配条件的行数
sql 语句 limit关键字
limit m :检索前m行数据,显示1-10行数据(m>0)
limit x, y:检索从x+1行开始的y行数据
布尔盲注示例
如 sqli-labs-php7 的 Less-8,利用布尔盲注获取数据库名
当返回的页面包含 "You are in" 字符串时为 true,否则为false

布尔盲注获取数据库名思路:
获取数据库名的长度,遍历或用二分法爆破
http://192.168.154.135/sqli-labs-php7/Less-8/?id=1' and length(database())=n--+种的n,当返回 true 的时候得到数据库的长度使用ascii、substr、database函数,利用二分法爆破数据库名称,如
http://192.168.154.135/sqli-labs-php7/Less-8/?id=1' and ascii(substr(database(), i, 1))=n--+种的第 i 个字符对应的 ascii 码为 n,那么对应的字符为 chr(n)
利用 python 脚本爆破数据库名如下:
时间盲注
在SQL注入过程中,无论注入是否成功,页面完全没有变化,无法通过页面判断是否存在注入,只能通过使用数据库的延时函数,根据执行返回的时长变化来判断是否存在注入。
时间盲注常使用 if(expr1, expr2, expr3), 其中 expr2 通常为 sleep 函数,当 expr1 为 true 时,返回 expr2, 否则返回 expr3。时间盲注常可以看作是布尔注入。
如 sqli-labs-php7 的 Less-9,利用时间盲注获取数据库名
宽字节注入
报错注入
floor报错注入
updatexml报错注入
extractvalue报错注入
堆叠注入
二次注入
最后更新于