ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

BurpSuite Intruder自动化识别绕过动态验证码的表单爆破实战

BurpSuite Intruder自动化识别绕过动态验证码的表单爆破实战

1. 项目概述与场景分析

搞Web渗透测试的人,十有八九会在登录爆破环节碰到验证码这道坎。你以为直接挂上BurpSuite Intruder就能跑字典?天真了。凡是做过几个真实目标的人都知道,正规点的系统早就把验证码校验、登录失败锁定、IP封禁这些基础防御堆满了。标题里面这个事儿——“利用BurpSuite Intruder模块自动化识别并绕过动态验证码进行表单爆破”,拆开来看其实有两层:一层是怎么让Intruder在爆破时绕过或识别验证码,另一层是自动化流程怎么做到不中断、不漏跑、不被封。

我最初做这个方向,是因为某次授权测试里遇到一个系统,密码策略极弱,但登录页加了个4位纯数字的动态验证码,刷新一次变一次。手工测了几个弱口令,效率低到崩溃。后来我把验证码识别模块和Intruder串起来,把爆破流程跑通了,整个过程才算真正有进展。这篇文章就按照我当时踩坑之后整理出来的完整思路来写,从工具原理到实操配置,再到常见问题的排查方法,尽量把能复现的细节都交代清楚。

适合看这篇文章的人有两类:一是正在学BurpSuite的渗透测试新人,看了能理解Intruder的核心机制和验证码绕过的基本套路;二是已经能跑通简单爆破,但被验证码卡住、想提升自动化程度的测试人员。文章里所有操作都基于授权测试环境,目的只有一个——在合规前提下把表单爆破这件老活儿做扎实。

2. 验证码机制拆解与识别方案选型

2.1 动态验证码的常见类型与破解难度分级

验证码设计的本质,是给自动化脚本设一道门槛。但门槛有高有低,实际测试中我按破解难度把常见的动态验证码分了四个等级,这样才好决定下一步用哪种识别方案。

第一级是4位纯数字、背景干净、无干扰线的验证码。这类验证码属于“秒杀”级别,不管是基于传统图像处理还是现在轻量级的深度学习OCR,识别率都能做到90%以上,速度几乎不影响爆破节奏。第二级是字符+数字混合、有一定扭曲但没有复杂背景的验证码,比如某些企业管理系统的登录页就是这种,识别率会掉到70%~85%,需要配合重试机制。第三级是加了噪点、干扰线、字符重叠的验证码,识别难度明显上升,这时候用通用OCR基本不靠谱,得针对目标单独训练或使用打码平台。第四级是行为式验证码,比如滑块、点选、旋转,这类已经不是图像识别能解决的问题了,Intruder直接爆破的路子走不通,通常得专门分析它的交互协议,属于高级对抗,这篇文章就不展开讲。

我建议动手前先在目标登录页刷新十次验证码,截图下来,肉眼判断它属于哪个等级。这个动作看着简单,实际上决定了你后续整个方案的走向:如果是最简单的一级验证码,干脆直接上OCR模型;如果是二级,考虑OCR配合多次重试;如果是三级,评估打码平台成本;如果是四级,就得停下来重新想思路,硬上没意义。

2.2 识别方案对比:OCR、打码平台与手工辅助

验证码识别方案的选择,本质上是“成本”和“识别率”的博弈。我把常用的三种方案列个表放在下面,方便直接对比。

方案类型适用验证码等级单次识别耗时识别率成本自动化程度
本地OCR模型一级、二级0.5~3秒70%~95%无直接费用,需调优高,可内嵌流程
第三方打码平台二级、三级1~5秒85%~98%按次收费,通常几分钱一次高,API调用
手工辅助识别任何等级3~10秒/次100%人力成本低,半自动

从我个人的使用体验来说,如果是做授权渗透测试,目标验证码是一级或二级,我优先用本地OCR,原因有两点:第一,数据不出本机,不会把目标系统的验证码图像送到第三方平台去,避免数据外泄风险;第二,不依赖外部服务,爆破流程不会因为打码平台的接口波动而中断。

