ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

Burp Suite Intruder四种攻击模式全解析:从原理到实战选型

Burp Suite Intruder四种攻击模式全解析:从原理到实战选型

1. 从一次内网渗透说起:为什么非要用Intruder不可

几年前做某家单位的授权测试,对方给了一个内网管理后台,登录接口没有验证码、没有多次失败锁定,但账号密码字典有几千条。手工一个一个试根本不现实,用脚本写循环又要处理Cookie、CSRF Token、请求头过期这些乱七八糟的东西。当时我打开Burp Suite的Intruder模块,把请求包从Repeater转发过去,标记好用户名和密码两个位置,选了Pitchfork模式,挂上字典,几分钟就出了结果。

这个经历想说明一件事:Intruder模块不是Burp Suite里最炫酷的功能,但它绝对是最常用的功能之一。无论你做Web渗透、API测试、还是内部系统的安全评估,只要涉及到"批量修改请求参数并观察响应差异",Intruder就是那个绕不开的入口。它本质上是一个请求变形器——把同一个HTTP请求丢给它,它按照你设置的位置标记和payload字典,一次次重组请求、发送、记录响应,最后把所有结果摆在你面前。

很多人用Intruder只会用默认的Sniper模式,拿一个字典跑一个参数位,跑完就完了。其实这个模块最核心的设计是四种攻击模式,它们分别对应不同的参数组合逻辑。选错模式,要么测不到想要的东西,要么结果里混杂大量无效请求,排查起来非常痛苦。这篇文章就从四种模式的原理说起,用实际测试场景展示每一步怎么配置、怎么判断结果,最后把我在真实项目中踩过的坑一并列出来。

2. 模式背后的核心逻辑:位置标记、payload集合与匹配关系

Intruder的工作流程可以拆成三件事:你先手工发一个基准请求(通常是登录、查询、修改这类操作),然后在请求的任何位置——URL路径、查询参数、Cookie、请求头、POST Body——插入两个§符号,表示"这里是要被替换的变量"。这个变量标记在Intruder里叫position,也就是位置。

接下来,你要给每个位置准备一个payload集合,也就是替换值的来源。这个来源可以是内置字典、文件导入、Burp的扩展生成器,也可以是一个简单的数字范围。

最后是攻击模式的选择。这一步决定了"位置"和"payload"之间如何配对。四种模式的差异集中体现在两个维度:有几个位置能参与替换,以及每个位置各自从哪个payload集合里取值、取多少轮。搞清楚这两个维度,整个Intruder的使用就通了一半。

拿一个简单的登录请求举例:

POST /api/login HTTP/1.1 Host: target.local Content-Type: application/json {"username":"admin","password":"123456"}

把username和password两处标记出来,就得到了两个位置。现在问题来了:你是想用一本用户名字典去试一个固定密码?还是用一对一的用户名密码组合去试?还是要用所有用户名去乘以所有密码,做完整的排列组合?这四种需求恰好对应四种模式。

Sniper模式一次只处理一个位置,所有payload轮流替换该位置,其他位置保持基准值不变。如果标记了多个位置,它会重复这个过程,每个位置依次作为"唯一变量"跑完整个payload集合。比如上面这个登录接口,用户名标记为位置1、密码标记为位置2,payload字典有100条,Sniper会先固定密码为123456,把100个用户名各试一遍;接着固定用户名为admin,把100个密码各试一遍。总请求数等于位置数乘以payload数量。

Battering Ram模式则把所有位置当成一个整体,每个payload值会同时替换所有位置。也就是说,如果payload字典是["a", "b", "c"],第一次请求所有位置都是a,第二次都是b,第三次都是c。它在实战中用得最少,因为"用户名和密码完全一样"这种场景本身就不常见,但在某些API批量测试中——比如多个参数需要保持相同值的签名校验场景——它会派上用场。

Pitchfork模式是多位置、多payload集合的并行配对,类似编程里的zip操作。位置1用自己的字典,位置2用自己的字典,每一轮请求取两个字典的第N条值来替换。如果字典A有1000条、字典B有3条,那总请求数只发3条——短的用完后整个攻击直接结束。这个模式最常见的用法就是"弱口令字典":用户名和密码各自一行,按行对应,像"admin/admin123"、"test/test123"这种。

