zzzteph/boxcutter
GitHub: zzzteph/boxcutter
将主流 Web 渗透测试工具集成在单一 Docker 容器中,通过统一 CLI 和 JSON 输出格式提供从侦察到漏洞扫描的全流程自动化能力。
Stars: 1 | Forks: 0
# boxcutter
**一个容器内的渗透测试工具包。**
ProjectDiscovery、OWASP ZAP、sqlmap、dirb、dirsearch 以及一组 Python
侦察/fuzz 工具,集成在同一个 CLI 下。每个命令在 stdout 上返回相同的 envelope(默认静默),因此它适用于 shell、CI job 或 agent loop:
```
{ "success": true, "kind": "findings", "data": [], "error": null }
```
## 构建与运行
```
docker pull ghcr.io/zzzteph/boxcutter:latest
docker run --rm boxcutter workflow web-scan https://google.com
```
| 选项 | 适用范围 | 作用 |
|---|---|---|
| `--output FILE` | 所有 | 将 JSON envelope 写入文件而不是 stdout |
| `--table` | 所有 | 在 stdout 上打印可读的文本表格而不是 JSON |
| `--debug` | 所有 | 将进度/诊断信息打印到 stderr |
| `--severity LEVELS` | 发现工具 + 工作流 | 仅报告这些严重级别的发现,例如 `--severity critical,high`;省略此项以报告全部 |
| `--timeout N` | 大多数工具 | 单个工具的时间预算(秒),默认值各不相同 |
| `--opt-args "..."` | 二进制包装器 | 直接传递给底层二进制程序的额外 flag |
| `--js` / `--params` | 爬虫,wayback | 仅保留 JS URL / 仅保留带查询参数的 URL |
| `--method` / `--data` | fuzz,path-fuzz | 用于 fuzz 的 HTTP 方法 / POST body |
| `--header "K: V"` | 大多数工具 + 工作流 | 额外的请求 header;在工作流中它会传递给每个内部工具(可重复使用) |
| `--wordlist PATH` | dirb | 自定义字典 |
| `--base-url URL` | js-endpoints,swagger-parser | 用于解析发现路径的基础 URL |
| `--steps` | 工作流 | 打印运行时的每个步骤(否则为静默) |
| `--arg TOOL="..."` | 工作流 | 将参数追加到内部工具,例如 `--arg fuzz="--timeout 60"` |
`boxcutter
--help` 可显示任何工具的确切选项。
## 工具
每个工具还支持 `--output FILE`、`--debug`、`--table`(参见选项);`kind` 列是它发出的 envelope 类型。在示例中,`boxcutter` 代表 `docker run --rm boxcutter` 或 `python3 boxcutter.py`。
### 侦察
| 工具 | 参数 | kind | 作用 |
|---|---|---|---|
| `subfinder ` | — | urls | 被动子域名枚举:攻击面的起点 |
| `dnsx ` | `--timeout` | urls | 解析 A/AAAA/CNAME —— 在你接触主机之前确认它是否真实存在 |
| `httpx ` | `--timeout` `--opt-args` `--header` | items | 哪些主机实际提供 HTTP(S) 服务(端口、协议、IP) |
| `screenshot ` | `--opt-args` `--header` | items | 无头浏览器截图(base64 PNG)+ 标题,用于快速视觉分类 |
| `wayback ` | `--js` `--params` `--inc-subdomains` `--timeout` | urls | 历史 URL(Wayback/CommonCrawl/OTX/URLScan)—— 免费的 endpoint + 参数 |
| `wayback-domains ` | `--timeout` | urls | 在这些存档中出现过的唯一主机 |
```
boxcutter subfinder example.com # passive subdomains
boxcutter dnsx api.example.com # does it resolve?
boxcutter httpx example.com --table # live HTTP services
boxcutter screenshot https://example.com # base64 PNG + title
boxcutter wayback example.com --params # archived URLs that have params
boxcutter wayback-domains example.com # archived hostnames
```
### 爬取
| 工具 | 参数 | kind | 作用 |
|---|---|---|---|
| `katana-crawl ` | `--js` `--params` `--timeout` `--opt-args` `--header` | urls | 使用 Katana 对站点进行主动爬取 |
| `zap-crawl ` | `--js` `--params` `--timeout` `--header` | urls | ZAP spider + AJAX spider(可触达 JS 渲染的链接) |
| `js-endpoints ` | `--base-url` | items | 从 JS 文件中提取 API endpoint 引用 |
```
boxcutter katana-crawl https://example.com
boxcutter workflow url-crawl https://example.com
boxcutter js-endpoints https://example.com/app.js
```
### 漏洞扫描器
| 工具 | 参数 | kind | 作用 |
|---|---|---|---|
| `nuclei ` | `--opt-args` `--header` | findings | 基于模板的扫描(CVE、配置错误、暴露) |
| `sqlmap ` | `--opt-args` `--header` | findings | 确认 + 利用 SQL injection |
| `dirb ` | `--wordlist` `--timeout` `--opt-args` | findings | 目录/文件暴力破解(C 语言编写,速度快) |
| `dirsearch ` | `--timeout` `--header` | findings | 目录/文件暴力破解(Python 编写,灵活) |
| `zap-scan-url ` | `--timeout` `--header` | findings | 对单个确切 URL 进行主动扫描,不进行爬取 |
| `zap-scan-full ` | `--timeout` `--header` | findings | 对整个站点进行爬取 + 主动扫描 |
| `zap-scan-openapi ` | `--timeout` `--header` | findings | 由 OpenAPI/Swagger spec 驱动的主动扫描 |
```
boxcutter nuclei https://example.com --opt-args "-tags cve -severity high,critical"
boxcutter sqlmap "https://example.com/?id=1"
boxcutter dirsearch https://example.com
boxcutter zap-scan-url "https://example.com/?id=1"
boxcutter zap-scan-full https://example.com
```
### Fuzzing
| 工具 | 参数 | kind | 作用 |
|---|---|---|---|
| `path-fuzz ` | `--method` `--header` `--timeout` | findings | 使用内置字典对 `FUZZ` 位置进行暴力破解 |
| `fuzz ` | `--method` `--data` `--header` `--status` `--timeout` `--payload` `--payload-file` `--pattern` | findings | 注入参数/路径/body(XSS、SQLi、SSTI、LFI、RCE、XXE、NoSQL、GraphQL、错误披露),使用 `{NUMBERS}` 枚举 ID,或发送你自己的 `--payload` |
`fuzz` 是基于信号的,而不是盲测。显式标记用于选择模式:`{NUMBERS}`
(或 `{NUMBERS[m-n]}`)用于枚举 IDOR 的数字 ID —— 经过 soft-404 过滤和去重;URL 或 `--data` 中的 `{FUZZ}` 在该位置注入 payload;如果没有标记,它将注入到每个查询参数(或者,如果路径具有类似 ID 的片段,则注入到每一个片段中)。动态 payload(`{RANDOM}` 反射,`EXPR` 评估)会被重新触发以进行确认(快速路径 ≥2/3,否则 ≥4/5);基于时间的盲注只有在响应时间与注入延迟呈单调比例变化时才会被报告。
**使用自带 payload**,通过 `--payload`(可重复)或 `--payload-file` —— 这将跳过内置集合并仅在 fuzz 点(相同的点:`{FUZZ}`、查询参数或 ID 路径片段)发送你的 payload。添加 `--pattern REGEX` 可仅在响应匹配时报告命中;否则,fuzz 仅发送每个 payload 并报告返回的内容。
```
boxcutter fuzz "https://example.com/?id=1" # inject every query param
boxcutter fuzz "https://example.com/search?q={FUZZ}" # inject one marked position
boxcutter fuzz "https://example.com/api/{NUMBERS}" --table # enumerate numeric IDs (IDOR)
boxcutter fuzz "https://example.com/api/{NUMBERS[1-500]}" # enumerate a range
boxcutter path-fuzz "https://example.com/FUZZ"
boxcutter fuzz "https://example.com/api" --method POST --data '{"q":"{FUZZ}"}'
# custom payload + pattern (only reports on match); drop --pattern to just send it
boxcutter fuzz "https://example.com/p?id=1" --payload "' OR '1'='1" --pattern "sql syntax|syntax error"
```
### 敏感信息 / 源码
| 工具 | 参数 | kind | 作用 |
|---|---|---|---|
| `scan-secrets ` | — | findings | 扫描响应 body 以查找暴露的敏感信息 / API 密钥 |
| `git-extract ` | — | findings | 从暴露的 `.git` 目录重建源码并扫描它 |
```
boxcutter scan-secrets https://example.com/app.js
boxcutter git-extract https://example.com/
```
### API 规范
| 工具 | 参数 | kind | 作用 |
|---|---|---|---|
| `swagger-parser ` | `--base-url` `--header` | items | 将 spec 解析为结构化的 endpoint 列表 |
| `swagger-endpoints ` | `--fuzzable` `--header` | urls | spec → 可扫描的 endpoint URL(`--fuzzable` = `{FUZZ}` 变体) |
| `swagger-specs ` | `--header` | urls | 探测常见路径以查找主机上的 spec URL |
```
boxcutter swagger-parser https://api.example.com/openapi.json
boxcutter swagger-endpoints https://api.example.com/openapi.json --fuzzable
boxcutter swagger-specs api.example.com
```
### GraphQL
| 工具 | 参数 | kind | 作用 |
|---|---|---|---|
| `graphql-detect ` | `--timeout` `--header` | urls | 使用 `{__typename}` 探测常见路径;列出 GraphQL endpoint URL |
| `graphql-audit ` | `--timeout` `--header` | findings | introspection、GET/CSRF、batching、冗长错误/敏感信息泄露、schema 指导的参数注入以及 mutation 暴露测试(**仅进行试探性探测** —— 永远不会执行 mutation) |
```
boxcutter graphql-detect api.example.com
boxcutter graphql-audit https://api.example.com/graphql
```
### 常规
| 工具 | 参数 | kind | 作用 |
|---|---|---|---|
| `http-request ` | `-D/--data` `-H/--header` | items | 原始 GET/POST(如果有 `--data` 则为 POST);返回状态、header、body |
```
boxcutter http-request https://example.com -D "q=1" # POST (GET without -D)
boxcutter raw nuclei -u https://example.com -t cves # run any bundled binary natively, no JSON
```
## 工作流
工作流将工具串联起来,并返回**一个合并的、带有来源标签的报告**。默认情况下为静默;`--steps` 显示实时进度;`--arg TOOL="..."` 调整任何内部工具;`--header "K: V"` 将认证信息传递给每个内部工具。
**侦察** —— 输出主机/服务列表:
| 工作流 | 作用 |
|---|---|
| `recon ` | 通过 subfinder + wayback 获取子域名,如果它们能被解析则保留 |
| `recon-http ` | 先进行 recon,然后使用 httpx —— 仅获取存活的 HTTP(S) 服务 |
**扫描单个目标:**
| 工作流 | 作用 |
|---|---|
| `full-scan ` | 爬取 (url-crawl + js-endpoints) -> nuclei -> zap-full -> 对每个参数 URL 进行 fuzz/sqlmap/nuclei-dast -> 对每个 JS 进行 secrets 扫描 |
| `dast-scan ` | 针对单个 URL 的 DAST 捆绑包:fuzz + nuclei -dast + sqlmap + zap-scan-url |
| `wayback-scan ` | 存档 URL -> 对每个参数 URL 进行 sqlmap / fuzz / zap-scan-url,对每个 JS 进行 scan-secrets |
| `wayback-custom-scan ` | wayback -> 使用**你的** payload (`--arg fuzz="--payload ... --pattern ..."`) 对每个参数 URL 进行 `fuzz`,对每个 JS 进行 scan-secrets |
| `url-crawl ` | Katana + ZAP 爬虫,合并并去重 |
| `secrets-hunter ` | 收集 JS 文件 (url-crawl + wayback,仅限 JS) -> 对每个文件进行 scan-secrets |
| `swagger-fuzz ` | 解析 spec 并对每个带参数的 endpoint 进行 fuzz |
| `swagger-dast ` | 针对每个 Swagger endpoint 的 DAST 捆绑包 |
| `swagger-discover ` | 探测常见的 spec 路径,然后对发现的每个 endpoint 进行 DAST |
| `graphql-scan ` | 发现 GraphQL endpoint,然后对每个进行审计 (graphql-detect → graphql-audit) |
**扫描整个环境** —— 从域名开始,首先枚举子域名:
| 工作流 | 作用 |
|---|---|
| `env-scan ` | 全方位:recon -> 存活的 HTTP -> 对每个进行 full-scan + secrets-hunter,对每个主机进行 wayback-scan |
| `secrets-env ` | env-scan 减去漏洞扫描器:recon -> 存活 -> 对每个进行爬取 + secrets-hunter |
| `nuclei-env ` | subfinder -> 对每个发现的子域名进行 nuclei 扫描 |
| `takeover-env ` | subfinder -> 对每个子域名进行 nuclei `-tags takeover` 扫描 |
| `wayback-secrets-env ` | subfinder -> 对每个子域名进行 secrets-hunter (wayback + crawl -> scan-secrets) |
| `wayback-full-scan-env ` | subfinder -> wayback 每个子域名 -> 对每个带参数的 URL 进行 dast-scan |
```
# subdomains that resolve, as a table
boxcutter workflow recon example.com --table
# only the live HTTP(S) services
boxcutter workflow recon-http example.com --table
# full scan one site: crawl -> nuclei -> zap-full -> fuzz/sqlmap/nuclei-dast per
# param URL -> secrets per JS (--steps prints each step)
boxcutter workflow full-scan https://example.com --steps
# same scan, but report only the critical/high findings (filters the merged
# output from every inner tool; --severity also works on a single findings tool)
boxcutter workflow full-scan https://example.com --severity critical,high
boxcutter nuclei https://example.com --severity critical,high
# DAST one URL, with an auth header passed to every inner tool
boxcutter workflow dast-scan "https://example.com/?id=1" --header "Authorization: Bearer T"
# every Swagger endpoint (or discover the spec first)
boxcutter workflow swagger-dast https://api.example.com/openapi.json
boxcutter workflow swagger-discover api.example.com
# whole environment from just a domain (subdomains enumerated first)
boxcutter workflow nuclei-env example.com --steps
boxcutter workflow takeover-env example.com
boxcutter workflow env-scan example.com --arg fuzz="--timeout 60"
```
### 认证扫描
传递 `--header "Name: Value"`(可重复)以在认证下进行扫描。在**工作流**中,它会传递给每个支持 header 的内部工具 —— fuzz、sqlmap、nuclei、dirsearch、swagger 工具以及所有四个 ZAP 工具(它们会通过 Replacer 插件将其注入到每个请求中)。相同的 flag 也可直接作用于每个工具。
.item}`(例如 `for_each: ${live}` → `${live.item}`)。
```
name: dast-scan
input: url
output: findings # the var to emit ('findings', or a ${list} like recon)
steps:
- tool: fuzz
target: ${url}
args: --timeout 120
save: findings
- tool: sqlmap
target: ${url}
save: findings
```
```
# run several tools per parameterised URL
- for_each: ${params}
do:
- tool: nuclei
target: ${params.item}
args: --opt-args=-dast
save: findings
- tool: sqlmap
target: ${params.item}
save: findings
```
步骤键:`tool` · `target`(运行对象:`${url}`、`${params.item}` 等) ·
`for_each` + `do`(对每个项目运行步骤;当前项目是 `${.item}`) ·
`args` · `save`(要收集到的变量) · `pick`(提取字段) · `select`(仅过滤) ·
`alive`(保留可解析的主机) · `workflow`(调用另一个工作流)。顶层的
`output:` 指定要发出的变量。
**过滤器**在 `${...}` *内部*重塑列表,通过 `|` 从左到右传递。
它们可在任何使用 `${...}` 的地方生效 —— `for_each`、`select`、`target`、`output`:
| 过滤器 | 效果 |
|---|---|
| `params` | 仅保留具有查询参数的 URL |
| `js` | 仅保留 `.js` URL |
| `dedup` | 合并共享相同路径和参数名的参数 URL(忽略值) |
| `unique` | 去除重复字符串(保持顺序) |
| `hosts` | 将每个 URL 缩减为其主机名(纯域名原样保留) |
| `url` | 确保有协议(将 `https://` 添加到纯主机前) |
```
# from a 'urls' var: keep parameterised URLs, collapse value-duplicates
- select: ${urls | params | dedup}
save: params
# iterate just the JS files found in 'urls'
- for_each: ${urls | js}
do:
- tool: scan-secrets
target: ${urls.item}
save: findings
```
实际示例 —— 假设 `urls` 变量包含:
```
https://x/?id=1 https://x/?id=2 https://x/news?id=9
https://x/app.js https://x/login https://x/login
${urls | params} -> ?id=1, ?id=2, news?id=9 only URLs with a query
${urls | params | dedup} -> ?id=1, news?id=9 ?id=2 folds into ?id=1 (same path+param)
${urls | js} -> app.js only .js files
${urls | unique} -> drops the duplicate /login
${urls | hosts} -> x just the hostname
```
单个值的工作方式相同:`${target | url}` 将 `example.com` 转换为
`https://example.com`,而 `${target | hosts}` 将 `https://x/a?b=1` 转换为 `x`
(这就是 `secrets-hunter` 向爬虫提供 URL 但向 wayback 提供纯主机名的方式)。
链式操作从左到右进行,因此 `params | dedup` 会先过滤出带参数的 URL,*然后*合并重复值。
### 你自己的工作流
将 `BOXCUTTER_WORKFLOWS` 指向一个包含 `*.yaml` 文件的目录,即可添加(或覆盖)工作流,而无需重新构建或修改 Python 代码:
```
# from source
BOXCUTTER_WORKFLOWS=./myflows python3 boxcutter.py workflow quick-look https://example.com
# in the container - mount the dir
docker run --rm -v "$PWD/myflows:/flows" -e BOXCUTTER_WORKFLOWS=/flows \
boxcutter workflow quick-look https://example.com
```
你的文件将出现在 `boxcutter workflow --list` 中,紧挨着内置工作流。一个 `name:` 与内置工作流(例如 `full-scan`)匹配的文件会**覆盖**它;任何其他名称都会被**添加**。它们使用与上述文档完全相同的工具、过滤器和 `${...}` 语法 —— 无需学习其他任何内容。
## 输出与依赖
`data` 始终是一个列表,而 `kind` 表示其中的内容,因此使用者可以提前了解数据结构:
| `kind` | `data` 项目 | 工具 |
|---|---|---|
| `findings` | `{severity, title, info, url}` | nuclei, sqlmap, fuzz, scan-secrets, dirb, dirsearch, zap-scan-*, ... |
| `urls` | 字符串 | subfinder, wayback, katana-crawl, zap-crawl, swagger-endpoints, ... |
| `items` | 对象(始终有 `url`;HTTP 代码使用 `status`) | httpx, js-endpoints, swagger-parser, ... |
`error` 在成功时为 `null`。只要工具运行过(即使没有发现任何内容),它就会返回退出状态码 0,只有在输入错误时才返回非零值,因此请根据 `success`/`data` 进行判断,而不是退出状态码。
依赖项:工具只需要 **Python 3 + `requests`**。工作流还需要 **PyYAML** 来加载 YAML 库(已内置在镜像中;如果是纯 Python 使用,请通过 `pip install pyyaml` 安装)。`naabu` 内置于镜像中(`boxcutter raw naabu ...`)。
仅限授权测试:你拥有或被允许评估的系统。
## 致谢
boxcutter 只是一个轻量级的包装器 —— 扫描工作是由以下项目完成的,所有荣誉归属于其作者:
- [ProjectDiscovery](https://github.com/projectdiscovery) — `subfinder`、`dnsx`、`naabu`、`katana`、`nuclei`、`httpx`
- [OWASP ZAP](https://www.zaproxy.org/) — 爬取和主动扫描 (`zap-crawl`, `zap-scan-*`)
- [sqlmap](https://sqlmap.org/) — SQL injection (`sqlmap`)
- [dirb](https://dirb.sourceforge.net/) + [dirsearch](https://github.com/maurosoria/dirsearch) — 内容发现
原创部分仅为 CLI、JSON envelope 和 YAML 工作流层;其他一切都是这些工具,通过包装以使用统一的格式进行交互。
标签:CISA项目, Docker, DOE合作, Web安全, 安全工具箱, 安全防御评估, 实时处理, 密码管理, 网络调试, 自动化, 蓝队分析, 请求拦截, 运行时操纵, 逆向工具