SQL 注入漏洞详解
本文最后更新于 2023年12月20日 14:28
SQL注入漏洞原理
漏洞原理
简单点说,就是服务端未对用户的输入进行过滤和验证,导致恶意的sql语句直接与后端sql查询语句进行,造成了非本意的查询结果并且回显到页面上,最终导致数据库敏感信息泄露等。
SQL 注入漏洞的核心原理在于攻击者能够将恶意的 SQL 代码注入到应用程序的 SQL 查询语句中,从而执行未经授权的数据库操作
漏洞场景
SQL注入漏洞可能出现在一切与数据库交互的地方,比如常见的查询用户信息、查询订单信息等查询处;搜索、筛选、过滤等;更新用户信息、添加备注等。另外,其他日志记录、分析等处也可能出现,比如记录用户登录处,记录用户登录日志处;比如常见的请求头中的字段,UA、XFF等。
检测方法
可以利用 sqlmap
工具 进行SQL注入的检查或利用,也可以使用其他SQL注入工具。也可以手工测,利用单引号、and 1=1以及字符型注入进行判断。
最后结尾记录记录一下 sqlmap
工具的使用
防御措施
- 使用参数化查询
- 输入验证和过滤
- 使用存储过程
- 最小权限原则
- 使用ORM框架
- 使用准备语句
- 使用安全的数据库连接
- 避免动态拼接SQL语句
- 使用防火墙和入侵检测系统
- 定期更新和维护数据库软件
SQL注入内容
SQL 注入需要满足以下两个条件:
- 参数可控:从前端传给后端的参数内容是用户可以控制的
- 参数带入数据库查询:传入的参数拼接到 SQL 语句,且带入数据库查询。
判断注入
- 当用户传入参数为 1’的时候,在数据库执行如下所示:
1
select * from users where id=1'
此 SQL语句不符合语法规则就会报错。
1 |
|
- 当用户传入参数为 1 and 1=1 时
1 |
|
因为 1=1 为真,id=1 也是真, and 两边均为真。所以页面会返回 id=1 的结果。
- 如果用户传入参数为 1 and 1=2 时
因为 1=2 为假 id=1 为真 and 两边有一个为假,所以页面返回与 id=1 不一样的结果。
由此可以初步判断存在 SQL 注入漏洞,攻击者可以进一步拼接 SQL 攻击语句,进行攻击,致使信息泄露,甚至获取服务器权限。
其实也就是看有没有回显。回显是指页面有数据信息返回。无回显是指根据输入的语句,页面没有任何变化或者没有数据库中的内容显示到网页中。
1 |
|
SQL注入分类
- 从注入参数类型分:数字型注入、字符型注入
- 从注入效果分:报错注入、无显盲注(布尔盲注、延时盲注)、联合注入、堆叠注入、宽字节注入、二次注入
- 从提交方式分:GET注入、POST注入、HTTP头注入(UA注入、XFF注入)、COOKIE注入
information_schema(一个数据库)
Mysql 5 以上有内置库 information_schema,存储着mysql的所有数据库和表结构信
重要的表
SCHEMATA 表
当前mysql数据库软件中所有数据库名称。
TABLES 表
存储数据库中的所有的表的名称,包括表属于哪个数据库。
COLUMNS 表
存储表中的所有列的名称,包括列属于哪个表。
练习语句
1 |
|
1 |
|
SQL 详细注入过程
实验环境:
- 结合 sqlilab 靶场
burpsuite
联合查询:
- sqlilab 靶场第1关为例
联合查询是最常用的SQL 注入手法,有两个要求:一是字段数相同,二是数据类型相同。
联合查询可以跨库,跨表查询
利用 order by
判断数据库字段为几列
1 |
|
通过尝试取3或者以内的值时,不报错;超过3之后就报错;说明此表有三列
猜数据库:
1 |
|
payload 利用另一种方式:
1 |
|
得到数据库名:security 版本号:5.5.53
PS:union 查询结合了两个 select 查询结果,根据上面的 order by 语句我们知道查询包含三列,为了能够现实三列查询结果,我们需要用 union 查询结合我们构造的另外一个 select.注意在使用 union 查询的时候需要和主查询的列数相同。
猜表名:
1 |
|
得到表名:emails,referers,uagents,users
通过上面的信息我们发现security库中有四个表,users表中极有可能存放用户登陆账号和密码
元数据库中爆users表的字段名
1 |
|
报错注入
- sqlilab 靶场第2关为例
使用两种函数实现报错注入
原理:是基于mysql数据管理软件函数的特性,将想查询的数据,通过报错语句回显到页面上1
?id=1' and extractvalue(1,concat(0x5e,(select database()),0x5e)) --+
?id=1'
将注入的内容开始,并在数字1后面添加了一个单引号,目的是破坏原始SQL查询的语法结构。and extractvalue(1,concat(0x5e,(select database()),0x5e))
是一个XPath注入攻击,用于提取数据库信息。这部分的作用是在XPath查询中将数据库名称连接起来。--+
是SQL中的注释语法,表示从此处开始注释,直到行尾。这里的+
只是为了确保注释符号不被过滤。1
?id=1'and updatexml(1,concat(0x7e,database(),0x7e),1) --+
?id=1'
: 将注入的内容开始,并在数字 1 后面添加了一个单引号,目的是破坏原始 SQL 查询的语法结构。and updatexml(1,concat(0x7e,database(),0x7e),1)
: 使用updatexml
函数,该函数用于更新 XML 数据。在这里,它的目的是将数据库名称(通过database()
函数获取)插入到 XML 结构中。--+
: 是 SQL 中的注释语法,表示从此处开始注释,直到行尾。这里的+
只是为了确保注释符号不被过滤。
布尔盲注
- sqlilab 靶场第5关为例
这关我们发现id=1和id=1000页面回显不一样
由此可知只有正确的数值他才有回显
可以先用length(database())
函数得出当前数据库的一个长度发现在长度为8的时候页面才有回显1
?id=1' and length(database())=8--+
得出他的数据库名长度之后可以使用ascii(substr(database(),1,1))
来判断出他的第一个字符是什么1
2
3
4
5
6
7
8
9
10?id=2' and ascii(substr(database(),1,1))=115 --+
# 115
# s
?id=2' and ascii(substr(database(),2,1))=101 --+
//`and ascii(substr(database(),2,1))=101` 是在尝试检查当前数据库名称的第二个字符是否为 ASCII 值为 101,这对应于字符 'e'。
# 115 101
# s e
只有正确页面才会有回显 由此可知前两位数据库字符为s e
延时注入
- sqlilab 靶场第9关为例
这关我们发现id=1和id=10000页面回显一样
这样我们就可以用延时注入的一个手法,使得让条件为真的数据服务器延时几秒后响应1
2?id=1'and If(ascii(substr(database(),1,1))=115,sleep(5),1)--+
//查询尝试测试当前数据库名称的第一个字符是否为 ASCII 值为 115(即字符 's')。如果条件满足,它将执行 `sleep(5)`,导致查询延迟 5 秒。
?id=1'
将注入的内容开始,并在数字 1 后面添加了一个单引号,目的是破坏原始 SQL 查询的语法结构。and If(ascii(substr(database(),1,1))=115,sleep(5),1)
是条件语句,检查当前数据库名称的第一个字符是否为 ASCII 值为 115。如果满足条件,则执行sleep(5)
,否则返回 1。--+
是 SQL 中的注释语法,表示从此处开始注释,直到行尾。这里的+
只是为了确保注释符号不被过滤。
当条件为真的时候,服务器会响应更长的时间
sqlmap
工具的使用
1 |
|
sqlmap
: SQL 注入渗透测试工具的命令行工具。-r 1.txt
: 从文件中读取 HTTP 请求,文件中包含了被测试网站的一个请求,以便 SQLMap 进行分析和注入检测。--dbs
: 列出所有的数据库名。--current-user
: 显示当前连接数据库的用户名。--current-db
: 显示当前数据库的名字-D "mysql"
: 指定目标数据库为 MySQL。--tables
: 列出目标数据库中的所有表名。-T "user"
: 指定目标表名为 ‘user’。--columns
: 列出目标表中的所有字段名。-C 'username,password'
: 指定目标字段为 ‘username’ 和 ‘password’。--dump
: 列出目标字段的内容。--os-shell
: 在特定情况下,可以直接获得目标系统 Shell。--level 3
: 设置 SQLMap 检测等级为 3。--risk 3
: 设置 SQLMap 注入风险等级为 3。--batch
: 在批处理模式下运行,避免提示用户输入。-p "id"
: 指定注入点为参数 ‘id’。--proxy http://127.0.0.1:8080
: 使用代理服务器,将请求通过本地代理服务器发送。
声明:本文所涉及到的仅用于学习交流使用,如有其他与本人无关