Cluster Bomb模式是最"暴力"的组合方式,类似笛卡尔积。位置1的每个值都会和位置2的所有值逐一组合。1000个用户名乘以1000个密码,会产生100万个请求。请求量爆炸是它的特点,也是它的风险所在。需要跑接口越权、权限矩阵、多参数爆破时,通常要选它。

下面这张表把四种模式的核心差异列出来,方便对照:

模式位置数量payload集合数量取值逻辑总请求量公式典型场景
Sniper任意,但每轮只动一个单个每个位置依次取完整字典位置数 × 字典大小单参数遍历、用户名枚举
Battering Ram任意,全部同时替换单个同一值替换所有位置字典大小同值参数测试、一次性批量置位
Pitchfork多个每个位置各一个按索引并行取值,取短即停最短字典长度弱口令配对、userid与订单号一对一
Cluster Bomb多个每个位置各一个全排列组合所有字典长度相乘越权组合、多参数排列、矩阵测试

3. 登录爆破与越权测试:实战中怎么决定选哪个模式

这一节直接聊决策。很多初学者拿着模式说明表依然不会选,原因是他们习惯于从"模式"出发而非从"需求"出发。我的经验是:先问自己一句话——这个请求里,哪些参数的值之间是有关联的,哪些是独立的?

有关联,指的是参数A的某个确定值,只应对应参数B的某个或某几个确定值,不能随意交叉。比如一个账号找回密码的接口,需要传username和email,这两个值必须来自同一条用户记录,交叉配对大概率全是无效请求。这时候用Pitchfork就对了。每个位置各挂一个字典,字典按行对齐,一一匹配。

没有关联,或者你故意要测"超出正常关联"的情况,才考虑Cluster Bomb。最典型的是越权测试。某站点查询订单详情:

GET /api/order/detail?orderId=10001&userId=20001

你手上有订单号列表和用户编号列表,目的是验证"用户A能不能通过改orderId查到用户B的订单"。订单号和用户编号在业务上不是一对一的,很多订单归属关系你也不清楚,所以你真正要测的是所有组合里有没有"越权漏洞"。Pitchfork在这里不合适,因为两条字典按行配对可能根本凑不出有效的"订单-用户"组合;Cluster Bomb才能保证每个订单号都遇到过每个用户编号。

再看单参数场景。你在测一个登录接口的用户名枚举漏洞:

POST /api/login HTTP/1.1 Content-Type: application/json {"username":"admin","password":"Welcome@123"}

响应里如果把用户名改成不存在的,返回"用户不存在";改成存在的,返回"密码错误"。这种情况下你只需要跑一个位置,Sniper是唯一的选择。把用户名标记出来,挂一个字典,跑完直接看响应差异。

Battering Ram什么时候用?我印象最深的一个场景是测某个内部API的参数签名。请求里有三个参数都要求等于同一个动态令牌值,正常逻辑是前端把同一个token放在三个字段里提交。我用Battering Ram挂一个token字典,三个位置同时替换,用来验证服务端是否校验了这个令牌的一致性。这种需求用其他模式反而凑不出来。

在实际测试中还有一种混合需求,比如"固定用户名,遍历密码;再做一轮固定密码,遍历用户名"。这个可以直接用Sniper跑两轮,也可以用Cluster Bomb跑一个用户名×密码的小矩阵。区别在于:Sniper的请求数少,适合初步探测;Cluster Bomb会暴露所有组合的响应,适合精细分析。如果目标防护策略严格、请求频率受限,优先用Sniper做粗筛,命中可疑响应后再局部做Cluster Bomb,这是比较稳妥的节奏。

再补充一点决策技巧:先看你要遍历的参数个数,再看参数之间是否按行关联,最后计算请求量级。三步走完,选型基本不会出错。选型正确带来的好处是结果里有效请求占比高、响应对比更清晰,你不需要花大量时间在噪音里捞信号。

4. 四种模式的完整实战拆解:从配置到响应判读

4.1 Sniper实战:跑单点遍历,注意响应长度的微妙差异

某次对一个存在用户资料修改接口的站点做测试,请求长这样:

POST /api/profile/update HTTP/1.1 Host: target.local Content-Type: application/x-www-form-urlencoded Cookie: session=abc123 user_id=10086&nickname=test&gender=1