但本地OCR也有自己的问题——耗时间。训练一个专用模型或者调优通用模型,起步就得半天。所以如果目标验证码稍微复杂一点,或者测试工期紧,我反而建议直接用打码平台。虽然每张几分钱,但识别率稳定,整体爆破速度反而更快。

至于手工辅助识别,看着有点笨,但在某些极端情况下确实有效。比如某个目标系统的验证码有5000种以上变体,机器识别率怎么调都只有50%,这时候我会挂上半自动模式——Intruder跑到验证码生成的时候,暂停,弹窗给我看验证码,我手动输入,再继续。虽然慢,但不会卡死整个流程。

2.3 OCR识别模块的工程化封装思路

确定了用本地OCR之后,接下来要把识别模块封装成Intruder能用得起来的东西。这里有个关键认知:BurpSuite Intruder没有办法直接加载Python写的OCR脚本,它需要通过自定义扩展或者外部API服务来对接。我选择的方式是写一个本地的HTTP服务,把OCR识别逻辑包在里面,Intruder通过自定义Payload处理器的API调用来获取识别结果。

封装时有一个细节必须注意:验证码图片的预处理直接影响识别率。我踩过的坑是拿原始验证码直接丢给OCR模型,结果识别率只有40%左右。后来加了灰度化、二值化、去噪、字符分割这一整套预处理流程,识别率才拉到了85%以上。如果你打算用深度学习模型,建议不要对单个字符做分割训练,直接把整张验证码喂给模型做端到端识别,现在的主流做法都是后者,抗干扰能力更强。

我把整个OCR服务设计成了三个接口:一个是识别接口,接收验证码图片,返回识别文本;一个是健康检查接口,方便排查服务是否存活;还有一个是重试接口,当识别结果可疑时主动丢弃并请求重新获取验证码。这三个接口看起来简单,但在实际爆破过程中能省掉很多不必要的轮次,尤其是重试接口,直接决定了爆破进程会不会因为一次识别失败就整个卡住。

3. Intruder核心机制与爆破策略设计

3.1 Intruder的四种攻击模式到底怎么选

很多人只知道Intruder有四个模式,但真到了用的时候不知道该选哪个。Sniper适合单参数爆破,比如只爆破密码;Battering Ram适合所有参数用同一个payload,比如某些接口的用户名和密码一样的场景;Pitchfork适合多参数按行对应,比如你有100个用户名和100个密码要一一对应;Cluster Bomb会做笛卡尔积,比如10个用户名和100个密码组合,就会产生1000个请求。

对于标题里这个场景——表单爆破,我的经验是:如果目标是只爆破密码且用户名已知,Sniper模式加一个字典就够;如果用户名未知且密码也未知,用Cluster Bomb组合跑。但要注意,Cluster Bomb的请求量会呈指数级增长,实际测试时必须在Intruder设置里加好请求间隔,否则极其容易触发目标的频率限制。

还有一点容易被忽略:Intruder的Payload Position要标记得准。比如一个登录表单有三个变量——用户名、密码、验证码,而你只打算爆破密码,那就要把Payload Position只标记在密码这个值的位置上,用户名和验证码位置保持固定,否则每个请求都冗余携带大量数据,拖慢整个爆破速度。

3.2 Payload Processing与验证码自动刷新配合

Intruder里有一个叫Payload Processing的功能,很多人没用过,它其实是在每次请求前对payload做一次加工处理。这在验证码爆破场景下极其关键,因为验证码是一次性的,每次请求前都必须先取新的验证码图片,然后调用OCR识别,再把识别结果填入请求参数。

实际操作中,我在Payload Processing里加了一个自定义规则,指向我本地OCR服务的识别接口。具体的执行链条是这样的:Intruder每发起一次请求前,先去目标系统的验证码接口拉取一张新的验证码图片,把图片数据推送给OCR服务,OCR返回识别文本,然后Intruder把这个文本作为验证码参数填充进登录请求。整个过程完全自动,不需要人工介入,只要OCR识别率不掉链子,就可以一直跑下去。

