1. 项目概述与核心价值
在渗透测试和Web应用安全评估中,登录、注册、找回密码等关键功能点前的验证码,常常是自动化攻击(如暴力破解、撞库)的主要防线。传统的BurpSuite Intruder模块虽然功能强大,但在面对图形验证码时,往往需要手动识别并输入,效率极低,严重制约了测试的深度和广度。这个项目要解决的,正是这个痛点:将高精度的开源验证码识别库ddddocr与BurpSuite的自动化攻击引擎无缝集成,实现验证码的实时、自动识别与绕过,从而让爆破、枚举等测试流程真正实现全自动化。
我之所以花时间折腾这个集成,是因为在实际项目中,遇到过太多“看似坚固”的验证码。有些验证码设计得并不复杂,但手动处理几十上百次请求就足以让人崩溃。而市面上一些现成的Burp插件要么收费,要么识别率感人,要么配置复杂。ddddocr这个基于深度学习的OCR库,在识别常见数字、字母验证码上表现出了惊人的准确率和速度,而且是Python编写、开源免费,这为我们打造一个轻量、高效、可控的自动化解决方案提供了绝佳的基础。这个方案不仅适用于安全测试人员,对于需要进行大量重复请求测试的开发者和QA工程师,同样具有很高的参考价值。
简单来说,这个项目的核心就是:在BurpSuite发起自动化攻击(如Intruder爆破)时,让每一次请求都能自动获取并识别页面中的验证码,然后将识别结果作为请求参数的一部分发送出去,全程无需人工干预。下面,我将从设计思路、环境搭建、核心实现到避坑技巧,完整地拆解这个过程。
2. 整体架构与设计思路拆解
要实现BurpSuite与ddddocr的集成,我们不能指望有一个现成的“一键安装”插件。因为BurpSuite的插件体系(基于Java)和ddddocr(基于Python)处于不同的技术栈。因此,核心思路是构建一个**“桥梁”式**的架构。这个架构通常包含三个部分:
- BurpSuite插件端(Client):负责在BurpSuite中捕获HTTP请求,提取出验证码图片(可能是Base64编码,也可能是图片URL),然后将图片发送给识别服务端,并接收返回的识别结果,最后修改原始请求,填入识别出的验证码。
- 验证码识别服务端(Server):一个独立的、常驻的HTTP/API服务。它接收来自BurpSuite插件端发送的验证码图片数据,调用ddddocr库进行识别,并将识别出的文本结果返回。
- ddddocr识别引擎(Engine):服务的核心,负责实际的图像识别计算。
为什么选择这种CS(客户端-服务器)架构,而不是开发一个纯Java的Burp插件直接集成ddddocr?主要原因有三点:
- 技术栈隔离:ddddocr及其依赖(如ONNX Runtime、Pillow)是Python生态的产物,在Java中直接调用非常复杂且容易出错。通过HTTP API解耦,双方只需遵守简单的数据协议。
- 灵活性与可维护性:识别服务可以独立部署、升级和扩展。例如,你可以将识别服务部署在一台性能更强的机器上,或者未来轻松切换为其他OCR引擎(如PaddleOCR),而无需修改Burp插件。
- 复用性:这个识别服务不仅可以服务于BurpSuite,理论上任何能发送HTTP请求的工具(如Python脚本、Postman集合、其他安全工具)都可以调用它。
在这个项目中,我们将重点使用一个非常成熟且活跃的开源项目captcha-killer-modified作为我们的BurpSuite插件客户端。它原生支持这种代理识别的模式。而服务端,我们将用Python的Flask框架快速搭建一个,专门用于调用ddddocr。
3. 环境准备与工具选型
工欲善其事,必先利其器。下面列出所有必需的软件和库,并解释其作用。
3.1 基础软件安装
- Java环境 (JRE 8+): BurpSuite是基于Java开发的,必须安装Java运行环境。建议安装最新的JRE 8或JDK 11 LTS版本。
- 检查安装:在终端输入
java -version。
- 检查安装:在终端输入
- BurpSuite Professional/Community: 本项目在Community(免费)版上即可运行。建议从PortSwigger官网下载最新版,这是最安全、稳定的来源。
- Python 3.7+: ddddocr需要Python 3.7及以上版本。建议安装Python 3.8或3.9,兼容性最好。
- 检查安装:在终端输入
python --version或python3 --version。
- 检查安装:在终端输入
- 代码编辑器/IDE: 如VS Code、PyCharm,用于编写和调试Python识别服务端代码。
3.2 核心Python库安装
识别服务端依赖于以下几个Python库,通过pip安装:
pip install flask pillow ddddocr requests- Flask: 轻量级Web框架,用于快速搭建提供识别API的HTTP服务。
- Pillow (PIL): Python图像处理库,ddddocr内部会用到,用于加载和处理图片数据。
- ddddocr: 核心OCR库,由深度炼丹炉训练,针对验证码识别优化。
- requests: 可选。用于在服务端代码中测试或进行额外的网络请求(例如,如果验证码是URL,可能需要先下载)。
注意:安装ddddocr时,它会自动安装其深度学习推理后端(如onnxruntime)。如果遇到网络问题,可以考虑使用国内镜像源,例如:
pip install ddddocr -i https://pypi.tuna.tsinghua.edu.cn/simple
3.3 关键BurpSuite插件:Captcha-Killer-Modified
这是整个方案的“大脑”。原版Captcha-Killer已经停止维护,而captcha-killer-modified是其社区维护的增强版,修复了大量问题,并增加了对新版BurpSuite的兼容性。
- 获取方式:在GitHub上搜索
captcha-killer-modified,从其Releases页面下载最新的.jar文件。 - 安装:在BurpSuite中,依次点击
Extender->Extensions->Add,在Extension type选择Java,然后加载下载的jar文件。 - 为什么是它:它提供了一个图形化界面来配置识别接口,支持多种图片格式输入(Raw、Base64、URL),并能无缝集成到Intruder、Repeater等模块中,通过设置
Payload Processing规则自动调用识别服务。
4. 验证码识别服务端(Python + Flask)搭建详解
服务端是整个流程的“计算中心”。它的任务很简单:接收一个包含图片的POST请求,识别,返回文本。我们将创建一个名为ocr_server.py的文件。
4.1 服务端核心代码实现
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ BurpSuite ddddocr 验证码识别服务端 运行:python ocr_server.py 默认监听 http://127.0.0.1:6000 """ import base64 import io import logging from flask import Flask, request, jsonify from PIL import Image import ddddocr # 配置日志,方便调试 logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) app = Flask(__name__) # 全局加载一次识别器,避免每次请求重复加载(耗时) # 注意:ddddocr 第一次初始化可能会稍慢,因为它要加载模型 try: ocr = ddddocr.DdddOcr(show_ad=False) # show_ad=False 关闭广告信息 logger.info("ddddocr 识别器初始化成功!") except Exception as e: logger.error(f"ddddocr 初始化失败: {e}") ocr = None def decode_image(image_data): """通用图片解码函数,处理Base64或二进制数据""" img = None try: # 尝试作为Base64解码 if isinstance(image_data, str) and (image_data.startswith('data:image') or len(image_data) > 200): # 处理可能包含 data:image/png;base64, 前缀的格式 if 'base64,' in image_data: image_data = image_data.split('base64,')[1] img_data = base64.b64decode(image_data) img = Image.open(io.BytesIO(img_data)) else: # 否则视为二进制数据 img = Image.open(io.BytesIO(image_data)) # 统一转换为RGB模式,兼容性更好 if img.mode != 'RGB': img = img.convert('RGB') return img except Exception as e: logger.error(f"图片解码失败: {e}") return None @app.route('/ocr', methods=['POST']) def handle_ocr(): """处理OCR识别请求的主接口""" if ocr is None: return jsonify({'error': 'OCR引擎未初始化'}), 500 result = {'success': False, 'code': '', 'message': ''} try: # 获取请求数据 data = request.get_json() if not data: # 如果JSON解析失败,尝试从form-data或raw body获取 data = request.form.to_dict() if not data and request.data: # 可能是纯二进制图片数据 image_raw = request.data img = decode_image(image_raw) else: image_data = data.get('image') if not image_data: return jsonify({'success': False, 'code': '', 'message': '未找到图片数据(image字段)'}), 400 img = decode_image(image_data) else: # JSON格式请求 image_data = data.get('image') if not image_data: return jsonify({'success': False, 'code': '', 'message': 'JSON中未找到image字段'}), 400 img = decode_image(image_data) if img is None: return jsonify({'success': False, 'code': '', 'message': '图片数据解析失败'}), 400 # 使用ddddocr进行识别 # 注意:ddddocr的classification方法接收的是bytes img_byte_arr = io.BytesIO() img.save(img_byte_arr, format='PNG') img_bytes = img_byte_arr.getvalue() code = ocr.classification(img_bytes) logger.info(f"识别结果: {code}") result['success'] = True result['code'] = code result['message'] = '识别成功' except Exception as e: logger.exception("识别过程发生异常") result['message'] = f'服务器内部错误: {str(e)}' return jsonify(result), 500 return jsonify(result) @app.route('/health', methods=['GET']) def health_check(): """健康检查接口,用于测试服务是否正常""" return jsonify({'status': 'ok', 'engine': 'ddddocr'}) if __name__ == '__main__': # 启动服务,监听本地6000端口,允许远程主机访问(如果需要) # debug=True 仅用于开发,生产环境应设为False app.run(host='0.0.0.0', port=6000, debug=False, threaded=True)4.2 服务端代码关键点解析
- 全局OCR对象:在服务启动时初始化
ddddocr.DdddOcr()对象。这是一个重要的性能优化点。模型加载到内存需要时间和资源,将其设为全局变量,可以让所有后续识别请求共享同一个模型,极大提升响应速度。 - 灵活的图片处理:
decode_image函数尝试处理多种常见的图片输入格式:- 带
data:image/png;base64,前缀的完整Base64字符串。 - 纯Base64字符串。
- 原始的图片二进制数据(Burp插件可能直接发送图片字节流)。 这种鲁棒性设计确保了服务能适应不同插件或工具的调用方式。
- 带
- 统一的RGB转换:PIL库打开的图片可能有多种模式(如RGBA, L等)。将其统一转换为
RGB模式,可以避免因图片格式问题导致的识别错误。 - 错误处理与日志:完善的
try...except块和日志记录,对于调试在BurpSuite中出现的“识别失败”问题至关重要。通过查看服务端日志,能快速定位是图片传输问题、解码问题还是ddddocr自身识别问题。 - 健康检查接口(
/health):这是一个好习惯。在浏览器中访问http://127.0.0.1:6000/health可以快速确认服务是否已成功启动。
4.3 启动与测试服务
在终端中,进入ocr_server.py所在目录,运行:
python ocr_server.py如果看到输出* Running on http://0.0.0.0:6000/和ddddocr 识别器初始化成功!,说明服务启动成功。
你可以使用curl或 Postman 进行快速测试:
# 假设有一张验证码图片 code.png base64_str=$(base64 -i code.png | tr -d '\n') curl -X POST http://127.0.0.1:6000/ocr \ -H "Content-Type: application/json" \ -d "{\"image\": \"$base64_str\"}"如果返回{"code":"5JgX","success":true}之类的JSON,恭喜你,服务端搭建成功。
5. BurpSuite插件配置与联动实战
服务端就绪后,接下来就是在BurpSuite中配置captcha-killer-modified插件,让它与我们的识别服务联动。
5.1 插件界面概览与基本配置
安装好插件后,在BurpSuite顶部标签页会多出一个Captcha Killer。
界面功能区:
- Request:用于配置如何从目标HTTP请求中“获取”验证码图片。
- Image:显示当前获取到的验证码图片。
- Result:显示识别服务返回的结果。
- Configuration:核心配置区,用于设置识别接口(我们的Flask服务)。
配置识别接口:
- 切换到
Configuration标签页。 - 在
Interface下拉菜单旁,点击Add新增一个接口。 - URL:填写我们的服务地址
http://127.0.0.1:6000/ocr。 - Content-Type:选择
application/json。 - Post Data:这里需要配置请求体。我们的服务期望一个JSON,包含
image字段。图片数据需要从HTTP请求中动态提取。配置如下:
这里的{"image": “[pic_base64]”}[pic_base64]是一个占位符,插件会自动用实际获取到的Base64图片数据替换它。 - Result Parsing:这里告诉插件如何从服务返回的JSON中提取识别出的文本。我们的服务返回格式是
{"code": “识别结果”, ...}。因此,配置Prefix为"code": ",Suffix为"。插件会截取这两个字符串之间的内容作为结果。
- 切换到
5.2 实战案例:配置一个登录爆破场景
假设我们目标登录接口为POST /login,请求参数为username=admin&password=123456&captcha=???,验证码图片由GET /captcha.php返回。
步骤一:获取验证码图片请求
- 浏览器访问登录页,BurpSuite开启代理拦截。
- 找到浏览器加载验证码的请求(通常是
GET /captcha.php或类似),将其发送到Captcha Killer插件界面。可以直接在Proxy history里右键该请求,选择Send to Captcha Killer。
步骤二:配置图片获取规则
- 在插件的
Request标签页,你会看到刚才发送的请求。 - 点击
Get按钮,插件会执行这个请求,并在Image区域显示获取到的图片。 - 关键步骤:我们需要告诉插件,后续每次识别时,都要重新执行这个
GET请求来获取新的验证码。在Request区域,确保选中的是我们刚发送的请求,这表示将其作为“图片获取模板”。
步骤三:测试识别接口
- 确保Python服务端正在运行。
- 在
Captcha Killer界面,点击Get获取一张新图片,然后点击识别按钮。 - 观察
Result区域。如果配置正确,这里会显示从我们Flask服务返回的识别结果。 - 重要调试:如果识别失败,查看Python服务端的终端输出日志,这里会有详细的错误信息,是排查问题的关键。
步骤四:集成到Intruder进行爆破这是最终目的。我们想爆破密码,但每次请求都需要新的验证码。
- 拦截一个完整的登录请求(包含用户名、密码和验证码),发送到
Intruder模块。 - 在
Positions标签页,清除所有自动标记,然后手动标记你想要爆破的参数,比如password。 - 最关键的一步:在
Payloads标签页,找到最下方的Payload Processing区域。点击Add->Invoke Burp Extension。 - 在弹出的扩展选择器中,选择
Captcha Killer。 - 这会打开一个配置窗口。你需要在这里配置“如何将识别出的验证码,填入到HTTP请求中”。
- 首先,在
Request部分,你需要指定“从哪里获取验证码图片”。这通常就是我们在Captcha Killer主界面配置好的那个GET /captcha.php请求模板。插件会记住这个配置。 - 然后,在
Response部分,你需要配置“如何从识别结果中提取验证码文本”。这通常就是我们在Configuration里配置的结果解析规则(Prefix/Suffix),插件也会自动应用。 - 最后,在
Update Request部分,你需要指定“将提取到的文本,替换原始请求中的哪个部分”。你需要指定一个参数名(如captcha)或一个自定义的字符串位置(通过Add定义范围)。通常,我们会选择Update parameter,然后输入参数名captcha。
- 首先,在
- 配置完成后,回到Intruder的
Payloads标签页,你会看到添加了一条处理规则。这意味着,Intruder在发送每一个Payload(即每一个密码尝试)之前,都会先执行以下流程: a. 调用Captcha Killer插件。 b. 插件执行配置的GET /captcha.php请求,获取一张全新的验证码图片。 c. 将图片发送到我们的http://127.0.0.1:6000/ocr服务进行识别。 d. 从返回结果中提取验证码文本。 e. 用这个文本替换原始攻击请求中的captcha参数值。 f. 发送修改后的登录请求进行爆破。
至此,一个全自动的、带验证码识别的爆破流程就配置完成了。点击Intruder的Start attack,你将看到请求自动进行,每个请求都使用了不同的验证码。
6. 常见问题、排查技巧与优化实录
在实际集成和使用过程中,你几乎一定会遇到各种问题。下面是我踩过坑后总结的排查清单和优化建议。
6.1 识别服务端常见问题
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 服务启动失败,提示端口占用 | 端口6000被其他程序占用 | netstat -ano | findstr :6000(Win) 或lsof -i:6000(Mac/Linux) 查找并终止占用进程,或修改app.run(port=新的端口)。 |
| 插件点击“识别”后,Result报错或为空 | 1. 服务未启动或网络不通。 2. 接口URL或Post Data配置错误。 3. 图片数据格式插件未正确提取。 | 1. 浏览器访问http://127.0.0.1:6000/health确认服务存活。2. 在插件 Configuration中检查URL和Post Data格式,确保与服务器代码一致。特别注意JSON格式和引号。3. 查看Python服务端日志,看是否收到请求以及错误信息。在插件 Image标签页,尝试将图片以Base64形式复制出来,用curl手动测试接口。 |
| 服务端日志显示图片解码失败 | 插件发送的图片数据格式不符合预期。 | 在Flask服务的handle_ocr函数开头添加logger.info(request.headers)和logger.info(request.data[:200]),打印原始请求信息,查看插件到底发送了什么。根据实际情况调整decode_image函数。 |
| 识别率突然下降 | 1. 验证码类型变化(如中文、算式)。 2. 图片有严重干扰线、扭曲。 3. ddddocr模型对该类型不擅长。 | 1. ddddocr对纯数字字母验证码效果最好。如果是算式验证码,需要先进行图像预处理(如二值化、去噪)或使用其他专门模型。 2. 考虑在服务端添加预处理逻辑(使用Pillow进行灰度化、二值化、降噪)。 3. 可以尝试 ddddocr.DdddOcr(beta=True)使用测试版模型,或寻找更专门的OCR库。 |
6.2 BurpSuite插件配置常见问题
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| Intruder攻击时,每个请求的验证码都一样 | 插件配置的“获取验证码图片”的请求,没有真正被执行,或者该请求本身不返回新图片。 | 1. 在Captcha Killer主界面,确认Request里选中的是获取验证码的请求(如GET /captcha.php),而不是登录请求。2. 单独在 Captcha Killer里多次点击Get,观察图片是否变化。如果不变化,说明目标服务器可能在会话(Session/Cookie)未变时返回相同图片。需要检查获取验证码的请求是否携带了正确的会话标识(如Cookie、Token),并在插件中配置“每次识别时使用当前会话”。 |
| Intruder攻击请求中的验证码字段未被替换 | Payload Processing规则中的“Update Request”配置错误。 | 1. 在Intruder的Payload Processing规则中,双击Captcha Killer规则进行编辑。 2. 在 Update Request部分,确认你选择的是Update parameter并正确填写了参数名(如captcha),且该参数名与原始请求中的参数名完全一致(包括大小写)。3. 可以先用 Repeater模块测试:在Repeater中右键,也有Extensions->Captcha Killer->Send to Intruder with Captcha的选项,从这里生成的Intruder攻击模板,其Payload Processing规则通常是预配好的,成功率更高。 |
| 插件界面点击“识别”正常,但集成到Intruder后失败 | Intruder的Payload Processing是在每个Payload发送前动态执行的,环境可能与插件主界面静态测试时不同。 | 1. 检查Intruder攻击的“Resource Pool”设置,是否限制了线程数或添加了延迟?过快的请求可能导致目标服务器反爬或会话失效。 2. 在 Captcha Killer的Configuration->Request配置中,确保勾选了Use current session或正确配置了会话处理(如自动更新Cookie)。这能保证Intruder在每次获取验证码时,使用的是最新的会话状态。 |
6.3 性能与稳定性优化心得
服务端性能:Flask默认是单线程同步的。当Intruder并发线程数很高时,识别服务可能成为瓶颈。可以通过
app.run(threaded=True)启用多线程,或者使用生产级WSGI服务器如gunicorn来部署服务。pip install gunicorn gunicorn -w 4 -b 0.0.0.0:6000 ocr_server:app-w 4表示启动4个 worker 进程,并发处理能力更强。识别缓存:对于某些系统,同一个会话短时间内验证码可能不变。可以在服务端添加一个简单的缓存(如使用
functools.lru_cache,以图片数据的MD5值为键),避免对完全相同的图片进行重复识别,减少计算开销。错误重试与降级:在Intruder的Payload Processing中,可以配置多条规则。例如,第一条规则调用Captcha Killer,如果识别失败(返回空),则触发第二条规则,使用一个固定的错误值或手动输入。这可以防止因偶发识别失败导致整个攻击停止。
验证码类型判断:更高级的用法是,在服务端对图片进行简单分析(如颜色分布、尺寸、轮廓),先判断验证码类型,再决定调用哪个识别模型(ddddocr用于字符,其他模型用于算式),实现更通用的识别服务。
这个从零搭建的BurpSuite集成ddddocr的方案,虽然需要一些配置步骤,但它给了你完全的控制权和透明度。你清楚地知道图片如何流转、文本如何识别、请求如何被修改。这种掌控感,在面对复杂多变的实际测试环境时,比任何黑盒工具都来得可靠。