目标是看user_id参数是否存在水平越权——也就是替换成别人的ID,能不能改别人的资料。我选择Sniper模式,标记user_id一个位置,payload类型选择Numbers,从10001到10150,步长1,总共跑150个请求。线程数设置为1,原因稍后细说。

跑完之后,Intruder的结果列表按请求顺序排列,每一行有状态码、响应长度、响应时间等列。大多数人只看状态码,但这一步正确做法是按响应长度排序,然后再看响应时间。状态码全部200的情况下,响应长度一旦偏离基准值,就说明接口返回的内容和正常情况不一样,那就是线索。

那次测试里,绝大多数响应长度都是532字节,唯独user_id等于10032的时候回包变成了625字节,多了一段"用户资料摘要"。顺着这个ID去查,发现能读到一个不属于当前账号的昵称和手机号。Sniper在这里的价值就是快速遍历一个维度的所有取值,让异常响应自己浮出来。

Sniper实战中要注意一个细节:如果你标记了两个位置,而你的字典有1000条,Sniper会跑2000个请求,但它不是"循环轮替"地跑,而是先跑完位置1的1000个,再跑位置2的1000个。如果你只想随机抽几个位置组合测一下,Sniper并不合适,它的设计理念是穷尽每个位置的可能性,而不是穷尽位置之间的组合。

4.2 Pitchfork实战:弱口令配对要这样挂字典

弱口令测试是Pitchfork的经典应用场景。假设目标登录接口返回结果正常但无验证码,请求体:

{"username":"$username$","password":"$password$"}

配置时位置1选username,location标记在JSON键值处;位置2选password。在Pitchfork模式下,每个位置会对应一个payload列表。你需要准备两份字典,且必须按行对齐——

用户名.txt 密码.txt admin admin123 admin 123456 test test@123 operator operator@2024

第一轮发的是admin / admin123,第二轮是admin / 123456,第三轮是test / test@123。这就是前面说的zip逻辑。很多人在这里犯错,把用户名和密码放进同一份字典,最后请求数量是对的,但组合错位,白白跑了大量无意义请求。

跑完后的判断思路也不同于Sniper。弱口令测试的密码字典通常只有几十条,重点是观察响应摘要。正常失败回包通常是统一的"用户名或密码错误";如果某条请求的响应长度明显不同,或者HTTP状态码变了,比如出现302跳转到首页,说明登录逻辑执行到了更深一层。另外留意响应时间,某些服务端在密码正确时会多查几个表,响应时间相应拉长几十毫秒,这在结果列表里也能看出来。

Pitchfork还有一个用法值得提一下:批量探测"同一用户在不同系统中的密码复用情况"。比如一个企业内部有OA、CRM、邮箱三个系统,用户名相同,密码策略不同,把三份字典按行对齐,一条条测过去,效率比逐个系统单独跑快得多。

4.3 Cluster Bomb实战:越权矩阵测到一半就该收手

Cluster Bomb是我用得最谨慎的一个模式,因为请求量膨胀快,风险也最高。一个典型的越权测试目标:

GET /api/v2/order/info?order_id=10086&sub_site=32

order_id是订单号,sub_site是站点编号。我想看看不同站点之间的订单数据是否隔离,两个参数之间没有关联关系,所以选Cluster Bomb。订单号准备200条,站点编号准备4个,总请求数是800个,不算多,能接受。

配置方法和Pitchfork一样,每个位置挂各自的字典,区别只是Intruder内部会做全排列组合:每个订单号都分别和sub_site=32、33、34、35各组合一次。

跑完之后,我通常会先加一个过滤规则,按响应长度分组。如果某个订单号在sub_site=33时返回了完整订单信息,而返回长度和32号站点明显不同,基本可以确认越权存在。Cluster Bomb的结果量在模式里是最大的,需要靠Length列排序来快速聚焦。

但这里要泼一盆冷水:别指望所有组合都跑完才收手。Cluster Bomb的请求量是几个字典大小相乘,如果你的字典有1000×1000,那就是100万个请求。真实测试中我通常按比例抽测,比如先各取前50条组合出2500个请求,如果看到可疑迹象再扩大范围。这既控制了请求量,也避免目标服务被刷爆,影响业务正常运行。

4.4 Battering Ram实战:同值替换的隐藏用途

