盲注(Blind SQL Injection)之所以危险,是因为攻击者无法直接在前端回显中看到数据库的报错信息或查询结果。为了在这种“盲视”环境下精准窃取数据,攻击者必须依赖数据库的内置函数将数据“信号”转化为可观测的布尔逻辑(真/假)或时间延迟。理解这些函数的恶意利用方式,是构建有效检测规则的核心。
不同于显错注入,盲注的检测不能仅依赖特征库拦截报错关键词。攻击者构造的Payload往往看起来是合法的SQL语法片段,只是逻辑极其复杂。我们需要深入分析攻击者如何利用字符串截取、数值比较、进制转换和时间控制这四大类函数来逐字符拆解数据,并据此建立基于行为特征的检测模型。
字符串截取与字符比较:盲注的核心探针在布尔盲注中,攻击者的核心目标是将目标数据的一个字符转换为一个真或假的逻辑问题。最经典的利用方式是对
SUBSTRING()、
MID()或
LEFT()/
RIGHT()函数的组合调用。攻击者通常不会直接写
SUBSTRING(password,1,1)='a',因为这种直白的写法容易被静态规则匹配。他们会利用数据库的特性进行变形。
例如,在MySQL中,攻击者偏爱使用
ORD()和
ASCII()函数将截取出的字符转换为数值进行大小比较。Payload形如
ORD(SUBSTRING((SELECT password FROM users LIMIT 1), 1, 1)) > 77。这种二分法查找能极大提高窃取效率。更隐蔽的利用方式是利用
INSTR()或
LOCATE()函数,通过判断子串是否存在来返回布尔值,例如
INSTR((SELECT database()), 'a')。检测这类攻击时,不能仅盯着函数名,要关注函数嵌套的深度和参数来源。当检测引擎发现SQL查询中字符串函数的参数是一个子查询,且外层包裹了比较运算符时,这几乎可以确定为盲注行为。 利用类型转换与隐式转换制造逻辑漏洞
数据库为了处理不同数据类型的比较,存在隐式类型转换机制,这在盲注中常被用来构造零误报的Payload。攻击者利用
CAST()和
CONVERT()函数不仅是为了规避过滤,更是为了制造逻辑判断。例如,在MSSQL中,攻击者可能会尝试将提取出的字符串转换为整数,如果字符串非数字,数据库会抛出错误,但在盲注场景下,攻击者会利用
CASE WHEN配合转换进行探测。
更高级的利用在于字符串拼接与转换的配合。
CONCAT()和
GROUP_CONCAT()本身不是盲注函数,但攻击者会将其与十六进制转换结合。例如,攻击者先通过
HEX()将整个数据转换为十六进制字符串,再通过
SUBSTRING()逐位读取。这种做法的隐蔽性在于,它绕过了针对常规字符ASCII码范围的监控。检测时,如果发现
HEX()、
UNHEX()、
BIN()等函数与截取函数紧密联动,且其参数包含查询表达式,应立即触发告警。此外,要特别注意
COALESCE()和
NULLIF()函数,它们常被用来构造条件分支,通过制造空值或非空值的状态来传递布尔信号。 时间盲注的延时函数与CPU密集型运算
当所有回显通道都被关闭,攻击者转向时间盲注。最直接的检测对象是
SLEEP()(MySQL)、
pg_sleep()(PostgreSQL)、
WAITFOR DELAY(MSSQL)等原生延时函数。然而,直接调用这些函数极易被拦截,因此攻击者发展出了“重型查询”技术。
攻击者会利用数据库内置的复杂数学函数或大表笛卡尔积来制造可控的延迟。例如,在MySQL中,
BENCHMARK()函数被设计用于测试查询性能,攻击者通过
BENCHMARK(5000000, MD5('test'))来消耗CPU时间。在检测层面,必须将BENCHMARK()视为与
SLEEP()同等级别的危险信号。另一种隐蔽手法是利用递归CTE(公共表表达式)或复杂的正则表达式函数。例如,在PostgreSQL中,构造一个恶意的正则表达式匹配
SELECT CASE WHEN (条件) THEN (SELECT repeat('a', 10000000)) ELSE 'a' END,利用CPU运算时间差来判定条件。检测这类攻击需要建立基线,监控数据库执行计划的耗时异常。如果一条原本毫秒级完成的简单查询,突然因为引入了特定的函数或子查询而导致执行时间呈指数级增长,且这种变化与用户输入参数直接相关,这极大概率是时间盲注。
高级编码与加密函数的滥用
为了绕过WAF对敏感关键词(如
password、
admin)的检测,攻击者会利用数据库自带的加密或编码函数在SQL语句内部进行动态解码。例如,在MSSQL中,
fn_sqlvarbasetostr和
fn_varbintohexstr常被用来处理二进制数据。攻击者会将窃取的数据先转换为VARBINARY,再通过字符串函数处理。
MySQL和PostgreSQL中的
TO_BASE64()和
FROM_BASE64()是重灾区。攻击者构造
SELECT FROM_BASE64(SUBSTRING(TO_BASE64((SELECT password FROM users)), 1, 4))。这种利用方式将数据流隐藏在Base64编解码的上下文中,使得基于明文特征的正则表达式完全失效。检测策略需要深入到语义层面,识别出
TO_BASE64的参数是一个子查询,且其返回值被截取函数处理。此外,AES加解密函数
AES_ENCRYPT()和
AES_DECRYPT()也被用于盲注,攻击者通过判断解密是否成功或解密结果的长度来推断数据。对于这类利用,检测的重点应放在加解密函数的参数是否包含动态查询结果上。 基于异常语法树的检测逻辑构建
传统的正则表达式匹配在面对上述高度灵活的函数组合时显得力不从心。有效的检测必须建立在SQL语法树的动态分析之上。我们需要在SQL解析阶段,提取查询的结构化特征。具体而言,要监控以下三种异常模式:
第一,函数嵌套深度异常。正常的业务SQL很少出现超过3层的函数嵌套,而盲注Payload往往嵌套了
MID(ORD(SUBSTRING(...)))这样的四层甚至五层结构。检测引擎应对嵌套深度设定阈值,并对超过阈值的查询进行上下文审查。
第二,布尔逻辑与比较运算的失衡。当检测到
SELECT语句中,
WHERE子句的条件表达式包含大量的
AND、
OR以及数字比较运算(如
> 1、
= 64),且这些比较运算的左值是一个复杂的函数调用结果时,应标记为可疑。
第三,子查询与函数的非法结合。这是最关键的指标。当聚合函数、字符串函数或延时函数的参数是一个独立的
SELECT子查询时,这违反了绝大多数业务代码的编写习惯。例如
LENGTH((SELECT username FROM users WHERE id=1))。建立规则时,应重点审计那些将子查询作为函数输入参数的SQL语句,并阻断这种数据流动。 利用数据库元数据函数进行信息探测
在盲注的前期侦察阶段,攻击者会大量使用元数据函数来确定数据库类型、版本和表结构。虽然这并非直接窃取数据,但它是盲注攻击链的关键一环。对于
@@version、
version()、
DATABASE()、
current_database()等函数,单独出现可能只是健康检查,但如果它们被嵌套在
SUBSTRING()或
ORD()内部,性质就完全变了。
例如,
SELECT SUBSTRING(version(), 1, 1)是典型的版本号逐位探测。更隐蔽的是利用
INFORMATION_SCHEMA的查询。攻击者不会直接
SELECT * FROM INFORMATION_SCHEMA.TABLES,而是通过
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES LIMIT 1 OFFSET N结合字符串函数进行遍历。检测系统需要具备识别“分页遍历系统表”的能力。当检测到对
INFORMATION_SCHEMA或
sys对象的查询中,包含了动态变化的
LIMIT、
OFFSET或
TOP子句,且这些子句的值来自变量或数学运算时,应结合上下文判断是否为自动化攻击工具的行为特征。 利用报错函数进行“半盲注”的变种
虽然标题聚焦盲注,但有一种边界情况常被忽视:攻击者利用那些在特定条件下触发报错的函数,将盲注转化为显错注入。例如,在MySQL中,
EXTRACTVALUE()和
UPDATEXML()函数在处理无效XPath时会产生错误。攻击者构造
EXTRACTVALUE(1, CONCAT(0x7e, (SELECT password FROM users))),如果数据库返回了XPath错误并泄露了拼接的数据,这实际上是一种通过函数触发的“报错注入”。
在盲注检测策略中,必须将这类函数视为高危特征。即使数据库配置了不显示错误,攻击者也可能利用
BIGINT溢出等技术制造条件竞争。检测时,对于
EXTRACTVALUE、
UPDATEXML、
ST_LATFROMGEOHASH等非业务常用且具有复杂解析逻辑的函数,应严格限制其参数中包含子查询或动态拼接的字符串。如果必须允许,应使用参数化查询在驱动层解决,而非在SQL文本中硬编码。
综合来看,攻击者利用数据库函数进行盲注的手法已经从简单的单函数调用演变为多函数联动、编码加密混淆、CPU时间侧信道攻击的复合模式。防御方必须摒弃静态关键词黑名单,转向基于SQL语法树深度解析、函数嵌套深度监控以及子查询数据流向追踪的动态检测模型,才能在盲注攻击发生的第一时间准确识别并阻断威胁。
