Skip to content

设计

前言

(本文中提到的目录 fuzz, 目录爆破, url 爆破为同一个意思)

spray 的设计不需要像 gogo 那样需要考虑非常多的取舍问题. spray 的目标只有一个, 解决在目录 fuzz 领域的一切问题.

以 chainreactors 一贯的风格, spray 的参数设计一样的会非常复杂, 但实际上很多参数并非为人类准备, 而是为了成为更大框架的一部分. 大多数人使用 spray 只需要关注一些最基本的操作即可.

目录爆破并非只是批量发包判断状态码, 这个领域还包括了对隐藏目录的发现, 字典的自动化构造, 智能判断有效目录, 爬虫与指纹识别, 与 WAF 的对抗等等

设计

spray 的出发点想要自动化的分辨有效目录与无效目录, 减少对数据进行手动二次分析处理.

开源世界

市面上有很多目录爆破工具或者类似的 httpfuzz 工具.

  • dirsearch python 开发的有性能瓶颈, 有一定的智能判断逻辑, 但还不够智能
  • ffuf 目前 http fuzz 领域的佼佼者, 但缺少对目录爆破的优化, 数据绝大部分情况需要二次处理才可以使用. 与 spray 定位有区别.
  • feroxbuster 目录爆破领域的佼佼者, 与 spray 有着类似的定位. 无法批量爆破, 同样不够智能, 在字典处理上不够自由.
  • gobuster 更接近 go 版本的 dirsearch. 性能上比 dirsearch 更好, 能做的事情与 dirsearch 差不多
  • kiterunner 原本我很看好的项目, 一个结合了目录爆破与 apifuzz 的工具, 但后续没更新, 有不少 bug.
  • httpx 它的设计上是为了从 http 协议中提取信息, 也有人用它来进行目录爆破, 效果上来说并不好, 但它对数据的一些处理方式值得学习

spray 有从上面的这几个工具中学到一些东西, 也深入的分析了这些工具的优缺点, 并进行重新设计. 但请注意 spray 并非是对上面这些工具的重新造轮子, spray 解决了不少这些工具没有考虑过的问题. 会在后续一一介绍.

还有一些更古早, 已经停止维护的项目不再介绍. 也有一些在某个细微领域有突破的工具, 将会在后续介绍.

spray 的总体设计

  • 智能化, 目录爆破中 99.9%的尝试都是无效的, 不能让人把精力放在这些无效的数据处理上. 需要找到一个通用的解决方案智能去除无效设计
  • 适应性, 每个目标都有着自己的规则, 根据目标去获取信息, 构造字典是智能化的基石. 尽可能的做到根据每个目标构造不同的字典.
  • 自动化与分布式, spray 面对的场景包括了大规模高并发的目录探测, 这样的场景并不太适合人工使用 spray, 为此 spray 设计了一些方式来适配这样的场景.
  • 辅助性, spary 并不能做到发现漏洞, 但它将尽可能的帮助人类去分析相关的信息.

spray 基于目录爆破实现, 但目录爆破并不是 spray 的目标.

spray 最终的目标是对单个站点深入的信息分析与提取, 去辅助红队人员进一步的渗透攻击.

智能化

背景

绝大多数目录爆破工具过度简化了现实世界中可能遇到的场景, 导致只能在理想状况下实现过滤无效页面. 当面对复杂的真实场景, 他们的这种方式既会导致误报, 又会导致漏报. 而 spray 要做的是尽可能减少误报与漏报.

这里指的智能化, 实际上是添加复杂的规则, 代替人类进行绝大多数重复无效的劳动. 主要是将我们对目录爆破这个领域的经验转为代码.

spray 的智能判断是我们认为的最佳解决方案, 但不一定是实际上的最佳解决方案. 有可能因为智能判断反而导致了一些漏判误判. 也为此提供了多种补救与缓解方案. 我们也在不断改进我们的逻辑并不断将新的经验沉淀到 spray 的代码中.

在 feroxbuster 与 dirsearch 中, 对有效目录的判断主要通过状态码与 body length. 这样的简单判断在很多场景下是不起作用的. 需要通过手动配置麻烦的filter, 通过人工介入的二次判断. 这种解决方案在单个目标上能通过几次交互修改找到一个特定的配置解决过滤的问题, 操作起来较为麻烦.