Battering Ram在外部测试里出场率低,在内部API联调或逻辑测试时反而常见。有一次我需要验证一个数据导出接口在"客户端标识"和"服务端校验参数"一致时才会放行,不一致就拒绝。请求长这样:

POST /api/export HTTP/1.1 Content-Type: application/json {"client_id":"gateway","request_id":"gateway","access_token":"gateway"}

三个字段在正常请求里都用了同一个值。我标记了三个位置,选Battering Ram,payload直接导入一份字符串字典,包含各种合法值、非法值、空字符串。每轮请求三个字段同时替换成同一个payload值,跑完看哪些值组合被服务端接受、哪些被拒绝,从而推断出校验逻辑是否严格检查了字段之间的相等关系。

如果你用Sniper跑同一个场景,则需要三轮,而且没法复现"三个字段同时相等"这个条件,测出来的结论是片面的。Battering Ram在"多个变量必须同步变化"的场景里是唯一语义正确的选择。正是因为这个限制,它通常配合编程生成的payload字典使用,而不是手工枚举。

5. 自定义payload与处理器:签名、加密、编码这些场景怎么办

真实接口往往不是"明文参数直接替换"这么简单。签名校验、时间戳、Base64编码、URL编码,各种前置处理让Intruder的普通字典派不上用场。这时候要引入两个机制:Payload Processing和Payload Encoding。

Payload Processing通俗讲就是"在payload发送前做一道加工"。Intruder内置了一堆处理规则,可以叠加多层。比如你的密码字段需要先做一次MD5再提交,那就在Payload Processing里先加一条Hash规则,选MD5,再让Burp替换进请求。如果参数需要先Base64编码再做URL编码,就先后加两条对应规则。规则从上到下按顺序执行,这个顺序很容易被忽略,实际踩坑概率极高。

举例:某金融测试环境里日期参数要转成时间戳秒级字符串,我的配置是这样的:

  1. 在位置标记处选中需要替换的原始时间戳;
  2. Payload type选Numbers,范围是1700000000到1700500000,步长60;
  3. Payload Processing里加一条"Convert to Base64"或者什么都不加,因为这个接口直接接受十进制数字字符串;
  4. 如果目标接受的是十六进制,则需要加一条规则把十进制转十六进制,再决定是否补前导零。

那如果是整体请求体需要做签名呢?Burp的Payload Processing处理不了"根据请求其他字段计算签名"这种需求,原因很简单:处理规则只作用于单条payload本身,它不知道请求上下文。这种场景更合适的做法是写一个Burp扩展,或者利用Intruder的Recursive Grep功能配合自定义字典生成。更常见的做法是退一步——用Python脚本或Postman的脚本先预制好一组带签名的完整请求,再导入到Intruder里。

还有一个高频需求是枚举数字型参数,比如用户ID、订单号、验证码。Intruder的Numbers类型直接生成数字序列,可以设定Min、Max、Step。注意Step的默认值是1,如果你要从10000到99999隔10取一个,需要手动把Step改成10,否则生成量会超出预期。加密类参数——比如RSA加密的密码字段——就不要在Intruder里硬拼了,Intruder没法在执行过程中动态导入密钥做非对称加密,这类场景应该用Turbo Intruder配合Python脚本或者写扩展,否则你会绕很多弯路。

6. 跑完一轮只是开始:结果判读、限速防封、线程配置

6.1 响应长度、状态码、时间之外的第四个指标

很多初学者跑完Intruder之后直接看Status列,全部200就觉得没戏。我见过不止一个人因此漏掉漏洞。实际上响应判读要综合几个维度:

  • 状态码:302、403、500这类明显的差异化状态码当然是线索,但也不能完全依赖,因为很多站点的越权接口故意所有请求都返回200,用不同JSON内容来区分。
  • 响应长度(Length):这是最常用的判断维度。Intruder结果列表里点一下Length列头可以排序,长度异常的请求立刻浮到顶部。注意有些HTTP响应里带动态随机内容,比如时间戳或随机token,会导致长度轻微波动,这种情况先观察基准值附近的分布区间,再设阈值。
  • 响应时间:某些接口在数据存在时会多查一次数据库,响应时间多几十毫秒。处理少量请求时可以关注,量一大就不太可靠了,因为网络抖动会掩盖信号。
  • 响应内容关键词:这个最可靠。Intruder支持在结果列表里加过滤条件,也可以把响应包发到Burp的Match and Replace或写一个简单的BApp扩展做关键词高亮。比如登录接口一直返回"用户名或密码错误",某条请求返回"登录成功",哪怕长度差异不明显,关键词过滤也能让你一眼锁定。

