SQL 注入复习总结

2022-08-06

一、介绍


1.什么是SQL注入?

sql 注入是一种将 sql 代码添加到输入参数中,传递到 sql 服务器解析并执行的一种攻击手法。

2.SQL注入的原理

SQL 是操作数据库数据的结构化查询语言,网页的应用数据和后台数据库中的数据进行交互时会采用 SQL。而 SQL 注入是将 Web 页面的原 URL、表单域或数据包输入的参数,修改拼接成 SQL 语句,传递给 Web 服务器,进而传给数据库服务器以执行数据库命令。

3.形成SQL注入的原因

用户输入的数据被 SQL 解释器执行。

4.SQL注入的危害

1、攻击者未经授权可以访问数据库中的数据,盗取用户的隐私以及个人信息,造成用户的信息泄露。
2、通过操作数据库对某些网页进行篡改;
3、修改数据库一些字段的值,嵌入网马链接,进行挂马攻击。
4、服务器被远程控制,被安装后门。可以对数据库的数据进行增加或删除操作,例如私自添加或删除管理员账号。
5、数据库被恶意操作:数据库服务器被攻击,数据库的系统管理员帐户被篡改。
6、破坏硬盘数据,导致全系统瘫痪。

5.SQL注入基本知识

注:以下命令语句均基于 MySQL 数据库

(1)增删改查语句

增 Insert

基本语法 INSERT INTO 表名称 VALUES (值1, 值2,....)
例子 insert into student(name,sex,age) values('张三',18,'男')

删 delete

基本语法 DELETE FROM 表名称 WHERE 列名称 = 值
例子 delete from student where id=1
改 update

基本语法 UPDATE 表名称 SET 列名称 = 新值 WHERE 列名称 = 某值
列子 update student set name = '张三' where id=1

查 select

基本语法 SELECT 列名称 FROM 表名称
例子 select * from student

(2)系统函数

  • version()——MySQL 版本
  • user()——数据库用户名
  • database()——数据库名
  • @@datadir——数据库路径
  • @@version_compile_os——操作系统版本

(3)字符串连接函数

concat(str1,str2,...)没有分隔符地连接字符串
concat_ws(separator,str1,str2,...)含有分隔符地连接字符串
group_concat(str1,str2,...)连接一个组的所有字符串,并以逗号分隔每一条数据

(4)一般流程

Mysql 有一个系统数据库 information_schema,存储着所有的数据库的相关信息,一般的, 我们利用该表可以进行一次完整的注入。以下为一般的流程。
猜数据库
select schema_name from information_schema.schemata

猜某库的数据表
select table_name from information_schema.tables where table_schema=’xxxxx’

猜某表的所有列
Select column_name from information_schema.columns where table_name=’xxxxx’

获取某列的内容
Select *** from ****

二、分类


根据输入参数分为

  • 数字型注入
  • 字符型注入

根据注入技巧分为

  • 联合注入
  • 盲注
  • 堆叠注入
  • 报错注入
  • 二次注入
  • 宽字节注入

根据提交类型分为

  • GET 注入
  • POST 注入
  • COOKIE 注入
  • HTTP 头部注入

1.根据输入参数分类的 SQL 注入

(1)数字型注入

我们经常可以看到这样的 url http://xxx.com/users.php?id=1 基于此种形式的注入,一般被叫做数字型注入,这是因为其注入点 id 类型为数字。这一类的 SQL 语句原型一般为 select * from 表名 where id=1。

(2)字符型注入

我们有时又会看到这样的 url http://xxx.com/users.php?name=admin 基于此种形式的注入,一般被叫做字符型注入,这是因为其注入点 name 类型为字符串。这一类的 SQL 语句原型一般为 select * from 表名 where name='admin'

2.根据注入技巧分类的 SQL 注入

(1)联合注入

什么是联合注入?

联合注入顾名思义,就是使用联合查询进行注入的一种方式,是一种高效的注入的方式,适用于有回显同时数据库软件版本是5.0以上的 MYSQL 数据库。

