1. 项目概述:从手动到自动的验证码对抗演进
在渗透测试和漏洞挖掘的日常工作中,验证码(CAPTCHA)就像一道横亘在自动化工具面前的“叹息之墙”。无论是进行登录爆破、批量注册还是数据枚举,一旦目标系统部署了验证码,传统的自动化脚本就会瞬间哑火,迫使测试者回到手动输入、肉眼识别的原始状态。这不仅效率低下,更让测试过程变得枯燥且不可持续。我经历过无数次在深夜对着屏幕上扭曲的字符,一边揉眼睛一边尝试输入,结果还因为看错而失败,那种挫败感是驱动我寻找自动化解决方案的最初动力。
“告别手动输入”这个标题,精准地戳中了每一位安全从业者的痛点。它指向的不仅仅是一个工具的使用,更是一种工作流的根本性变革。核心思路是利用 BurpSuite 这款渗透测试的“瑞士军刀”,通过一个名为captcha-killer-modified的插件作为桥梁,将验证码图片从请求中提取出来,并调用一个高效、易用的本地识别引擎ddddocr进行自动识别,最后将识别结果无缝填充回请求中,实现全自动的验证码绕过。这套组合拳的精妙之处在于,它巧妙地绕开了云端OCR服务可能存在的延迟、费用和隐私问题,将识别能力完全本地化、离线化,使得整个攻击链可以在内网或任何无外网的环境下高速、稳定地运行。
这套方案适合所有需要进行Web应用安全测试、尤其是涉及身份验证环节测试的安全工程师、渗透测试人员以及红队队员。即使你对BurpSuite插件开发或深度学习OCR原理了解不深也没关系,因为我们将要搭建的,是一个高度集成化、“开箱即用”的解决方案。接下来,我将带你从零开始,完整复现这套自动化验证码识别与爆破的流水线,并分享我在实战中积累的配置技巧和避坑经验。
2. 核心组件选型与原理浅析
2.1 为什么是 BurpSuite + captcha-killer-modified?
BurpSuite 作为渗透测试的事实标准,其强大的可扩展性是其核心优势之一。通过 Extender API,开发者可以为其注入几乎任何想要的功能。captcha-killer-modified正是这种可扩展性的杰出代表。它是原版 captcha-killer 的一个功能增强分支,主要解决了原版对某些新版BurpSuite兼容性不佳、以及接口调用不够灵活的问题。
它的工作原理可以概括为“拦截、中转、回填”:
- 拦截:插件在BurpSuite的代理层或Intruder模块中,监听流经的HTTP/HTTPS请求。当它检测到请求中包含验证码图片(通常是通过
<img>标签的src属性指向一个动态生成的图片URL,或者响应体直接是一张图片),便会将其“捕获”。 - 中转:插件并不自己识别验证码,而是作为一个“调度中心”。它把捕获到的图片数据(通常是Base64编码或图片URL),通过你配置的接口,发送给外部的识别服务。这个服务可以是云端的OCR API,也可以是我们即将搭建的本地 ddddocr 服务。
- 回填:收到识别服务返回的文本结果(即验证码字符)后,插件会按照你预设的规则,将这个结果填充到原始HTTP请求的特定位置。例如,替换掉请求参数中名为
captcha或code的字段值。
注意:captcha-killer-modified 本身不提供识别能力,它只是一个高度可配置的“搬运工”。它的强大之处在于其灵活的接口配置和正则匹配能力,能够适配各种千奇百怪的验证码提交格式。
2.2 ddddocr:轻量级本地OCR神器的崛起
在验证码识别领域,我们有很多选择,从付费的云服务(如阿里云、腾讯云OCR)到开源的Tesseract,再到基于深度学习的各种项目。ddddocr之所以脱颖而出,成为本项目的最佳拍档,主要基于以下几点考量:
- 离线运行,无网络依赖:所有计算都在本地完成,无需向任何第三方发送数据,保证了测试过程的安全性和隐私性,也避免了因网络波动导致的识别失败或延迟。
- 识别率高,泛化能力强:ddddocr 基于深度学习模型训练,对于常见的字符验证码(数字、字母混合)、滑动验证码的缺口识别,甚至点选验证码都有不错的识别效果。其模型在大量数据集上训练过,对于抗扭曲、抗干扰线、背景噪点等常见的验证码干扰手段具有一定的鲁棒性。
- 部署简单,调用方便:它是一个Python库,通过
pip install ddddocr即可安装。它提供了极其简洁的API,通常只需两三行代码就能完成图片的加载和识别,非常适合集成到自动化脚本或作为HTTP服务提供。 - 资源消耗低:相比于一些庞大的深度学习框架,ddddocr 相对轻量,在普通的测试机器上也能快速运行,不会给系统带来过大负担。
其核心原理是卷积神经网络(CNN)。简单来说,你可以把它想象成一个经过大量“看图识字”训练的“大脑”。当你把一张验证码图片输入给它时,这个“大脑”中的多层网络结构会逐层提取图片的特征(如边缘、角点、纹理),最终在输出层给出它认为最可能的字符序列。ddddocr 内置的模型已经封装好了这个过程,我们无需关心内部复杂的矩阵运算,只需享受它带来的便利。
2.3 组合工作流全景图
理解了两个核心组件,整个自动化流程就清晰了:
- BurpSuite Intruder负责发起爆破攻击,生成大量包含验证码请求的Payload。
- captcha-killer-modified 插件在Intruder每次发起请求前(或收到验证码响应时)介入,截获验证码图片。
- 插件将图片发送给我们本地启动的ddddocr HTTP服务。
- ddddocr 服务识别图片,将文本结果返回给插件。
- 插件将识别结果替换到原始请求的对应参数中。
- BurpSuite 发送已被“修正”的完整请求到目标服务器。
- 服务器返回响应,Intruder根据响应判断爆破是否成功。
这个闭环流程,实现了从“手动眼瞅”到“自动识别”的质变。
3. 环境搭建与详细配置步骤
3.1 第一步:搭建本地 ddddocr HTTP 服务
captcha-killer-modified 需要通过HTTP接口与识别服务通信,因此我们需要先将 ddddocr 包装成一个Web服务。
这里我提供一个最常用且稳定的Flask实现方案。首先确保你的环境已安装Python3(建议3.7及以上)。
# 安装必要的库 pip install ddddocr flask接下来,创建一个Python脚本,例如命名为ocr_server.py:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import ddddocr from flask import Flask, request, jsonify import base64 import traceback app = Flask(__name__) ocr = ddddocr.DdddOcr(show_ad=False) # 初始化识别器,show_ad=False关闭广告 @app.route('/ocr', methods=['POST']) def handle_ocr(): """ 处理OCR识别请求。 预期接收JSON格式数据:{'image': 'base64编码的图片数据'} 返回JSON格式:{'code': 状态码, 'message': '信息', 'data': '识别结果'} """ result = {'code': 500, 'message': 'Internal Error', 'data': ''} try: # 1. 获取请求数据 req_data = request.get_json() if not req_data or 'image' not in req_data: result['code'] = 400 result['message'] = 'Bad Request: Missing image field' return jsonify(result), 400 # 2. 解码Base64图片数据 image_b64 = req_data['image'].split(',')[-1] if ',' in req_data['image'] else req_data['image'] image_bytes = base64.b64decode(image_b64) # 3. 调用ddddocr进行识别 # 注意:ddddocr接收的是bytes类型的图片数据 res = ocr.classification(image_bytes) # 4. 返回成功结果 result['code'] = 200 result['message'] = 'OK' result['data'] = res return jsonify(result), 200 except Exception as e: # 5. 异常处理 print(f"OCR识别出错: {e}") print(traceback.format_exc()) result['message'] = f'Server Error: {str(e)}' return jsonify(result), 500 if __name__ == '__main__': # 启动服务,监听本地5000端口 app.run(host='0.0.0.0', port=5000, debug=False) # 生产环境务必设置debug=False关键配置与操作解析:
ocr = ddddocr.DdddOcr(show_ad=False):初始化识别器对象。show_ad参数用于控制是否在控制台打印库的推广信息,设为False让输出更干净。@app.route('/ocr', methods=['POST']):定义了一个路由,意味着我们的服务地址将是http://你的IP:5000/ocr,并且只接受POST请求。- 数据格式:插件默认会以JSON格式发送数据,其中
image字段的值是Base64编码的图片字符串。我们的服务端代码需要正确解码。 app.run(host='0.0.0.0', port=5000):将服务绑定到所有网络接口的5000端口。如果你只在本地BurpSuite使用,host可以设为127.0.0.1。端口可以按需修改。
保存脚本后,在终端运行:
python ocr_server.py如果看到类似* Running on http://0.0.0.0:5000的输出,说明服务已成功启动。你可以用Postman或curl测试一下:
curl -X POST http://127.0.0.1:5000/ocr -H "Content-Type: application/json" -d "{\"image\": \"$(base64 -i 你的验证码图片.jpg)\"}"实操心得一:服务稳定性:在实际爆破过程中,这个OCR服务可能会被调用成千上万次。务必确保脚本的异常处理足够健壮,并且使用
debug=False启动,否则一个错误可能导致整个服务崩溃。可以考虑使用gunicorn等WSGI服务器来提升并发能力和稳定性,对于高强度爆破场景尤为重要。
3.2 第二步:BurpSuite 与 captcha-killer-modified 安装配置
安装BurpSuite:从官网下载专业版或使用社区版。确保版本较新(如2023.x及以上),以保证插件兼容性。
安装插件:
- 下载
captcha-killer-modified的Jar包。通常可以在GitHub等开源平台找到编译好的版本。 - 打开BurpSuite,进入Extender标签页 ->Extensions->Add。
- 在 “Extension type” 下拉框中选择 “Java”,然后点击 “Select file...” 选择你下载的Jar包,最后点击 “Next” 加载。加载成功后,在Extensions列表中应能看到 “Captcha Killer Modified”。
- 下载
配置插件接口:
- 加载插件后,BurpSuite顶部菜单栏或标签页会出现Captcha Killer的标签,点击进入。
- 界面主要分为“识别接口配置”、“请求模板配置”、“结果处理配置”等区域。
- 首先,在识别接口配置部分:
- 接口URL:填写我们刚启动的ddddocr服务地址,即
http://127.0.0.1:5000/ocr。 - 请求方法:选择
POST。 - 请求格式:选择
JSON。 - 请求体:这里需要构造发送给OCR服务的JSON数据。通常使用以下模板:
这里的{"image": "{captcha}"}{captcha}是一个占位符,插件会自动将捕获到的验证码图片Base64编码后替换到这里。
- 接口URL:填写我们刚启动的ddddocr服务地址,即
- 然后,在结果处理配置部分:
- 提取方式:选择
JSON Path。 - 提取表达式:填写
$.data。这是因为我们服务返回的JSON结构是{"code":200, "message":"OK", "data":"识别结果"},使用JSON Path$.data可以精准提取出识别出的字符串。 - 点击旁边的Test按钮,如果下方日志显示成功发送请求并提取到了识别结果,说明接口配置正确。
- 提取方式:选择
实操心得二:接口测试的重要性:在配置完接口后,务必使用“Test”功能。它会用一张内置的测试图片去调用你的OCR服务。这是排查“服务是否启动”、“网络是否通畅”、“数据格式是否正确”等问题的最快方法。如果测试失败,根据日志信息(如连接拒绝、超时、返回格式不符等)逐一排查。
3.3 第三步:实战抓包与插件规则配置
理论配置完成,现在进入实战。我们以一个假设的登录接口为例:POST /login,参数为username、password、captcha,验证码图片由GET /captcha接口返回。
抓取登录流程数据包:
- 浏览器配置代理指向BurpSuite,访问目标登录页面。
- 在BurpSuite的Proxy -> HTTP history中,你会看到两个关键请求:
GET /captcha:响应体是一张验证码图片。POST /login:请求体包含用户名、密码和手动输入的验证码。
将请求发送到插件:
- 在HTTP history中,右键点击
GET /captcha这个请求,选择Send to Captcha Killer。这会将这个请求设置为“验证码获取请求模板”。 - 同样,右键点击
POST /login请求,选择Send to Intruder,准备进行爆破。
- 在HTTP history中,右键点击
在Captcha Killer中配置请求模板:
- 切换到Captcha Killer标签页,你应该能看到刚才发送过来的
GET /captcha请求详情。 - 这里的关键是让插件知道“如何从服务器的响应中拿到验证码图片”。通常有两种情况:
- 情况A:响应体直接是图片二进制流。这是最简单的情况。你需要在插件界面找到“图片提取”相关设置(可能叫“Image Source”或“Response Extraction”),选择“从整个响应体提取”。插件会自动将其转为Base64。
- 情况B:响应是HTML,图片通过
<img src=”…”>引用。这种情况更复杂,需要先发送一个请求获取这个图片URL的内容。captcha-killer-modified 通常能自动处理这种重定向。如果不行,你可能需要手动分析,将最终获取图片的请求设置为模板。
- 配置好后,可以点击“获取验证码”按钮,预览下方是否能正确显示图片,以及右侧的识别结果是否出现。
- 切换到Captcha Killer标签页,你应该能看到刚才发送过来的
在Intruder中配置Payload和插件联动:
- 切换到Intruder标签页,选中我们发送过来的
POST /login请求。 - 在Positions子标签中,清除BurpSuite自动标记的变量,然后手动将
username、password的值标记为攻击变量(如§§)。注意:先不要标记captcha参数! - 进入Payloads子标签,为
username和password设置你的字典。 - 最关键的一步:进入Resource Pool子标签(社区版可能没有,专业版有),或者直接在Intruder菜单中,找到Add Burp Extension Input/Output相关的选项。我们需要在这里引入Captcha Killer作为“Payload Processor”或 “Session Handling Rule”。
- 更通用的方法(推荐):使用Session Handling Rules。在BurpSuite顶部菜单
Project options->Sessions->Session Handling Rules->Add。 - 在新规则中,添加一个
Run a macro动作。 - 创建一个宏(Macro),这个宏应包含两个步骤:1. 执行
GET /captcha请求(即我们发送到Captcha Killer的那个请求)。2. 从该请求的响应中,通过Captcha Killer获取识别结果,并存储在变量中(如captcha_code)。 - 然后,在规则中再添加一个
Update request parameter的动作,将变量captcha_code的值,更新到POST /login请求的captcha参数上。
- 更通用的方法(推荐):使用Session Handling Rules。在BurpSuite顶部菜单
- 这样配置后,Intruder在发送每一次爆破请求前,都会先自动执行这个宏:获取新的验证码 -> 识别 -> 替换到请求中 -> 发送登录请求。实现了全自动化。
- 切换到Intruder标签页,选中我们发送过来的
踩坑记录一:动态Token与Cookie:很多系统的验证码接口会与Session绑定,可能需要携带特定的Cookie(如
JSESSIONID)才能获取到正确的验证码。在配置宏(Macro)时,务必确保GET /captcha请求能继承当前会话的Cookie。在Session Handling Rule的宏编辑器中,通常有“Use cookies from the current session”之类的选项,一定要勾选。否则会导致获取的验证码与登录会话不匹配,永远失败。
4. 高级技巧与深度优化方案
4.1 处理复杂验证码与ddddocr调优
并非所有验证码都能被ddddocr轻松搞定。遇到识别率低的情况,可以尝试以下优化:
图片预处理:ddddocr接收的是原始图片字节。如果验证码背景噪点特别严重,可以在服务端代码中加入预处理环节。例如,使用
PIL(Pillow) 库进行二值化、去噪、裁剪等操作后再送给ddddocr识别。from PIL import Image, ImageFilter import io # 在调用ocr.classification之前 image = Image.open(io.BytesIO(image_bytes)) image = image.convert('L') # 转为灰度图 # 可以添加更多处理,如二值化、降噪等 processed_bytes = io.BytesIO() image.save(processed_bytes, format='PNG') processed_image_bytes = processed_bytes.getvalue() res = ocr.classification(processed_image_bytes)预处理是一把双刃剑,处理得好能大幅提升识别率,处理不当反而会丢失特征。需要根据目标验证码的特点进行针对性调整。
使用ddddocr的专项模型:ddddocr 除了通用识别,还提供了针对滑动验证码缺口识别的
DdddOcr(det=False, ocr=False)模式,以及用于目标检测的模型。如果你的验证码是滑动或点选类型,需要研究使用对应的API。多引擎融合与投票机制:对于特别顽固的验证码,可以部署两个不同的OCR服务(例如ddddocr + 一个本地部署的Tesseract),然后在服务端进行识别,如果两者结果一致则采用,不一致则记录日志或尝试第三次识别(如调用一个付费云API作为仲裁)。这能显著提升整体通过率,但会增加复杂度和耗时。
4.2 提升爆破效率与稳定性
资源池(Resource Pool)与延迟设置:在Intruder的Resource Pool中,可以限制并发线程数。过高的并发会导致:1) 本地OCR服务压力过大,识别错误率上升或崩溃;2) 目标服务器压力过大,触发IP限速或封禁。建议根据目标服务器性能和自身网络情况,从低并发(如2-5个线程)开始测试,逐步增加。同时,可以在请求间添加随机延迟,模拟人工操作。
验证码结果缓存与复用:有些系统的验证码在短时间内可以重复使用。可以在OCR服务端添加一个简单的缓存机制(如使用Python的
lru_cache),对相同的图片Base64哈希值,直接返回缓存结果,避免重复识别,极大提升速度。但要注意验证码的有效期,过期的缓存会导致请求失败。错误重试与熔断机制:在OCR服务端或调用方,增加错误重试逻辑。例如,当识别结果长度明显不符合预期(比如验证码是4位数字,却识别出3个或5个字符),可以自动重试识别一次。当连续失败次数过多时,可以暂时“熔断”,停止发送请求并报警,防止因服务异常导致大量无效请求。
4.3 验证码识别结果的后处理
ddddocr 识别出的结果可能包含空格、换行或难以识别的字符(如0和O,1和l)。我们可以在服务端返回结果前进行后处理:
def post_process(text): # 去除首尾空白字符 text = text.strip() # 替换常见混淆字符 text = text.replace(' ', '').replace('\n', '') # 可以根据经验自定义替换规则,如 '0' -> 'O', '1' -> 'l' 等,但这需要谨慎,最好基于统计 # 如果验证码明确是数字,可以过滤掉字母 # import re # if re.match(r'^\d+$', expected_format): # 假设全是数字 # text = re.sub(r'[^0-9]', '', text) return text将处理后的text再放入返回的JSON中。这能有效清理识别结果,提高爆破成功率。
5. 常见问题排查与实战心得
在实际部署和使用过程中,你几乎一定会遇到下面这些问题。这里我整理了详细的排查清单和解决方案。
5.1 连接失败与识别服务异常
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| Captcha Killer 测试接口时提示“连接失败”或“连接被拒绝” | 1. ddddocr 服务未启动。 2. 防火墙/安全软件阻止了端口。 3. URL或端口填写错误。 | 1. 检查终端,确认python ocr_server.py正在运行且无报错。2. 在命令行执行 curl http://127.0.0.1:5000/ocr(POST请求需用工具测试),看是否能收到响应(预期是405 Method Not Allowed,这至少证明服务可达)。3. 检查BurpSuite中配置的URL是否与脚本中 app.run的host和port一致。如果BurpSuite和脚本不在同一机器,需确保IP地址正确且网络互通。 |
| 测试接口能通,但提示“提取结果失败” | 1. 服务返回的JSON格式与插件中配置的“提取表达式”不匹配。 2. 服务内部出错,返回了非200状态码或错误信息。 | 1. 在Captcha Killer插件界面,仔细查看“日志”或“响应”区域,复制出服务返回的原始数据。用JSON格式化工具查看其结构。确保$.data路径能指向正确的识别结果字段。如果服务返回的是{'result': 'abcd'},则表达式应改为$.result。2. 查看OCR服务端的控制台输出,是否有Python异常抛出。检查图片Base64解码部分是否出错。 |
| 识别结果一直为空或明显错误 | 1. 图片提取位置错误,插件发送的Base64数据并非验证码图片。 2. ddddocr 对该类型验证码识别率低。 3. 验证码图片需要额外的HTTP请求(如带Cookie、Token)才能获取。 | 1. 在Captcha Killer中,使用“获取验证码”功能,并保存图片到本地,用图片查看器打开,确认是否是目标验证码。 2. 尝试用该图片手动调用你的OCR服务,看结果如何。如果本地测试就识别错误,属于ddddocr能力边界问题,考虑上文提到的预处理或换用其他引擎。 3. 检查验证码获取请求(GET /captcha)是否足够“纯净”。有时获取验证码需要先访问一个页面获取动态参数。这时需要配置更复杂的宏,或者使用插件提供的“从先前的请求中获取参数”等功能。 |
5.2 爆破流程失败与结果分析
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| Intruder攻击开始后,所有请求的验证码参数值都相同 | Session Handling Rule或宏配置有误,没有在每次请求前执行获取新验证码的操作。 | 1. 检查Session Handling Rule的触发范围(Scope)是否包含了目标URL。 2. 在Rule中,确保宏被正确添加并启用。可以打开宏的详细配置,查看其步骤。 3. 一个简单的测试方法:在Intruder攻击时,打开Proxy的HTTP history,观察每次爆破请求之前,是否确实有一个新的 GET /captcha请求发出。如果没有,说明宏未生效。 |
| 验证码参数被更新了,但服务器仍然返回“验证码错误” | 1. 验证码一次性使用,但宏的执行导致“获取验证码”和“使用验证码”的请求之间,会话状态发生了变化(如Cookie过期)。 2. 服务器有更复杂的反爬机制,如请求频率限制、验证码与提交参数绑定(hidden token)等。 3. 识别准确率不够高。 | 1. 检查宏的配置,确保GET /captcha和POST /login两个请求使用的是同一个会话上下文(Cookie Jar)。在BurpSuite的宏编辑器中,仔细检查“Update Cookie”等选项。2. 分析登录请求,除了 username,password,captcha,是否还有其他的隐藏字段(如csrf_token,timestamp等)。这些字段可能需要在宏里从上一个响应中提取,并更新到登录请求中。这是一个更高级的Session Handling配置。3. 在Intruder结果中,筛选出响应长度或状态码与众不同的请求,查看其响应体,确认是否是验证码错误。计算一下成功率,如果成功率低于50%,可能需要优化识别引擎或引入重试机制。 |
| 攻击速度非常慢 | 1. OCR服务识别速度是瓶颈。 2. 网络延迟或目标服务器响应慢。 3. Intruder并发设置过低,或请求间延迟过大。 | 1. 监控OCR服务端的CPU/内存使用率。考虑优化代码(如缓存)、升级硬件,或者部署多个OCR服务实例做负载均衡(这需要修改插件调用逻辑,指向一个负载均衡器)。 2. 在Resource Pool中适当增加线程数,并减少请求间隔。但要注意平衡速度和成功率/隐蔽性。 |
5.3 我的核心实战心得
- 先手动,后自动:在配置任何自动化工具之前,务必先用手动方式完整走通一遍流程。用浏览器正常登录一次,并用BurpSuite记录下所有请求。仔细分析每一个请求的参数、Cookie、Header的依赖关系。这能帮你理解Session的维持机制,是后续配置宏和Session Handling Rule的基础。
- 模块化测试:不要试图一步到位。先确保
python ocr_server.py能独立运行并正确识别你手动保存的验证码图片。再确保Captcha Killer插件能通过Test按钮成功调用这个服务。最后再集成到Intruder和Session Handling Rule中。每一步都测试通过,能极大降低排查复杂度。 - 日志是你的朋友:开启BurpSuite的详细日志(Extender -> APIs -> Event listeners),同时保持OCR服务端的控制台输出可见。当出现问题时,对比两边的时间戳和消息,能快速定位问题是出在请求发送、OCR识别还是结果回填环节。
- 尊重目标系统:自动化爆破会产生大量请求。务必在授权测试的范围内进行,并合理控制并发速度和总量,避免对目标业务造成实质影响。在测试环境中充分演练后再应用于生产环境测试。
- 保持更新与学习:captcha-killer-modified 和 ddddocr 都在不断更新。关注它们的GitHub仓库,新的版本可能会修复bug、提升兼容性或增加新功能(如支持更多验证码类型)。同时,验证码技术也在进化,当遇到图形对抗更强的验证码(如行为验证、空间推理)时,这套方案可能失效,需要探索更高级的自动化或半自动化方案。