我的习惯是:先按Length排序扫一遍,再挑几条长度异常的看响应包原文,最后对可疑请求做一次手工Repeater确认。自动化结果只能作为线索来源,最终判定一定要人工复核,这个原则在任何测试里都不能丢。

6.2 线程数、延时与防护策略:既要速度又要活着出来

Intruder默认的Thread count在旧版本里是5,新版本里可能要自己调。很多人图快,直接拉到20、50,结果要么目标WAF封了你的IP,要么目标服务直接被刷到宕机,测试被迫中断,得不偿失。

建议的做法:

  • 外部目标,有WAF:线程数控制在1~5,每请求之间加200~1000ms延时。Intruder自带的Throttle选项可以设置请求间隔,放在Options > Request Threads里,或者用Payload Processing里的延时规则。
  • 内网目标、授权明确:可以适当提升到10~20,但依然不建议满线程跑。原因不是防封,而是过多的并发请求会让目标的负载均衡或日志系统产生大量告警,影响测试的隐蔽性。
  • 大字典场景:建议先小范围试跑50条,观察目标响应速度和自身网络延迟,再决定最终并发数。

另外,绝对不要在未授权或边界模糊的目标上直接开Cluster Bomb跑大字典。这个模式产生的请求量远超其他模式,一旦目标侧认为这是攻击行为,轻则封IP,重则引起法律纠纷。授权测试也要跟对方确认好压力边界,最好约在低峰期执行。

6.3 四种模式常见的错误配置,写出来给大家避坑

来自真实项目的经验,以下错误我基本都犯过:

Sniper模式标了两个位置结果跑了双倍请求。如果你只是想跑一个位置的枚举,别手滑在另一个位置也打上标记。Sniper会依次处理所有标记位置,即使你只在Payload Configuration里挂了一个字典,它也会拿这个字典分别去跑每个位置。标记要克制的原则,在Intruder里很重要。

Pitchfork的字典行数没对齐。两份字典行数不一致时,短的跑完,整个攻击就停了。你以为跑完了,实际上还有很多组合没测。配置之前先检查行数是否一致,不一致就用脚本补齐或截断。

Cluster Bomb的请求量估算错误。见上面公式:请求量是所有字典大小的乘积。字典大小不是"预估行数",而是真实行数。导入字典后先在Payload列表里看实际总量,再计算总请求数,超预期就果断分流抽测。

Battering Ram试图测多值组合。这个模式每个payload值同时替换所有位置,它测的是"同一个值在不同位置的兼容性",而不是不同值的组合。如果你需要"A位置用x、B位置同时用y"这种非同步的组合,请不要试图用Battering Ram实现。

6.4 配合Burp扩展与脚本,把Intruder变成真正的测试引擎

最后想提一下Intruder的边界。它的核心定位是"批量发送请求并记录响应",在这个边界内它做得很优秀,但一旦需要复杂的关联、条件分支、动态签名,它就不太够用了。

我的实际流程通常是三层配合:

  1. Intruder负责粗扫描:大批量遍历参数、快速定位异常响应;
  2. Repeater负责精细验证:对Intruder筛选出的可疑请求手工修改、比对、确认漏洞;
  3. 脚本或Burp扩展负责复杂逻辑:比如登录后携带动态Token的多步请求、需要临时计算签名的参数,用Python脚本批量生成请求,再用Burp代理转发记录响应。

三层配合下来,Intruder就从一个"单点爆破工具"升级成了整个测试流程的入口引擎,效率和准确率都会提升很多。

在实际项目里,我把Intruder当作一个"请求差异探测器"来用——它不直接给答案,但能让异常从海量正常流量里浮现出来。这个思路比执着于某个具体模式更重要。模式只是工具,能不能在最短时间内定位到有效线索,取决于你对请求模型的理解深度,而不是按钮按得多熟练。多在实际目标上练几次,把每种模式的请求量公式和响应判读方法内化成习惯,后面遇到任何接口,你看到参数结构就能立刻反应过来该开哪个模式、挂什么字典、跑完看哪一列,这才算真正玩明白了。

RELATED READING

延伸阅读

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