union 操作符介绍

UNION 操作符用于合并两个或多个 SELECT 语句的结果集。请注意,UNION 内部的 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条 SELECT 语句中的列的顺序必须相同。

联合注入演示

使用 sqli-labs less1 进行联合注入的流程演示

爆数据库

http://127.0.0.1/sqli-labs-master/Less-1/?id=-1' union select 1,group_concat(schema_name),3 from information_schema.schemata--+
image

爆 security 数据库的数据表

http://127.0.0.1/sqli-labs-master/Less-1/?id=-1'union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security'--+
image

爆 users 表的列

http://127.0.0.1/sqli-labs-master/Less-1/?id=-1'union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users'--+
image

爆数据

http://127.0.0.1/sqli-labs-master/Less-1/?id=-1'union select 1,group_concat(username),3 from users--+
image

(2)盲注

什么是盲注?

盲注就是在注入过程中,获取的数据不能回显至前端页面。此时我们需要利用一些方法进行判断或尝试,这个过程称为盲注

盲注的分类
a.布尔盲注

什么是布尔盲注?

布尔盲注是指利用页面返回的对错信息来间接推测数据库中的信息的一种手段。(构造逻辑判断)

适用条件:布尔盲注一般适用于页面没有回显字段(不支持联合查询),且 web 页面返回 True 或者 false 时。

常用函数

截取函数:left() right() substr() mid()
转换函数:ascii() hex()
比较函数:if()
b.时间盲注

什么是时间盲注?

时间盲注又称延时注入,指通过页面执行的时间来判断数据内容的注入方式。

什么时候使用?

当注入时,无论我们传入什么值,网页正常或报错都显示一种页面时,那么网页的是否正常显示将不是我们用来判断是否注入成功的依据,就要用基于时间的盲注。

方法:通过if判断语句与 sleep 函数结合,通过网站访问的响应时间来判断sql语句的正确性

示例:if(left(database(),1)='s',0,sleep(3))
这段 payload 的含义是如果数据库的第一位为's'时,网页正常执行,否则延时3秒

c.报错盲注

什么是报错盲注?

是指通过构造特定的SQL语句,让攻击者想要查询的信息通过页面的错误提示回显出来的注入方式

什么时候使用:当正常的回显注入无法显示结果,网页可以显示报错信息时,就可以使用报错注入

常见的报错函数

1)floor 报错注入

在进行报错注入时,floor()函数一般需要与rand()、count()、group by联用。

示例

Select 1,count(*),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)*2)) a from information_schema.columns group by a

原理

rand() 随机产生0~1的数值,floor() 向下取整,所以 floor(rand()*2结果是0或1。

group by 用于分组,当同时执行count和group by函数时,MYSQL会创建一个虚拟表,虚拟表进行计数和分组。在执行group by语句的时候,group by语句后面的字段会被运算两次。

floor会报错的原因就是group by在向临时表插入数据时,插入重复主键导致的报错,又因为报错之前concat()里的语句已经执行过了,所以会直接爆出concat函数里执行后的结果

2)exp 报错注入

示例

select exp(~(select * FROM(SELECT USER())a))

原理

exp()即为以e为底的对数函数,exp中的函数成功执行后返回0,对0按位取反会返回一个无符号的BIGINT值,所以会造成Double型数据溢出错误从而报错,借此得到数据。

3)extractvalue 和 updatexml 报错注入

示例

extractvalue(1,concat(0x7e,(select @@version),0x7e))
updatexml(1,concat(0x7e,(select @@version),0x7e),1)

原理

当使用 extractvalue(xml_frag, xpath_expr) 函数时,若 xpath_expr 参数不符合 xpath 格式,就会报错。而 ~ 符号(ascii 编码值:0x7e)是不存在 xpath 格式中的, 所以一旦在 xpath_expr 参数中使用 ~ 符号,就会产生 xpath syntax error (xpath语法错误),通过使用这个方法就可以达到报错注入的目的。
updatexml同理