如果我们想一次性对多个目标进行扫描, dirsearch 与 feroxbuster 的手动 filter 逻辑将会失效, 不同网站之间完全不能使用同一个 filter 策略. 并且 feroxbuster 采用多进程的方式进行多目标并发, 几乎破坏了它原本非常优雅直观的输出界面. 总而言之, 开源的工具在 filter 上都不够智能.

而 ffuf 与 httpx 更是只用来获取原始数据, 几乎 100%的需要人工介入的方式进行二次数据处理. 它们虽然也提供了各种的 filter 之类的功能, 但在实际使用场景上的定位更接近发包器.

spray 采用的方式是, 获取两个 baseline(index 页面与随机页面)的方式, 动态的根据每个网站自身建立基准值的模型. 然后在后续处理中, 都会根据这两个基准值进行判断. 基本上解决了 90%的场景.

当然现实世界中不会如此简单, 只需要两个 baseline 不可能解决所有的问题, 为了解决后面的 10%的问题以及为了性能上的优化, spray 并设计了一个三段式的过滤逻辑, 每个阶段中都有大量的判断逻辑进行尽可能的智能判断. 具体的逻辑可以文档中的细节看到.

当然三个三段式的逻辑加上 baseline 解决了 99%的问题, 但为了最后的 1%, 设计的 spray 真正最复杂功能.

这些功能包括

  1. 通过模糊 hash, 对网页相似度进行对比
  2. 通过模糊状态码进行更进一步的动态 baseline. 即模糊状态码列表中的结果会与相同状态的 baseline 进行对比
  3. 手动维护的智能过滤状态码列表, 一定程度调整三段式逻辑
  4. --match--filter的表达式语言进行手动的规则配置

通过这几个对三段式智能过滤的补充, 我们才确定 spray 有了应对 100%场景的能力 (欢迎找一些奇葩网站给我出难题^ ^).

当使用到 3 和 4 这两个高级功能时, 实际上 spray 就遇到了和 feroxbuster 与 dirsearch 类似的困境, 需要一定程度的人工手动配置了. 当然也不是没有解决办法, 只不过解决方案在 spray 之外, 需要引入更加庞大的分析引擎去实现. 这也是 spray 作为工件的意义所在.

而通过 baseline, 三段式过滤, 以及补充功能的 1 与 2. 在非人工干预的情况下, spray 能覆盖到 99.9%的场景. spray 已经做到了对同类工具的跨越式突破.

无效的 200 页面

这个相对比较好理解, 例如实际上的 404 页面返回 200 状态码. 例如泛解析的目录, 所有页面都返回了 200 状态码.

是在实战中比较多见的情况, 可能针对单一情况其他工具能进行手动配置的过滤, 但是如果一个站有好几种不同的 200 状态码, 过滤起来还是比较麻烦的.

在单一 200 页面的情况下, 假如 200 页面是动态的, spray 也能完美的进行过滤. 但是对于多个泛解析目录同时存在, 且返回的页面都不同. spray 也只能通过手动配置去解决. 相对比其他工具的手足无措已经有了很大进步.

有效的 404 页面

这个情况知道的人可能少一些. 其实也和工具有关, 使用市面上的所有目录爆破工具去 fuzz. 遇到 404 状态码, 都会选择过滤, 使用者根本无法判断有没有可能存在有效的 404 页面. 在真实世界中, 这样的情况在现代化的网站中并不少见.

举个例子, 某个网站给/api配置了泛解析, 但除非访问到/api/getUser是返回 200, 其他页面均为404.

这种情况, 这个/api的路由实际上是有效的, 可以构造字典进行 fuzz, 或者寻找 api 文档进行深度测试.

大部分工具只有/api这个路由被配置为 403 或者 200 的时候才有可能发现. 但在真实世界中, api 通常会被重写其所有的输出, 以保证统一格式为 json 之类的结构化数据. 状态码不再是 http 状态码, 而是 json 中的一个字段.

还有一种情况, 在反代的场景中. 随机目录的 404 页面是由反代服务器返回的. 但当命中的反代的配置, 流量打到某个业务上时, 如果业务的这个目录恰巧返回的时 404, 那么这个 404 页面也是有效的, 而且通常是高价值的.

刚才也提到了, 之所以这种情况少见不是因为它真的少见, 而是所有工具都会忽略这种情况.

在 spray 中, 404 页面将被认为是需要模糊对比的页面. 如果发现的 404 页面与 random baseline 中的页面完全不同, 这个时候就会将其作为有效结果输出.