这里要特别提醒一个细节:验证码的获取接口和登录提交接口往往是分离的,而且验证码值通常和Session ID或者Cookie绑定。如果Intruder每次请求都新建会话,就会导致验证码和登录请求不在同一个会话上下文里,后端校验必挂。我的做法是手动设置Cookie和Session保持逻辑,确保每次新请求复用同一个会话,验证码才能对得上号。

3.3 爆破节奏控制:限速、重试与并发

爆破不是越快越好。很多新手一上来就把线程调到50甚至100,结果跑了三分钟目标就把IP封了,前功尽弃。我后来学乖了,凡是真实目标系统的测试,Threads建议设置在5~15这个区间,同时开启Throttle,就是把每次请求间隔设成100到500毫秒。这个节奏下,目标系统的日志里看起来像正常用户的低频尝试,而不是机器在扫。

更关键的是重试机制。验证码识别率再怎么高,总有识别失败的时候。我希望的流程是:如果识别失败,本次请求直接放弃,不提交给目标系统,重新拉取新的验证码再试。这里要用的就是Payload Processing里的自定义处理器,或者干脆在MATCH/REPLACE规则里做二次判断。实际测试下来,识别失败率控制在15%以内时,重试机制对整体爆破速度的影响是可以接受的。

还有一个经验:Intruder的Resource Pool设置里可以控制最大并发数和请求速率,建议在爆破前先估算一下字典大小和单请求耗时。比如字典有5000个密码,单请求耗时约2秒(含验证码识别),那么总耗时就是5000乘以2约等于1.4小时,这样就可以提前规划测试窗口,避免出现跑了一下午却发现字典路径配错了的尴尬。

4. 完整实操:从请求捕获到自动爆破落地

4.1 捕获登录请求与变量定位

动手第一步是用BurpSuite代理抓登录请求。具体操作流程是:浏览器设置代理指向127.0.0.1:8080,目标系统登录页输入一个测试账号和一个测试密码,提交后在BurpSuite的HTTP History里找到这条登录POST请求。右键发送到Repeater先看看结构。

看请求时需要定位这些关键地方:请求头里的Cookie和Session字段、POST参数中用户名字段、密码字段、验证码字段、以及CSRF Token之类的隐藏字段。这里经常有个坑——很多系统登录请求里带了CSRF Token,这个Token如果不在每次爆破前更新,后端会直接拒掉所有请求。碰到这种目标,就得把获取Token的逻辑也一起写进自动化流程里,在Payload Processing里先发一个GET请求去拿Token,再填充到POST请求中。

定位完成后,把请求发送到Intruder,设置Payload Position。以我经常碰到的场景为例,一个POST请求长这样:

POST /login HTTP/1.1 Host: target.example Cookie: JSESSIONID=abc123xyz Content-Type: application/x-www-form-urlencoded username=admin&password=§pass§&captcha=§code§&csrf_token=fix_token_value

密码位置标记为pass,验证码位置标记为code,用户名固定,Token固定。做完这一步,Intruder的请求模板就准备好了。

4.2 Payload配置与OCR服务对接

接下来是配置Payload。我需要在Intruder里添加两个Payload Set:第一个Set用于密码字典,加载本地密码字典文件;第二个Set用于验证码识别结果,类型选择Extension-generated,并指向我写好的自定义扩展。

这里要重点说明自定义扩展的写法。BurpSuite有官方提供的Extender API,支持Java、Python(通过Jython)等方式编写。我用的是Python,核心逻辑就是在调用generateNextPayload时,先去目标验证码接口拉取图片,然后传给本地OCR服务,拿到识别结果后返回给Intruder。另外还得维护一个全局变量,把当前会话的Cookie和验证码状态保存好,防止并发请求时上下文错乱。