(3)堆叠注入

什么是堆叠注入?

顾名思义,堆叠注入就是将一堆 sql 语句叠加在一起执行,使用分号结束上一个语句再叠加其他语句一起执行。

堆叠注入产生的原因?

服务器在访问数据端时使用了可同时执行多条 sql 语句的方法,比如 php 中的mysqli_multi_query()函数

联合注入与堆叠注入的区别

union injection(联合注入)也是将两条语句合并在一起,两者之间有什么区别么?区别就在于 union 或者 union all 执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。

堆叠注入演示

使用 sqli-labs 进行演示

可以直接构造如下的 payload:

http://127.0.0.1/sqli-labs-master/Less-38/index.php?id=1%27;insert%20into%20users(id,username,password)%20values%20(%2738%27,%27less38%27,%27hello%27)--+

image

打开 phpMyAdmin 可以看到 sql 语句被成功执行,用户成功添加

image

(4)二次注入

什么是二次注入?

二次注入是指已存储(数据库、文件)的用户输入被读取后,再次进入到SQL查询语句中导致的注入。

二次注入流程

第一步:插入恶意数据
第二步:引用恶意数据

二次注入演示

使用 sqli-labs less24 进行演示

image

查看当前用户,可以看到 admin 的初始密码为123

image

注册用户名为 admin’# 的账号

image

image

登录 admin’#,并修改密码为 666666

image

如图 admin 的密码被修改

image

(5)宽字节注入

什么是宽字节?

当某字符的大小为一个字节时,称其字符为窄字节.当某字符的大小为两个字节时,称其字符为宽字节.所有英文默认占一个字节,汉字占两个字节

转义函数
为了过滤用户输入的一些数据,对特殊的字符加上反斜杠“\”进行转义;Mysql中转义的函数包括 addslashes,mysql_real_escape_string,mysql_escape_string 等,还有一种是配置magic_quote_gpc(魔术引号),不过 PHP 高版本已经移除此功能。

宽字节注入原理?

我们在注入过程中,输入语句中的特殊字符会被 addslashes()这类转义函数进行转义,但 MySQL 采用了 gbk 编码,会将两个字节看作一个汉字,例如,\ 的 URL 编码是%5c 当用户加上 %df 形成 %df%5c 数据库会自动将 %df%5c 识别成汉字,从而将 \ 闭合,起到了绕过转义的效果,这样即可实现注入攻击。

3.根据提交类型分类的 SQL 注入

(1)GET 注入

什么是 GET 注入?

GET 注入:通过 GE T传参的方式,传输恶意语句,进行 SQL 注入

GE T注入介绍

提交数据的方式是 GET,注入点的位置在 GET 参数部分。比如有这样的一个链接http://xxx.com/news.php?id=1,其中id是注入点。

(2)POST 注入

什么是 POST 注入?

POST注入:通过POST传参的方式,传输恶意语句,进行SQL注入,本质和GET注入是一样的

POST 注入介绍

使用 POST 方式提交数据,注入点位置在 POST 数据部分,常发生在表单中。

(3)COOKIE 注入

什么是 COOKIE 注入?

修改 cookie 的值进行注入的方式

COOKIE 注入介绍

HTTP请求的时候会带上客户端的Cookie,注入点存在Cookie当中的某个字段中。

(4)HTTP 头部注入

什么是 HTTP 头部注入?

一般获取头部的信息用于数据分析,通过请求头部可以向数据库发送查询信息,构造恶意语句可以对数据库进行信息查询。

HTTP 头部注入介绍

注入点在HTTP请求头部的某个字段中。比如存在User-Agent字段中。严格讲的话,Cookie其实应该也是算头部注入的一种形式。因为在HTTP请求的时候Cookie是头部的一个字段。

三、绕过


关键字通用绕过思路

1. 双写绕过关键字过滤

2. 使用同义函数/语句代替

如if函数可用如下语句代替
case when condition then 1 else 0 end

