测试 SQL 注入是字符型还是数字型的方法
在 SQL 注入测试中,首先需要判断注入点属于数字型还是字符型(字符串型),因为两者的注入语法和闭合方式完全不同。以下是几种常用且有效的测试方法。
方法一:使用单引号(最简单直接)
向参数提交一个单引号 ‘,观察页面返回。
字符型:SQL 语句中会使用单引号包裹用户输入,例如 WHERE name = ‘$input’。输入 ’ 后,SQL 变成 WHERE name = ‘’’,导致引号未闭合,通常会触发数据库错误(如 You have an error in your SQL syntax)或页面异常显示。
数字型:SQL 中直接拼接数字,例如 WHERE id = $input。输入 ’ 后,SQL 变成 WHERE id = ‘,因为 ’ 不是有效数字,数据库可能会报类型转换错误,或者由于参数被强制转换(如 MySQL 中 ‘1’ 会自动转为 1),但通常不会出现明显的语法错误,可能返回正常或空结果。
如果单引号导致页面报错或出现数据库错误信息,大概率是字符型。如果页面无变化或只返回空结果,可能是数字型(但需进一步确认)。
方法二:使用逻辑运算(and 1=1 / and 1=2)
分别提交两个请求:参数=原始值 and 1=1 和 参数=原始值 and 1=2。
数字型:SQL 直接拼接,例如 id = 1 and 1=1 为真,id = 1 and 1=2 为假。两次返回结果应不同(前者有数据,后者无数据或页面不同)。
字符型:直接拼接会导致语法错误(因为缺少引号闭合)。需先闭合引号,例如 name = ‘admin’ and ‘1’=‘1’。因此测试时使用 参数=原始值’ and ‘1’=‘1 和 参数=原始值’ and ‘1’=‘2。
测试步骤:
先判断是否可以使用 and 1=1 和 and 1=2 产生差异。
如果直接加入 and 1=1 页面正常,and 1=2 页面异常 → 数字型。
如果直接加入报错,尝试在参数后加单引号再跟 and ‘1’=‘1,如果成功且结果差异 → 字符型。
方法三:使用加法/减法运算(适用于数字型)
提交 参数=原始值+1 或 参数=原始值-1。
数字型:SQL 会计算算术表达式,例如 id = 1+1 等价于 id = 2,返回 id=2 的结果。如果页面结果与原始值(如 id=1)不同,说明是数字型。
字符型:‘admin’+1 在大多数数据库中会报错或转换为数字(如 MySQL 中 ‘admin’ 转为 0,‘admin’+1 = 1),但这可能产生不可预测结果。但通常字符型不会直接支持算术运算,容易报错。
注意:该方法适用于支持隐式类型转换的数据库(如 MySQL),但不如前两种可靠。
方法四:利用注释符号测试闭合方式
提交 参数=原始值’ – (注意 – 后要有空格) 或 参数=原始值’ #(MySQL)。如果页面返回正常,说明字符型且单引号被正确闭合注释掉。如果报错,尝试双引号 " 或括号等。
数字型:不需要注释,直接加 and 1=1 即可工作。
综合判断流程
先提交单引号 ‘,观察页面是否报错 → 报错则字符型嫌疑大。
提交 and 1=1 和 and 1=2:
如果两者返回不同结果 → 数字型。
如果报错,则尝试提交 ’ and ‘1’=‘1 和 ’ and ‘1’=‘2:
如果返回不同结果 → 字符型(单引号闭合)。
如果仍报错,尝试双引号 " and “1”=“1 → 字符型(双引号闭合)。
提交 参数=原始值+1,如果页面结果与原始值不同 → 数字型。
实际测试 Payload 示例
假设原始 URL 为 http://example.com/page?id=1。
测试 Payload 预期现象(数字型) 预期现象(字符型)
?id=1’ 可能报类型错误或正常返回空 数据库语法错误,页面报错
?id=1 and 1=1 正常返回数据 可能报错(缺少引号)
?id=1 and 1=2 无数据或不同页面 可能报错
?id=1’ and ‘1’=‘1 报错(因为数字型不应有引号) 正常返回数据(闭合成功)
?id=1’ and ‘1’=‘2 报错 无数据(假条件)
?id=1+1 返回 id=2 的数据 报错或返回其他(如 ‘1’+1 可能变为 2 但罕见)
最可靠的方法组合:单引号 + 逻辑运算对比。只要掌握上述三种主要探测方式,就能快速区分数字型与字符型注入点。判断清楚后,即可根据类型构造具体的注入语句(如 UNION 查询、报错注入等)。
如何判断 SQL 注入中过滤掉了哪些内容
在 SQL 注入测试中,确定了注入点类型(数字型/字符型)之后,下一步往往是探测 Web 应用或 WAF 对输入实施了哪些过滤。这包括:过滤了哪些特殊字符、哪些 SQL 关键字、是否进行了转义、是否使用了黑名单或白名单等。下面给出系统性的判断方法。
一、基础探测思路
过滤的本质是破坏或阻止你构造有效的 SQL 注入 payload。判断过滤内容的核心方法是:试探性地输入可能被用于注入的字符/关键字,观察返回结果(报错信息、响应内容、页面行为)与正常输入的差异。通过差异推断哪些字符被删除、替换、转义或直接拦截。
二、具体测试步骤与判断依据
1. 测试特殊字符是否被过滤/转义
这些字符常用于闭合语句或构造注入:’ " ` ) ( ; % \ 等。
方法:依次输入这些字符,观察:
直接消失:例如输入 1’,回显变成 1(引号被删除) → 可能使用了黑名单删除。
被转义:输入 1’,回显 1' 或数据库报错 ’’’’ 转义后多一个引号 → 可能开启了 addslashes() 或 magic_quotes_gpc(已废弃)或使用了转义函数。
触发 WAF 阻断:返回 403、跳转、或提示恶意请求 → 该字符在黑名单中且触发了拦截。
无影响且无报错:正常执行 → 该字符可能未被过滤,或数据库本身能处理。
常见测试集:
' " ` ) ( ; \ %0a %0b /* // # -- +
2. 测试注释符是否可用
注释符用于闭合语句剩余部分:–、#、/*。输入 ’ – 或 1’ # 后页面表现:
原本错误的语句变得正常 → 注释符有效且未被过滤。
返回错误或依然异常 → 可能注释符被过滤(删除或转义)。可尝试 URL 编码 %23(#)、%2D%2D(–)或使用 ;%00 等替代。
3. 测试 SQL 关键字是否被过滤
常用关键字:SELECT、UNION、AND、OR、WHERE、FROM、ORDER、BY、INSERT、UPDATE、DELETE、DROP 等。
探测方法:在参数中注入关键字,观察响应。
大小写绕过测试:输入 SeLeCt。若小写被过滤,大写可能漏过 → 说明过滤可能不区分大小写或仅过滤特定大小写形式。
双写绕过测试:输入 SELSELECTECT。若过滤机制仅删除一次关键字,可能残留 SELECT。
使用注释或换行分割:输入 SEL/foo/ECT。若内联注释未被过滤,可能绕过。
URL 编码/双重编码:%53%45%4C%45%43%54。如果 WAF 解码一次而应用解码两次,可能绕过。
观察报错:如果输入包含 AND 1=1,页面报错“非法关键字” → 明确过滤了 AND。
没有报错但注入失败:可能是过滤了该关键字,也可能是其他原因(如参数类型限制)。
4. 测试空格是否被过滤
注入语句中空格常被用于分隔关键字。输入多个空格、%20、%0a、%09、%0d、/**/、%0b、%0c 等。
如果普通空格被删除(如 1 and 1=1 变成 1and1=1 报错),尝试使用其他空白符替代:%0a、%0d、%09(Tab)、%0b、%0c。
若所有空白符都被过滤,可尝试使用内联注释 /注释/ 代替空格,如 1/foo/and/bar/1=1。
5. 测试等号(=)是否被过滤
= 常用于条件判断。可替代方法:LIKE、REGEXP、IN、BETWEEN、<>、>、< 等。
输入 1 and 1=1 如果报错,尝试 1 and 1 like 1。若后者成功,说明 = 被过滤。
6. 测试函数名是否被过滤
例如 DATABASE()、VERSION()、USER()、SLEEP()、BENCHMARK() 等。
若 AND SLEEP(5) 无效但 AND BENCHMARK(1000000,MD5(‘a’)) 有效 → SLEEP 被过滤。
尝试大小写变形、双写、注释分割,或使用等价函数(如 SLEEP 可用 BENCHMARK 替代)。
7. 联合测试 —— 使用故意错误的 payload 观察错误信息
构造一个必然出错的注入,例如 id=1’ and extractvalue(1,concat(0x7e,version()))–+(针对 MySQL)。如果返回数据库错误信息,可能会直接揭示哪些字符或语法被过滤。很多情况下,错误信息会显示被转义后的内容或拦截日志。
三、高级方法:二分法/字典枚举
如果手动测试繁琐,可以使用脚本向目标发送大量 payload,根据响应内容(页面长度、状态码、返回文本)自动判断过滤规则。例如:
发送 1’ +
发送 1 union select 1,2,3,逐步修改关键字,直到不再被拦截。
四、典型过滤模式及特征一览表
| 过滤现象 | 可能原因 |
|---|---|
| 输入 ’ 变成 ' | 使用了 addslashes 或 mysqli_real_escape_string |
| 输入 ’ 消失 | 黑名单删除单引号 |
| 输入 union select 导致 403/500 | WAF 或代码中过滤了该关键字组合 |
| 输入 %27(单引号的URL编码)正常 | 后端解码后未正确过滤,或WAF未检测编码 |
| 空格变成 + 或消失 | 过滤空格,但未过滤其他空白符 |
| 输入 1 and 1=1 正常,但 1 union select 1 报错 | 可能仅过滤了 union 关键字 |
| 所有输入都变成纯文本/无变化 | 参数可能使用了 intval() 强制转换或参数化查询,无法注入 |
五、实际操作建议
分段测试:先测试特殊字符,再测试 SQL 关键字,最后测试函数。
保留原始正常请求:作为对比基准。
使用不同的注入位置:GET 参数、POST body、Cookie、HTTP 头,过滤规则可能不同。
尝试编码绕过:URL编码、Unicode编码、十六进制编码、双重编码。
使用内联注释:MySQL 中 /!50000SELECT/ 可能绕过简单黑名单。
结合报错信息:如果开启了错误显示,可以直接看到被转义后的 SQL 片段。
六、示例:判断过程实录
假设原始 URL:http://example.com/news.php?id=1
测试 id=1’ → 页面空白,无报错 → 可能单引号被删除或转义。
测试 id=1%27 → 同样空白 → 过滤了单引号。
测试 id=1 and 1=1 → 页面正常返回新闻内容 → and 未被过滤。
测试 id=1 union select 1 → 页面返回 403 Forbidden → 关键字 union 被 WAF 拦截。
测试 id=1 UnIoN SeLeCt 1 → 也返回 403 → 过滤不区分大小写。
测试 id=1 /!50000union/ select 1 → 页面返回正常(无注入结果)但仍正常显示 → 可能内联注释没有被正确解析,或者联合查询本身因列数不对而失败,需要进一步测试。但至少说明 /!/ 没有被过滤掉。
结论:单引号被过滤;union 关键字被黑名单过滤(不区分大小写),但其他关键字(and)可用;可使用内联注释,但需要寻找其他注入方式(如布尔盲注、时间盲注)。
通过以上系统化的测试,可以逐步绘制出目标应用的“过滤映射表”,从而定制出能绕过这些规则的注入 payload。
SQL注入绕过过滤技术大全
在SQL注入过程中,目标应用常常会实施各种过滤(黑名单、转义、WAF规则等)。掌握绕过技术是成功注入的关键。本文按过滤类型系统介绍绕过方法,每种方法附带原理和示例。
一、关键字过滤绕过
应用可能过滤 SELECT、UNION、AND、OR、WHERE 等SQL关键字。
1. 大小写变形
原理:过滤仅针对特定大小写形式(如全部小写)。
示例:SeLeCt、UnIoN、aNd、oR。
适用:简单黑名单。
2. 双写关键字
原理:过滤逻辑可能是删除关键字,删除一次后剩余部分重新拼接成完整关键字。
示例:SELSELECTECT → 删除中间的 SELECT 剩下 SELECT;ANDAND → 剩下 AND。
注意:适用于仅替换/删除一次的情况。
3. 内联注释分割
原理:MySQL 中 /!12345SELECT/ 仍会被解析为 SELECT(12345 为版本号,可省略)。
示例:/!UNION/、/!50000SELECT/。
扩展:任意注释内容:SEL/foo/ECT,部分WAF不检测注释内容。
4. 使用等价符号或函数替代
AND/OR:使用 && 替代 AND,|| 替代 OR(需URL编码 %26%26、%7C%7C)。
等号:= 被过滤时使用 LIKE、REGEXP、IN、BETWEEN、<>、>、<。
示例:1 AND 1=1 → 1 && 1 LIKE 1。
5. 换行/回车分割
原理:某些过滤只匹配空格后的关键字,换行符可被忽略。
示例:1%0aAND%0a1=1(%0a是换行)。
6. URL编码/双重编码
原理:WAF只解码一次,后端却解码两次,导致WAF看到编码字符不拦截。
示例:%25%37%33%25%36%35%25%36%63%25%36%35%25%36%33%25%37%34 解码一次为 %73%65%6c%65%63%74,再解码为 select。
7. 使用空字节(仅旧版PHP ) 原理:C语言字符串以空字节 %00 结尾,部分WAF忽略其后内容。
示例:1%00 union select。
二、空格过滤绕过
空格用于分隔SQL语句中的关键字或条件。
1. 替代空白符
可用的空白符(URL编码后):
制表符 %09、换行 %0a、回车 %0d、换页 %0c、垂直制表 %0b。
示例:1%09and%091=1。
2. 使用注释代替空格
内联注释:/注释/ 可当作空格。
示例:1/foo/and/bar/1=1。
3. 使用括号包裹表达式
原理:括号不需要空格,可改变运算顺序。
示例:1 and(1=1)。
*4. 使用特殊符号(MySQL)
+、-、*、/、% 等算术运算符可分割关键字(但需注意优先级)。
示例:1+and+1=1(+ 在数字上下文中可能被当做加法,需谨慎)。
5. 使用反引号(MySQL)
示例:1and1=1(较少用)。
三、引号过滤绕过
当单引号被过滤或转义时,无法闭合字符串。
1. 使用宽字节注入(GBK/GB2312编码)
原理:PHP使用 addslashes 转义单引号(’ -> '),但在GBK编码中,反斜杠 \(%5c)与前面的字符组合成宽字节字符,使得转义失效。
示例:输入 %df%27,经过转义变成 %df%5c%27,%df%5c 被解释为汉字“運”,剩下的 %27 成为独立单引号。
前提:数据库连接编码为GBK,且使用 addslashes(或 magic_quotes_gpc)。
2. 使用十六进制或编码表示字符串
原理:不使用引号表示字符串,直接传递十六进制值。
示例:SELECT * FROM users WHERE name = 0x61646d696e(0x61646d696e 是 admin 的十六进制)。
3. 使用函数返回值代替字符串
示例:CHAR(97,100,109,105,110) 返回 admin。
4. 无引号情况利用(数字型注入)
原理:如果注入点是数字类型,根本不需要引号闭合,直接注入数字即可。
5. 逃逸转义:使用 \ 吃掉后续字符(特殊场景)
如果应用将 ’ 变成 ',输入 \ 可能导致后续字符被转义,但需要具体上下文。
四、注释符过滤绕过
–、#、/* 被过滤时,可使用其他方法闭合语句。
1. 使用 ; 或 %00 截断
示例:1’ union select 1;%00(%00 空字节截断MySQL字符串)。
注意:空字节注入在旧版MySQL有效。
2. 使用 || 或 && 构造闭合
原理:不依赖注释,通过逻辑闭合。例如字符型注入:1’ and ‘1’=‘1,无需注释。
3. 利用数据库特性闭合
MySQL:1’ and ‘1’=‘1’ 最后多出的单引号被 and 后条件匹配掉。
MSSQL:可使用 – 的变体 ;– 等,或利用 %00。
4. 使用括号和函数平衡
如果必须注释掉后面的内容,且 #、– 被过滤,可考虑构造能让剩余语句语法正确的 payload。
五、函数/关键字组合过滤绕过
有时单独过滤 SELECT 或 FROM,但加在一起会拦截。
1. 使用内联版本注释
/!50000SELECT/ 或 /!50000UNION/ 绕过黑名单。
2. 使用等价函数替换
| 过滤的目标 | 替代方案 |
|---|---|
| DATABASE() | SCHEMA()、CURRENT_SCHEMA |
| VERSION() | @@VERSION、@@GLOBAL.VERSION、@@VERSION_COMMENT |
| USER() | CURRENT_USER()、SESSION_USER()、SYSTEM_USER() |
| SLEEP(n) | BENCHMARK(1000000,MD5(‘a’))、HEAVY QUERY |
| BENCHMARK | SLEEP 或 LATENCY(MariaDB) |
| CONCAT() | CONCAT_WS()、GROUP_CONCAT 或 |
| SUBSTR() | MID()、SUBSTRING()、RIGHT()、LEFT() |
| ORD() | ASCII()、CHAR() |
3. 使用报错函数替代联合查询
当 UNION 被严厉过滤时,使用报错注入(extractvalue、updatexml、floor 等)或布尔盲注。
六、逗号过滤绕过
在 UNION SELECT 1,2,3 或 SUBSTR(str,1,1) 中逗号被过滤。
1. 使用 JOIN 绕过 UNION 中的逗号
UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c
等价于 UNION SELECT 1,2,3。
2. 使用 OFFSET 或 FROM … FOR 绕过
LIMIT 1 OFFSET 1 替代 LIMIT 1,1。
3. 使用 CASE WHEN 或 IF 替换截断函数中的逗号
SUBSTR(str FROM 1 FOR 1) -- 替代 SUBSTR(str,1,1)
MID(str FROM 1 FOR 1)
*4. 使用 JOIN 结合 USING 或 ON 绕过(特殊场景)
七、HTTP参数污染(HPP)
原理:WAF只检查同名参数的第一个值,而后端可能取最后一个值(或合并)。
示例:?id=1&id=UNION SELECT。某些WAF检测第一个 id=1 放行,后端获取 id 数组,PHP/Apache 默认取最后一个值,导致注入参数传入。
适用:部分WAF和中间件配置。
八、分块传输绕过(Chunked Transfer Encoding)
原理:将payload分块发送,WAF可能未对分块内容进行完整检测。
实现:使用Burp Suite等工具将POST请求改为分块编码,将恶意SQL语句拆成多块,使WAF看不到完整关键字。
九、数据库特性类绕过
1. MySQL 特殊写法
反引号:SELECTidFROM users(可用于绕过某些正则,将关键字包在反引号内)。
花括号:SELECT {id} FROM users(某些版本支持)。
科学计数法:1e0 代替 1。
2. MSSQL 特性
注释 /* 与 – 但支持 ;–。
变量:DECLARE @q varchar(2000); SET @q=‘SELECT’; EXEC (@q); 可绕过简单过滤。
LIKE 通配符:% 和 _。
3. PostgreSQL 特性
$tag$ 字符串引用:SELECT $tag$admin$tag$ 无引号。
:: 类型转换:‘1’::int。
4. Oracle 特性
字符串连接:‘ad’||‘min’。
CHR():CHR(97)||CHR(100) 构造字符串。
十、综合绕过思路总结
在实际测试中,过滤往往是多层的。推荐步骤如下:
探测过滤规则:逐一输入特殊字符、关键字,观察响应。
尝试最基础的绕过:大小写、双写、URL编码。
使用替代空白符/注释:如果空格被过滤。
使用数据库特有语法:如内联注释、/!50000UNION/。
考虑编码与WAF解析差异:双重编码、分块传输、HPP。
转换注入方式:无法联合查询时转向报错注入、布尔盲注、时间盲注。
终极方法:使用DNSlog外带数据(OOB),彻底绕过回显限制。
附:快速绕过对照表
| 过滤内容 | 常用绕过方法 |
|---|---|
| SELECT | SeLeCt、SELSELECTECT、/!SELECT/ |
| AND/OR | &&、 |
| 空格 | %09、%0a、/**/、+、括号 |
| 单引号 | 宽字节、十六进制、函数、无引号数字型 |
| 注释符 | 利用逻辑闭合、;%00、 |
| 逗号 | JOIN、FROM … FOR、CASE 替代 |
| 等号 | LIKE、REGEXP、IN、<> |
| 函数名 | 等价函数、大小写、内联注释 |
掌握以上技术后,面对绝大多数过滤都能找到绕过路径。实战中需结合具体数据库类型和过滤规则灵活组合使用。