OCR服务的响应格式我设计得很简单,JSON结构,包含status和code两个字段。识别成功,status为ok,code返回识别文本;识别失败,status为fail,Intruder扩展会重新拉取验证码进行下一轮尝试。这个重试逻辑尽量放在扩展内部实现,不要在Intruder主流程里频繁重试,否则并发高了反而会拖垮本地OCR服务。

4.3 请求结果判定与命中标记

Intruder跑起来之后,下一步就是在Attack窗口里观察结果,判定哪些请求是爆破成功的。不能光看HTTP状态码——很多系统即使密码错误也返回200,只是页面里有提示文案。所以正确的做法是:先用Repeater手动提交一个肯定错误的密码,记住返回特征;再提交一个正确密码(如果你有测试凭据),对比返回差异。

通常我会用两个特征点来设置匹配规则:一是响应内容长度,密码错误和密码成功时页面长度往往有明显差异;二是关键字,比如“登录成功”在源码里对应的class或提示文本。在Intruder的Options里,设置Grep-Match填入成功后的关键字,比如welcome、dashboard或者具体的成功跳转URL,这样Intruder会在结果列表里标绿,一眼就能筛出成功请求。

跑完一轮之后,建议把所有响应包含成功关键字的条目单独导出来,用Repeater逐一验证,因为偶尔会有误报。比如某些系统返回了200,页面里也确实出现了“welcome”字样,但那可能是因为验证码错误时页面里嵌了静态资源的路径。这个误判问题我遇到过不止一次,所以最终确认一定不能省。

4.4 半自动模式与异常中断恢复

自动模式跑得顺是理想情况,现实中总会遇到一些问题:识别率掉到50%以下、目标开始拒绝特定IP的请求、目标出现人机验证……这时候就得切到半自动模式。我的做法是在自定义扩展里设了一个参数,当连续五次识别失败时,扩展自动暂停Intruder的请求队列,然后弹窗提示处理人员看当前验证码截图并手工输入。这个模式在攻坚阶段特别管用。

爆破中途断掉的情况也要有预案。比如Intruder跑了40%的时候网络断了,或者代理挂了,重新跑一遍整个字典代价太高。我的习惯是跑之前先记下当前字典进度,在扩展里每100个请求写一次日志文件,记录最近完成的下标。断掉之后,从记录的下标处接着跑,不用从头再来。这个事看着不起眼,但在两三小时的爆破任务里能省出大量重复时间。

5. 常见问题与排查技巧实录

5.1 验证码识别正确但登录仍然失败的排查

最让人崩溃的Bug就是验证码明明识别对了,登录请求返回还是“验证码错误”。排查第一步:确认验证码是否与服务端当前会话绑定。很多系统的验证码存在Session里,每次拉取验证码图片后,服务器在Session里存了一份答案。如果Intruder每次请求重新创建了全新的Session,那么带着旧Cookie提交的新验证码,对后端来说就是不存在的值,自然校验失败。

排查方法很直接:在BurpSuite的Repeater里,用同一个Session的Cookie,连续提交两次“先拉验证码再登录”的请求,对比请求头里的Cookie值。如果两次Cookie不一样,就说明会话重置了。解决办法是在扩展里全局维护一个Session,拉验证码和提交登录都用同一个Cookie。

还有一种情况是前端在提交时对验证码做了加密处理。比如登录请求里的验证码字段不是明文,而是经过前端JS加密后的密文。这种隐蔽操作不抓包根本发现不了,手工复制验证码提交时是对的,但脚本里填入的是明文,后端解密失败就会报错。遇到这种情况,解决方案是把加密函数也用Python或Java重写一遍,或者直接在无头浏览器里执行前端JS完成加密,再把结果POST出来。

5.2 OCR识别率剧降的常见原因

OCR识别率突然从85%掉到30%,最多的情况是图片预处理环节出了问题。比如目标系统上线了新验证码样式,加了背景噪点或者改了字体,而预处理代码还停留在老的逻辑。建议每次爆破前都保留现场截图,包括当天的验证码样本和预处理后的图片样本。如果预处理后的图片肉眼都看不清字符,那OCR识别率低就不奇怪了。