403/500/503 页面的细节

这三个状态码是很常见的用来处理一些未授权/错误页面的状态码. 它们不同于 404 页面的只可能由中间件与业务提供, 这三个状态码可能是中间件|业务|框架|CDN|WAF. 造成的原因也多种多样, 可能是触发了 WAF 告警, 可能是中间件的统一配置, 可能是业务的错误处理页面, 可能是 Cookie 权限不够, 可能是框架的一些配置.

很多时候会发现*.do或者/cgi-bin/或者*/etc/password/*这样的路由都会发现通配符的这个页面. 通配符不再是单一的目录之后的场景, 而是可能在任意位置出现.

面对这种情况, 其他工具能做的也只有手动配置. 如果同时出现了上述多种情况, 手动配置就无能为力了, 只能首尾不顾, 或者选择使用 ffuf 配上一个脚本手动处理数据.

而 spray 实际上能做的也不多, 如果这些特例恰巧对应的不同的状态码, spray 是能通过基于状态码的动态 baseline 去智能过滤的. 但如果这个站的所有特例都是 403 状态码. spray 也只能手动编写一个复杂的表达式去处理. 好在 spray 也不是无能为力.

隐患

对 spray 来说, 智能过滤是最具代表性的智能化. 实际上, spray 在很多其他细节上也有类似的智能设计. 但这是存在隐患的, 过度的智能导致某些东西被隐含了, 除非是这个领域的专家, 否则可能用了几年都理解不了其中的细节.

性能

背景

这个领域的性能不能通过一味的提高并发解决. 甚至可以说, 只提高并发数, 反而会导致性能降低. 现代的这类工具通过 keep-alive 解决了一部分问题, 但做法未免有些粗糙, 并没有最大程度的释放性能. 而且性能也是由多方面决定, 需要在不同的场景下选择不同的方案, 最终达成极限但又安全的性能释放.

目录爆破领域的唯一性能指标是有效并发数量. 也就是一定时间内能交互多少包.

但这个指标实际上受到 client, http 协议, 目标状态影响. 要将其优化到极限, 需要从多方面入手.

client

这里的 client 指的是发包的工具, 在 python 中, 通常是 urllib 或 request 两个库. 在 go 中, 大多数工具选择了net/http.

而 spray 则是同时使用了net/httpfasthttp, 原本计划使用全部的 fasthttp, 但这个库在 host 爆破时存在一些问题, 所以代码上封装了一层, 去自动的选择 client.

当然语言本身也会带来一定程度的影响, go 的并发调度性能比 python 强上一个数量级.

选择 fasthttp 原因是, fasthttp 通过各种手段优化其代码性能. 在低负载场景, 可能感受不到, 因为这时候主要的瓶颈是目标的接受能力, 但当开始多目标批量爆破时, 这个影响会越来越大. 最终在本地的理论极限测试下, fasthttp 会比 net/http 有 2-3 倍的性能提升. 这种几乎翻倍的性能最终让我接受了一些 fasthttp 的坑点, 使用了 fasthttp 作为默认的 client.

使用 fasthttp 坑点

它复用了几乎所有的 bytes buf, 因此 releaseResponse 之后, 可能 buf 会被复用, 导致 buf 的数据被覆盖. 这个 bug 我花费了好几天时间才定位到. 最新版本已经不存在这个 bug 了

HTTP

在大多数场景下, http 协议对性能的影响最关键的是keep-alive, 这个功能需要 http1.1 之后才支持. 好在绝大多数服务现在都已经支持了. 这也是 spray 性能提升最大因素.

keep-alive 在 http1.0 中是需要手动打开, 在 http1.1 中是自动打开, 在 http2 中则变成了多路复用, 性能有数量级上的提升.

对于代码来说, keep-alive 的影响就非常大了. 在 python 中, 需要手动控制 keep-alive 的并发池去复用. 但好在 go 中不论是fasthttp还是net/http都内置自带了链接的复用. 如果手段去维护这个链接池不仅带代码水平有一定要求, 实际效果也会远差于官方提供的. 这也是 dirsearch, dirmap 性能有数量级的代差的原因.

并且 spray 通过 1.5 倍 thread 的并发池的冗余, 解决了部分链接失效的导致 keep-alive 中断的问题.

在未来, spray 还将支持 http2, http3 的 client, 去实现最大的性能的并发.

并发数

spray 每个目标都将维护一个自身的链接池, 可以通过-t参数控制每个目标的并发数, 因为 keep-alive 的影响, 实际上只需要很少的 thread, 既可实现非常高的 QPS.

spray 默认的 thread 是 20, 在爆破 nginx 时, 能打到 1500/s 的速度. 这个速度大多数场景下已经够用了. 如果需要跳转 thread 也请注意, 不要开得太高, 这并没有意义. 超过 100 个 thread 的配置, 绝大多数情况都不会生产实际作用, 这个时候的性能瓶颈时自己/目标的带宽或者服务本身的性能上限.

spray 在爆破反向代理中间件(nginx, f5, Lighttpd)时, 能实现最大理论性能

如果目标是 tomcat 之类的中间件, 请求可能会直接打到业务上, 而业务的性能是远远远低于那些反代中间件的. 这个时候就有可能导致 CC 攻击造成 DOS.

Danger

请根据实际的情况选择并发. 请根据实际的情况选择并发. 请根据实际的情况选择并发.

错误处理

上述这些措施下的带来的性能提升比开一个非常高的线程数的效果要好的多, 能在相同 thread 实现数百倍的提升, 客户端与服务器的负载也会低得多. 但错误总是让人意想不到.

目标很可能部署了 WAF, 或者 CDN 厂商的各种限制 导致任务执行到一半就被拉黑, 或者被 banIP. 这种情况下后续的扫描都是没有意义, 继续执行全部的扫描任务反而会导致无法判断哪些效任务扫描. 这个时候, 就需要及时的退出并保证保存有效数据.

这一点非常关键, 是后续分布式扫描实现的基础. 对于反代的目录爆破来说, 选择分布式不是为了更快的完成任务, 而是绕过目标的种种限制.

在 spray 中, 进行了多种方式的判断. 将会通过一定间隔的 check 包, 去判断 random 与 index 是否已经发生偏离. 如果是被 banIP, 这种情况简单的多, 通过设置错误阈值就可以解决.

但真实场景中, 错误可能是偶发性的, 比如某个字典命中了 WAF, 导致单个包出现了 WAF, 但后续包并没有再次触发. 所以如果每个正常的结果都会将错误累计清空, 只有连续发生的大量基准值偏差或者请求错误, 才会退出程序. 并通过记录每个错误数据, 来找到错误最初发生的位置.

后续的任务只需要换个节点, 接着上次的任务扫描即可.

错误处理还包括了一些其他的特殊情况, 例如

  • body 超过 spray 默认的限制(默认为 100k), 导致部分功能无法正常生效, 通过添加参数--read-all解决
  • 目标使用了 chunked, 导致 fasthttp 无法解码. 通过添加-c standard切换 client 解决.

字典构造

背景

绝大部分工具将字典生成与爆破的行为分开了, 造成了一定程度的割裂. 很多人会发现有工具没合适的字典, 或者想拿一个字典批量爆破一批目标却找不到合适的工具. 我也正是因为遇到了这样的困境才选择去实现一个新的工具. 因此 spray 从设计上就考虑到了这种割裂, 并尝试解决这种割裂.

spray 与其他目录爆破最大的不同还在于 spray 将字典构造引入到了目录爆破的过程中.

在传统的解决方案之下, 工具和字典是分离的(或许有些工具提供了一些简单的替换, 例如替换 host, 替换 ext 之类的功能, 但在实际流程上还是脱节的), 需要提前准备一个覆盖足够广的字典, 然后读取字典进行高并发的扫描.

字典生成器

但在 spray 中, 提供了类似 hashcat 中的基于掩码生成器基于规则生成器, 以及一些函数装饰器对字典进行修饰, 可以在没准备字典的情况下, 对某些场景进行爆破.

举个例子, 不少大型机构, 会使用一台 nginx 对内网的多个服务进行反代, 而这样的反代绝大多数情况都是通过一个较短的 path. 例如plms这样的缩写. 使用 spray 的掩码生成器, 生成一个四位的随机目录字典, 或者可以带上目标的一些关键字进行更复杂的掩码字典生成结合规则字典生成.

递归

在现代化的网站中, 递归实际上效果并不好, spray 并不鼓励使用递归.

在传统的目录爆破工具中, 通常包含一个递归配置, 将爆破到的所有目录都在进行一次全量的字典爆破.

但 spray 中提供了另一种选择, 可以通过类似--recu current.IsDir() && current.Status == 200配置自定义的递归规则, 进行相对高效的递归.

虽然 spray 提供了相对高效的递归方式, 但还是不建议使用递归.

轻量级的递归

在 spray 中, 更优雅的解决方案是基于规则的进一步字典生成.

提供了--append-rule, 可以根据有效目录进行额外的字典生成. 这个是在爆破到某些目录之后, 进行进一步的字典组合.

例如, 想要扫描有效文件的备份, 当扫到了index.php的时候, 自动根据指定的规则, 生成如index.php~这样的字典, 进行爆破.

信息提取

背景

应该说 100%的同类工具都只考虑 http 本身带来的信息, 例如状态码, body 长度. 从没有考虑 http 内包含的更多信息, 例如指纹, title, 敏感信息. 在目录爆破时, 是会获取这些数据的, 但会被直接抛弃. 而 spray 不仅会收集这些被其他工具抛弃的数据, 还会尽可能从中榨取所有有价值的数据.

spray 的输出结果更像是 httpx 与 ffuf 那样, 不仅仅只有目录. 有不少目录爆破工具在输出结果时, 抛弃了所有中间输出, 导致对其二次处理还需要 httpx 这样的工具配合,再次获取信息. 会额外发送不少请求包.

spray 的文件输出结果中, 包含了完整的请求摘要信息.

在目录爆破时, spray 还将对 title, 指纹进行识别, 并提供了--extract自定义正则从网页中提取数据. 也包含了一个简易的爬虫.

在没有启动附加功能时, spray 会获取的信息有:

  • http 基本信息, 状态码, content-type, body-length, redirect,请求耗时等
  • html 的 title
  • 被动的指纹识别
  • body 与 header 的 hash
  • spray 提供的一些额外属性, 例如被过滤原因, 目录来源, 是否有效, 是否为模糊有效

爬虫

爬虫的逻辑类似jsfinder, 从网页中提取可能的 url, 并进行递归的爬虫处理.

这个爬虫的表现远低于 headless 爬虫, 如果有更高级的需要, 还是建议使用katana , rad,crawlgo 这样的 headless 爬虫工具.

但相比于原版的 jsfinder 中的逻辑, spray 进行了大量优化和调整, 以 jsfinder README.md 中的https://www.mi.com为例, 有数百倍的差距(还是在额外限制了 scope 的情况下). 并能实现对很多 jsfinder 无法正确识别的链接进行识别.

当这个爬虫与智能过滤结合到一起, 就自然而然的实现了对这些有效目录的验证.

经过一段时间的实践, 我发现爬虫配合上 spray 的信息收集能力, 能创造出很多惊喜. 为此. spray 进行了一些微小的调整.

  • 通过爬虫进行的扫描将跳过 precompare 的智能过滤. 因为爬虫获取的目录本就是相对高价值的目录, 就算是 404 页面也不一定是无效结果. 现在爬虫将拥有更高的优先级
  • spray 当前只关注目录/文件, 因此爬虫爬到的参数与 frag 将会被自动删除.
  • 爬虫配合--extract进行信息提取能拿到很多有趣的数据
  • 爬虫配合递归加上备份文件与通用文件的扫描, 也能发现很多有趣的新结果
  • 爬虫让 favicon hash 荣获新生. 非默认的(/favicon.ico)的 ico 也能准确识别出指纹了.

就算是简易的爬虫. 实现起来也较为复杂, spray 目前内置的爬虫也不是最优的配置. 几乎每个版本都要对爬虫功能进行大量微调. 这个 spray 中的爬虫还有很大的潜力.

爬虫未来提升的方向:

  • 当前爬虫被强制指定为当前 host, 但我发现很多网站会在第三方 cdn 上托管自己的 js, 而这些 js 中有可能包含敏感数据. 因此后续将会对 scope 做智能优化, 以及提供对应的参数.
  • 根据爬虫的结果去构造字典

指纹

目前, 除了 httpx 提供了 wappalyzer 的规则库, 其他工具都没有实现类似的功能.

而 wappalyzer 关注的主要是网站的组件, 例如使用了什么语言, 引入了什么组件. 它的关注点与渗透测试场景实际上并不一致. 在红队/渗透测试场景中, 更需要的是类似 whatweb 那样的指纹识别, 供应商识别.

而 spray 与 gogo 共享了指纹库, 实现了对数千条红队可能会关注的指纹的识别.

并且同样引入了主动指纹识别, 根据配置生成可能存在关键信息的目录进行爆破. 这里的逻辑与 gogo 中的主动指纹识别有细微的不同. spray 会将需要主动探测的目录生成一个字典, 并进行全量的规则匹配. 而非 gogo 那样的精准匹配. 因此实际上 spray 在配置了相同的指纹的情况下, 会比 gogo 有些许提升(可能只能提升 1%不到, 某些情况下能带来惊喜)

spray 没有 gogo 那么多顾虑, 后续也可能会考虑将 wappalyzer 接入, 提供更丰富的信息.

也因此, 在配置了同样指纹的情况下, spray 的效果会远好于 gogo, 有好几个方面综合达成了这一点.

  • spray 中内置了爬虫, 爬虫配合上被动指纹识别, 能识别到很多之前识别不到的指纹, 例如通过 js 跳转的页面, 在 gogo 中只能通过识别跳转的 url 中的特征, 而 spray 通过爬虫可以获取到跳转后的页面,

  • 也因为爬虫可以获取到所有的 ico 路径, spray 添加了基于 content-type 的智能判断, 可以将 ico hash 发挥到极致. 很多非/favicon.ico的 ico 也能正常识别

  • spray 不会像 gogo 一样将 host 解析为 ip, 因此配合上 host 爆破, 或者面对 cdn 常见, 指纹识别也能正常发挥作用.

  • spray 手动处理 redirect. 在 gogo 中并没有将指纹识别作用到 redirect 的每一次跳转, 而 spray 因为手动处理了所有 redirect 请求, 因此一些非常特殊的网站也能正常识别. 这一点曾经在好几次项目中发现了非常隐蔽且关键的指纹. 并且能获取每次 redirect 的基本信息.

正因为爬虫与被动指纹识别的结合, spray 实际上还有用更加强大的指纹识别能力. 就算网站修改了首页的样式, 在 JS 文件中的特征依旧不能改变.

gogo 原本的指纹库是为了内网场景做了不少取舍, 而现在这些舍弃的东西都可以加回来. 后续将会给 spray 提供更加强大的指纹库.

不得不说, 当初为 gogo 设计的最小化匹配的指纹库并不完全适应 spray, 存在一定的误报, 还需要后续优化指纹库.

其他附加信息收集手法

除了已经提到过的爬虫和主动指纹识别之外, spray 目前还提供了多种方式进行收集. 当然手法不止 spray 提供的这些, 如果有 spray 还不支持的类似的功能, 欢迎提供 issue.

这些功能都需要添加额外的参数或者-a打开. 因此称之为additional信息收集手法.

  • --bak , 备份文件的爆破, 逻辑类似 fuzzuli 中的基于 host 的字典生成+通用的常见备份文件目录. 考虑到 url 请求可能会将备份文件下载到内存中, 造成溢出. 默认的 body 上限为 100k. 超过 100k 的文件将不会继续读取, 而是放弃这个握手, 重新建立连接(keep-alive 如果不读完 body, 将无法复用链接)

  • --bak-file , 对于单个文件的备份字典生成, 与之前提高的轻量级递归有些类似. 如果发现有效文件(非目录), 将会根据这个文件生成一个字典, 进行爆破.

  • --common, 通用文件爆破, 语言 | 框架 | IDE | 开源等等都会有留下一些通用文件. 有些时候没有发现指纹, 但可以通过这些通用文件发现一些信息

  • --recon, 自动提取敏感信息, 在有效目录中提取敏感信息,可在 yaml 中配置规则, 目前的规则参考 HaE 与 nuclei 中的 exposure

通过 spray 本身的功能即可创造出很多有趣的预设. 后续可能会维护一个预设仓库, 用来面对各种场景实现多种多样的玩法.

协议升级

如果对一个 https 网站使用 http 去访问, 则大概率会获得 400 的状态码, 告诉你协议错误. 但很多网站配置了自动的协议升级, 通过 location 重定向到 https 的对应网站. 而在批量爆破时, 很难人工一个一个去判断是 http 还是 https.

为此 spray 中添加 http upgrade to https 的优化. 并且在协议升级时, 会重新获取 baseline, 并在命令行有提示.

在未来, spray 还会针对 http2/http3 自动尝试协议升级.

WAF 判断

部分 waf 的逻辑会在爆破到一定数量时进行封 ip,或者跳转到 waf 页面处理.

如果是封 ip 比较好处理, 连续的报错积累到一定程度退出程序即可.

处理跳转类型的 waf 相对复杂一下. 我们通过每隔一定请求, 发送 check 包, 并与最初的基准值进行对比, 如果连续多个 check 包的与基准值发生偏离, 那就判断已经被 waf 了.

实际上, 指纹库中支持了对 waf 指纹的识别, 但因为部分 waf 存在全局指纹, 导致可能出现一些误判, 这一点将会在指纹库优化后, 重新上线基于指纹的 waf 判断. 现在只能在输出结果中看到 WAF 指纹, 不会对被 WAF 退出有实际影响.

这是单个任务与 waf 对抗的手段, 仅限于发现 waf, 并不能绕过 WAF. 绕过的手段会在批量与分布式中介绍.

数据二次处理

因为 spray 的输出格式接近 httpx 与 ffuf, 同样支持对他的数据的进行二次处理, 可以像 httpx 与 ffuf 一样使用 spray.

为此提供了--dump参数不论结果是否有效, 保存 spray 请求的每一个响应.

后续还会提供完整的响应包的原始(header, body)数据. 用作进一步的的数据分析. 甚至实现类似 nuclei 中 offline scan 一样的功能.

批量与分布式

(功能已实现, 文档待完成)

断点续传

(功能已实现, 文档待完成)

死线

(功能已实现, 文档待完成)

其他目录收集方式

spray 想要解决的是需要主动发包的场景. 实际上其他方式.

被动的 url 收集

即不与目标进行交互实现的纯被动 url 收集. 常见的有数据源:

  • webarchive
  • 各家搜索引擎
  • 各家威胁情报平台
  • ...

后续可能会提供一个类似 subfinder 那样的工具实现. 似乎开源社区也已经提供了类似的工具.

通过 headless 爬虫进行的 url 收集

  • katana projectdiscovery 提供的工具, 比较符合 chainreactor 的设计, 后续将会集成到 kindred 中
  • rad 长亭提供的闭源的 headless 爬虫
  • crawlgo 360 提供的开源 headless 爬虫

headless 爬虫其实不少, 为安全领域设计的爬虫又不多

基于 api 的 url 收集

这个领域属于 api 安全, 也有非常多的细节, spray 暂时不打算涉及.常见的有:

  • swagger
  • wsdl
  • graphql
  • showdoc/yapi/apidoc 等自动生成的文档工具
  • ....

我个人会使用apifox进行手动测试

其他手法

除了上面这几项, 这个领域继续深挖还有很多内容.

  • 请求走私与缓存投毒在这个领域的利用
  • 一些 CDN,中间件的特殊 header 对结果的影响.
  • 各种 403bypass 手段
  • java 的权限绕过手法怎么自动化
  • CDN/反向代理真的只有 host 与 path 两种方式去爆破么?
  • 类似HaE的信息收集能在目录爆破中起到什么作用?

某些领域 spray 也无能为力, 但其中一部分, 将在 spray 后续更新中实现.

spray 面临的场景是和 gogo 一样复杂的. 这个领域不需要我去强调其重要性, 在最近几年的红蓝对抗中就能深刻感受到. 很多时候真的只是工具不行, 心有余而力不足. 面对很多网站只能一遍遍的去针对性的编写一个 python 脚本, 就像写 exp 一样. spray 是为了解决这样的问题设计, 并打磨了小半年.

有谁会去在一个项目中爆破几千个 nginx 呢?

todo

这些其他方式有些与 spray 的设计冲突, 有些只是暂时没有好的解决办法. 有些是因为我的时间有限, 还位于 todo 之中..

  • 各种 403bypass 与 java 权限绕过将会以 rule 的形式实现.
  • HaE 的一些规则与 nuclei 中的exposures将会一起以插件的形式加入 spray
  • ffuf 中的一些 fuzz 技术将会经过重新设计后加入 spray, 定位也与 ffuf 原本的设计有些许不同
  • yakit 中的 fuzztag 也值得参考, 与 mask 和 rule 结合能创造出不少新玩法. 或许能解决更多问题

(这里就不使用 tasklist 了, 每实现一部分就会在 todo 中删除, 转化为文档)