3. 预处理,或其他手段造成字符串拼接来绕过

1';set @a=concat("sel","ect * from users");prepare sql from @a;execute sql;

4. 大小写变换

-1' UnIoN SeLeCt 1,2,database()--+

5. 编码绕过
使用URL编码对输入语句进行加密,然后服务器端会对其进行解密。

6. 内联注释绕过

内联注释就是把一些特有的仅在MYSQL上的语句放在 /!.../ 中,这样这些语句如果在其它数据库中是不会被执行,但在MYSQL中会执行。

常见关键字绕过思路

1. and/or 被过滤/拦截

(1)双写anandd、oorr

(2)使用运算符代替&&、||

(3)直接拼接=号,如:?id=1=(condition)

(4)其他方法,如:?id=1^(condition)

2. 空格被过滤/拦截

(1)注释绕过空格

示例:
select/**/user()/**/from/**/dual

(2)括号绕过空格

示例:select(group_concat(table_name))from(information_schema.taboles)where(tabel_schema=database());#
(3)Tab 代替空格

(4)使用两个空格

(5)使⽤其他不可⻅字符代替空格

如 %09, %0a, %0b, %0c, %0d, %a0

3. 逗号被过滤/拦截

(1)改用盲注

(2)使用 join 语句代替

示例:
union select * from ((select 1)A join (select 2)B join (select 3)C);

(3)使用 substring 函数

substring(str FROM pos)

4. 单双引号被过滤/拦截/转义

(1)需要跳出单引号的情况:尝试是否存在编码问题而产生的SQL注入(宽字节注入等)

(2)不需要跳出单引号的情况:字符串可用十六进制表示、也可通过进制转换函数表示成其他进制。

5. 比较符号(= < >)被过滤/拦截

(1)使用 in() 绕过

示例:
?id=' or substr((select database()),1,1) in('s')

(2)greatest,strcmp 等函数进行绕过

示例:
select * from users where id=1 and greatest(ascii(substr(database(),0,1)),64)=64

select strcmp(left(database(),1),0x32);#lpad('asd',2,0)

四、防御


SQL注入危害虽大,但是可以完全杜绝

1.严格的数据类型

在传入参数的地方限制参数的类型,比如整型 Integer,随后加入函数判断,如is_numeric($_GET[‘id’]) 只有当 get 到的 id 为数字或者数字字符时才能执行下一步。但这种方法存在一定的限制,只能在特定的页面才能使用,一般大部分都是要求我们传入的字符串,但可以很大程度限制整型注入的情况。

2.特殊字符转义

数字型注入可以通过检查数据类型防御,字符型则需要对特殊字符进行转义。在 MySQL 中我们需要对" '进行转义,这样可以防止恶意攻击者来闭合语句。我们通常使用 addslashes() 等安全函数来转义特殊字符。

3.使用预编译语句

当一条 SQL 语句执行前,需要先进行语法分析。如果没有使用预编译的方法来执行 SQL,那么每次在执行 SQL 时都需要先进行语法分析,然后再执行。此时进行 SQL 注入,恶意的代码(payload)拼接上 SQL 语句之后才会进行语法分析,其中 payload 中包含的关键字等内容便会被识别,因此会被成功执行。

而预编译则是先在待传入参数的位置增加占位符(?),并对 SQL 语句进行语法分析和编译,并将结果存入内存中等待调用。在调用时,传入的参数会自动放入占位符的位置,并执行 SQL 语句。此时如果进行注入,由于并没有重新进行语法分析,payload 中包含的关键字等不会被识别,而是作为字符串的形式存在于语句中,因此 payload 不会成为 SQL 语句的一部分。

预编译可以防止 sql 注入的原因:允许数据库做参数化查询。在使用参数化查询的情况下,数据库不会将参数的内容视为 SQL 执行的一部分,而是作为一个字段的属性值来处理,这样就算参数中包含破环性语句(or ‘1=1’)也不会被执行。