另一个容易被忽略的原因是请求头里的Referer或User-Agent影响了验证码图片的生成。有些系统会根据UA判断访问环境,普通脚本的UA会被降级处理,返回低清晰度或大干扰量的验证码图片。解决方法是把浏览器的完整UA复制到BurpSuite的请求模板里,模拟真实浏览器请求验证码图片。

还有一个原因是OCR服务本身发生了内存泄漏或者模型污染。长跑场景下,OCR服务进程的显存或内存占用持续上涨,最终导致推理速度越来越慢,甚至返回空结果。我的做法是在OCR服务里加一个计数器,每处理5000张图片自动重启一次模型加载,清空缓存。这个经验是长期跑自动化爆破后总结出来的,基本能保证识别率不掉。

5.3 目标触发锁定机制后的应对策略

爆破过程中最怕的不是验证码,而是触发账号锁定或IP封禁。目标系统如果设置了连续5次登录失败锁定账号,那你自动化跑两组密码后账号就锁了,密码再正确也登录不上去。面对这种情况,爆破策略必须调整:不爆破固定用户名,改成“每用户只试两个常用密码,轮换大量用户名”,这种喷洒(Password Spraying)思路能有效规避账号锁定。

对于IP封禁,更简单的应对是挂代理池。我在实战中会用多个代理轮流发包,每个代理的请求量控制在触发阈值以内。但注意,并不是所有测试场景都用代理池——加代理会增加网络延迟,降低爆破效率。所以只有在目标明确存在IP封禁机制时才启用,平时做好限速就够了。

还有一种更微妙的情况:目标系统只封特定路径的IP,但静态资源仍然放行。这种时候,爆破请求会返回302跳到验证页面,导致Grep-Match的匹配规则失效。排查思路是看爆破进行中响应长度是否出现了统一的“平线”,如果连续几十个请求都在同一奇怪的长度,大概率是跳转到了人机验证页面。此时应该停下,手工刷新一次浏览器,走完人机验证,拿到新的Cookie再继续。

5.4 常见问题速查表

现象可能原因排查方向解决方案
验证码识别没错但登录失败Session未复用对比Repeater中的Cookie是否一致扩展中全局维护固定Session
请求全返回错误验证码前端对验证码加密后再提交抓包检查登录提交字段是否为明文重写加密函数或调用无头浏览器
OCR识别率骤降目标验证码样式更新或图片预处理失效检查预处理后的图片样本重新调优预处理流程或重新训练模型
目标响应均匀变长触发了人机验证,请求被重定向观察响应长度曲线是否出现平线手工过验证,换新Cookie续跑
账号被锁导致后续爆破无效连续失败触发锁定策略查看系统登录响应是否有锁定提示改为密码喷洒模式,控制每用户尝试次数
OCR服务越来越慢内存泄漏或模型缓存未清监控OCR服务内存占用加入定时模型重载机制

5.5 实操心得:字典质量比工具更影响结果

最后说一个容易被忽视的点:Intruder再强,密码字典不行,爆破结果也白搭。我见过很多人直接把网上流传的弱口令字典拖进去跑几千个,跑完发现目标密码根本没在字典里。我的做法是先用目标公司的信息收集结果定制字典——比如域名、电话区号、年份、员工姓名等方式组合生成一批候选密码,再叠加通用弱口令。这类定制字典往往几十条就能命中,比跑几万行的通用字典高效得多。

组合密码生成这事,BurpSuite的Payload Processing里也支持规则拼接,比如把年份加在常见单词前后。但要注意,Intruder的Payload Processing里做拼接,成本是按组合数算的,很容易膨胀成几十万条。所以我的建议是,不要依赖Intruder做智能生成,而是用自己写的脚本在本地生成好字典文件,再导入Intruder跑。这样既能控制字典规模,还能提前做好去重,跑起来更快。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进