github编辑

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-php7arrow-up-right 的 Less-1

  1. 使用 union select 语句爆破,payload 如下

前两个 payload 都是如下报错,union 前后两个语句列数不一样 Alt text 直到第三个 payload 才回显正确结果,说明原始查询结果有三列 Alt text

  1. 使用 order by 指数(order by数字以2的n次方指数增加) 和 二分法爆破,payload 如下

前两个 payload 都是能够正确回显 Alt text 直到第三个 payload,说明查询结果没有 4 列那么多,这时候就可以知道列数有三列。如果第三个payload是正常的,第四个payload是异常的,那么取中间值,也就是6((4+8)/2 = 6)继续爆破。 Alt text

联合注入常用函数

联合注入常使用如下字符串拼接函数

内置函数
描述
举例

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-php7arrow-up-right 的 Less-8,利用布尔盲注获取数据库名

当返回的页面包含 "You are in" 字符串时为 true,否则为false

正常访问返回的页面

布尔盲注获取数据库名思路:

  1. 获取数据库名的长度,遍历或用二分法爆破http://192.168.154.135/sqli-labs-php7/Less-8/?id=1' and length(database())=n--+种的n,当返回 true 的时候得到数据库的长度

  2. 使用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-php7arrow-up-right 的 Less-9,利用时间盲注获取数据库名

宽字节注入

报错注入

floor报错注入

updatexml报错注入

extractvalue报错注入

堆叠注入

二次注入

最后更新于