校对依据:直接解包了你桌面上的 turbo-intruder-all .jar,读了它的 META-INF/MANIFEST.MF、内置 examples/*.py,以及 burp/Scripts.class 里内嵌的 Python 运行时(RequestEngine / Engine / queue 的真实签名)。 所以下面的参数名、默认值、脚本写法,跟你手上这个版本是一致的,不是照抄官网新版文档。


0. 先搞清楚你这个 jar

项 值
文件 turbo-intruder-all .jar(39.7 MB,fat jar,内置 Jython 2.7.0)
内部版本 v1.1.0(BurpExtender 里 Loaded Turbo Intruder v1.1.0)
构建时间 2020-08-21
主类 burp.Fast_httpKt
脚本语法 Python 2.7(Jython)—— 没有 f-string,print 用语句写法

判断:能用,但这是 5 年前的版本。当前官方是 2.x,参数更全(autoContentLength、hostHeader、learnCharsetLength、rateLimit 等)、对 HTTP/2 单包攻击支持更完整。 如果你在新版 Burp(JRE 17/21)上加载报错或脚本跑不动,别修这个 jar,直接去 BApp Store 装新版——省时间。

加载

Burp → Extender → Extensions → Add
  Extension type: Java
  Select file: turbo-intruder-all .jar

装好后:任意请求 → 右键 → Extensions → Turbo Intruder(或 Send to Turbo Intruder),弹出攻击窗口。

窗口四块

┌─────────────────────────────────────────────┐
│ Request 模板(用 %s 当占位符)    [Attack]  │
├─────────────────────────────────────────────┤
│ Python 脚本编辑器(queueRequests / handleResponse)│
├─────────────────────────────────────────────┤
│ 结果表:payload | status | length | wordcount | time │
└─────────────────────────────────────────────┘

模板里写 %s 的位置就是载荷插入点;多个 %s 对应 queue() 里传的列表,按顺序填。


1. 最小可用脚本(官方 default.py,已核对)

def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint,
                           concurrentConnections=5,
                           requestsPerConnection=100,
                           pipeline=False
                           )

    for i in range(3, 8):
        engine.queue(target.req, randstr(i), learn=1)
        engine.queue(target.req, target.baseInput, learn=2)

    for word in open('/usr/share/dict/words'):
        engine.queue(target.req, word.rstrip())


def handleResponse(req, interesting):
    if interesting:
        table.add(req)

三件事必须记住:

  1. 只有两个函数会被调用:queueRequests(target, wordlists) 发请求,handleResponse(req, interesting) 处理响应。
  2. learn=1 / learn=2 把响应登记成基线;之后凡与基线不同的响应,interesting=True。这是它比 Burp Intruder 好用的核心。
  3. table.add(req) 才把这条放进结果表;不 add 就是白跑。

2. API 全表(v1.1.0 真实签名)

RequestEngine 构造参数

RequestEngine(endpoint, callback=None, engine=Engine.THREADED,
              concurrentConnections=50, requestsPerConnection=100,
              pipeline=False, maxQueueSize=100, timeout=5,
              maxRetriesPerRequest=3, readCallback=None, readSize=1024,
              resumeSSL=True, autoStart=True)
参数 默认 说明
endpoint — 必填,target.endpoint 或 'https://host:443'
engine THREADED 见下表
concurrentConnections 50 并发连接数。默认模板里写 5 是保守值
requestsPerConnection 100 单连接复用发多少个请求
pipeline False True 走 HTTP/1.1 管道;填数字=每 N 个请求读一次
maxQueueSize 100 未发出请求的队列上限,内存保护
timeout 5 秒,socket 读超时(注意官方新版默认不同)
maxRetriesPerRequest 3 失败重试次数
readCallback / readSize None / 1024 流式读取回调,可用于"读到 token 就发下一条"
autoStart True False 时需手动 engine.start()
resumeSSL True 断点续传 TLS 会话

引擎怎么选(关键判断)

class Engine:
    BURP     = 1   # 走 Burp 自身发包,可见于 Proxy history,慢,但一定能过 Burp 的 HTTP/2 栈
    THREADED = 2   # 线程池 + 原生 socket,默认。快,适合 HTTP/1
    ASYNC    = 3   # 异步原生 socket,HTTP/1
    HTTP2    = 4   # 异步 + HTTP/2 多路复用,竞态/单包测试用它
  • Engine.BURP 有硬限制:requestsPerConnection 被强制为 1、pipeline 关闭、readCallback 不可用(源码里直接 print 提示)。
  • 做竞态选 HTTP2;做纯爆破选 THREADED;要结果落进 Burp Proxy 历史选 BURP。

方法与属性

engine.queue(template, payloads=None, learn=0, callback=None, gate=None, label=None)
engine.openGate(gate)          # 放行被 gate 挂住的请求
engine.start(timeout=5)        # autoStart=False 时手动启动
engine.complete(timeout=-1)    # 等队列跑完再返回,竞态脚本必加
engine.userState               # dict,跨响应共享状态
req.status        # 状态码
req.length        # 响应长度
req.wordcount     # 单词数(比长度更抗随机噪声)
req.time          # 本次请求耗时(秒,浮点)—— 时序攻击靠它
req.response      # 完整原始响应字符串
req.label         # queue 时打的标签,用于分类处理
req.engine        # 反向拿到引擎,访问 userState
req.template      # 原始模板
req.words         # 本次用的载荷列表
req.request       # 实际发出的请求

3. 常用套路(全部来自 jar 内 examples/,可直接抄)

3.1 单参数字典爆破

def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint,
                           concurrentConnections=5,
                           requestsPerConnection=100,
                           pipeline=False)
    for word in open('pass.txt'):
        engine.queue(target.req, word.rstrip())

def handleResponse(req, interesting):
    if req.status != 404:
        table.add(req)

3.2 多参数笛卡尔积(两个 %s)

for u in open('users.txt'):
    for p in open('pass.txt'):
        engine.queue(target.req, [u.rstrip(), p.rstrip()])

3.3 递归目录爆破

def handleResponse(req, interesting):
    if '404 Not Found' not in req.response:
        table.add(req)
        for word in open('dirs.txt'):
            req.engine.queue(req.template, req.words[0] + '/' + word.rstrip())

3.4 多主机同一套字典

req = '''GET / HTTP/1.1
Host: %s
Connection: keep-alive

'''
engines = {}
for domain in open('domains.txt'):
    d = domain.rstrip()
    engines[d] = RequestEngine(endpoint='https://' + d + ':443')

for word in open('words.txt'):
    for d, engine in engines.items():
        engine.queue(req, word.rstrip() + '.' + d)

注意:多主机时统计面板的数字不准,别拿它估算进度。

3.5 限速(防封、防把自家服务打挂)

import time
triedWords = 20        # 每 20 个词
timeSecs   = 5         # 停 5 秒
throttleMillisecs = 200

def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint,
                           concurrentConnections=5,
                           pipeline=False,
                           engine=Engine.BURP)   # BURP 引擎更温和
    n = 0
    for word in open('words.txt'):
        time.sleep(throttleMillisecs / 1000.0)
        engine.queue(target.req, word.rstrip())
        n += 1
        if n == triedWords:
            time.sleep(timeSecs)
            n = 0

3.6 竞态(同时发,是本工具最值钱的用法)

def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint,
                           concurrentConnections=30,
                           requestsPerConnection=100,
                           pipeline=False)

    for i in range(30):
        engine.queue(target.req, target.baseInput, gate='race1')

    engine.openGate('race1')     # 非阻塞,把所有被挂住的请求一起放出去
    engine.complete(timeout=60)  # 等全部回来

def handleResponse(req, interesting):
    table.add(req)
  • gate='race1' 的含义:请求发到最后一个字节之前就挂住,openGate 一喊一起放。这就是"同时到达"的原理。
  • 想真正逼近"单包",把引擎换成 engine=Engine.HTTP2(HTTP/2 多路复用后多请求落进同一个 TCP 包)。v1.1.0 有这个引擎,但不如新版成熟,测不出差异就换新版再试。

3.7 时序盲注 / 时间侧信道

def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint,
                           concurrentConnections=1,
                           requestsPerConnection=100)
    engine.userState['base_times'] = []
    for i in range(20):
        engine.queue(target.req, randstr(i), label='benchmark')
    for username in ['test', 'foo', 'admin']:
        engine.queue(target.req, username)

def handleResponse(req, interesting):
    if req.label == 'benchmark':
        req.engine.userState['base_times'].append(req.time)
    elif req.time > max(req.engine.userState['base_times']) + 10:
        table.add(req)

3.8 边读边发(token 依赖场景)

def queueRequests(target, wordlists):
    global engine
    engine = RequestEngine(endpoint=target.endpoint,
                           concurrentConnections=2,
                           readCallback=handleRead,
                           readSize=256)      # 单次 socket 读的大小
    engine.queue(target.req)

def handleRead(data):                        # data 只是"最后一次读到的内容"
    if 'token' in data:
        engine.queue('something-using-the-token')

BURP 引擎不支持 readCallback,会直接打印提示。

3.9 无限爆破 / 剪贴板词表

for word in wordlists.clipboard:        # 直接吃剪贴板,一行一个
    engine.queue(target.req, word)
for word in wordlists.observedWords:    # 被动扫描期间见过的词
    engine.queue(target.req, word)

seed = 0
while True:                             # a, b, ... aaa, aab 无限生成
    batch = []
    seed = wordlists.bruteforce.generate(seed, 5000, batch)
    for word in batch:
        engine.queue(target.req, word)

4. 结果过滤

def handleResponse(req, interesting):
    if interesting:                      # 与 learn 基线不同
        table.add(req)
    if req.status == 200 and req.length > 5000:
        table.add(req)
    if req.label == 'benchmark':
        pass                             # 基线样本不入表
  • learn=N 是分组基线:多个 learn=1 的响应互相比较,得出"正常长什么样"。
  • 只按 length 判会误报(页面带随机 csrf 值长度会飘),wordcount 更稳。
  • 结果表右键可以看 request/response 原文,也可以导出。

5. 性能调优

目标 配置
单纯要快(HTTP/1) THREADED,concurrentConnections=50,requestsPerConnection=100
HTTP/2 目标 / 竞态 HTTP2,concurrentConnections 按目标承受力,30 起步
不能把目标打挂 降 concurrentConnections 到 2~5,加 3.5 的 sleep 限速
结果要进 Burp 历史 engine=Engine.BURP(并接受单连接限制)
内存吃紧 降 maxQueueSize(默认 100)
目标慢/抖 提高 timeout(默认只有 5 秒!)

6. 坑清单

现象 原因 处理
Loaded ... v1.1.0 后窗口打不开 / 报错 老版本在新 Burp + JRE 17+ 上兼容性差 换 BApp Store 新版
脚本报 SyntaxError 用了 Python 3 语法(f-string、print() 混用) Jython 2.7,用 % 格式化
请求发出去了结果表空 忘了 table.add(req) 补上
interesting 永远 False 没做 learn,或基线样本就在噪声里 先发 3~7 个 randstr(i) + 1 个 baseInput 建基线
一条都没回来 endpoint 与模板里的 Host 不匹配 端口/协议对齐
Engine.BURP 下并发不生效 该引擎强制单连接单请求 换 THREADED
大字典跑到一半停 maxQueueSize 撑满 + 目标超时 提 maxQueueSize 和 timeout
拿到 429 / 被封 并发过高 限速 + 降并发
%s 里的特殊字符没编码 模板替换是原文拼接 自己 urllib.quote()

7. 蓝队视角:这东西在日志里长什么样

Turbo Intruder 的流量跟 Burp Intruder 有明显区别,适合当检测口径:

1. 单 TCP 连接上复用 100 个请求            → requestsPerConnection 的指纹
2. HTTP/1.1 pipelining:一个连接里请求不等响应连发 → 顺序错乱的请求时间戳
3. 同一毫秒内并发命中同一接口,payload 不同   → 竞态攻击特征(正常业务几乎没有)
4. HTTP/2 单连接上瞬间涌出几十个 stream,时间差 < 1ms → 单包攻击特征
5. 请求节奏完全均匀(无人类点击间隔、无静态资源请求,只打同一个接口)
6. UA/Cookie 固定不变,Referer 缺失,只重复单一参数

落地检测建议:

- 按 (源IP, 目标接口) 做 1 秒窗口聚合:请求数 > 30 且参数各不相同 → 告警
- HTTP/2 统计单连接 stream 数:短时间内 > 20 个并发 stream 且路径相同 → 关注
- 登录/优惠券/提现这类"一次性语义"接口,同一账号在同一秒内出现多次成功 → 竞态告警
- 反向踩点:自家系统用这套脚本自测,能打出来的问题在日志里一定也能看见,
  这条链路(自测 → 看日志 → 写规则)比直接抄规则更值钱

边界:这套东西只在授权范围用(靶场、自家资产、有书面授权的项目)。竞态类攻击尤其注意——打 500 次成功 500 次,可能直接把生产数据写乱。


8. 速查:一条命令跑起来

1. Burp 装上 jar → 右键目标请求 → Send to Turbo Intruder
2. 模板把要变的位置改成 %s
3. 粘贴 3.1 或 3.6 的脚本
4. 填并发数、点 Attack
5. 看结果表 → 挑 interesting 的肉眼验证