xcull/xcull
GitHub: xcull/xcull
一个安全感知的URL去重工具,用于侦察流水线中清理攻击面并保留可利用模式。
Stars: 0 | Forks: 0
xcull
用于侦察流水线的安全感知 URL 去重工具。
将嘈杂的侦察 URL 折叠为干净的攻击面,同时保留可利用的模式。
功能 ·
安装说明 ·
使用方法 ·
运行 xcull ·
示例 ·
标志用例 ·
输出 ·
流水线集成 ·
基准测试 ·
问题报告 ·
赞助 ·
许可证
## 功能
- 单一静态 C 二进制文件,无运行时依赖。
- 流式去重,在 780,200 个 URL 上峰值 RSS 恒定为 22 MB。
- 保留每个不同的对象标识符(数字、UUID、十六进制)用于 IDOR 和 BOLA 枚举。
- 保留每个不同的会话令牌,用于会话绑定的授权测试。
- 基于查询参数形状进行去重,而非原始查询字符串。
- 保留重复的参数名(`?id=1&id=2`)作为不同的 HTTP 参数污染攻击面。
- 路径模板化折叠重复的 slug 变体,而不合并不同的端点。
- 默认过滤二进制资产、wayback 噪音和扫描器探测 URL。
## 安装说明
### 快速安装 (Linux x86_64 / arm64)
```
curl -fsSL https://raw.githubusercontent.com/xcull/xcull/main/install.sh | sh
```
下载最新发行版,验证其 `sha256`,并安装到
`/usr/local/bin`。遵循 `PREFIX`(例如 `PREFIX=$HOME/.local`)和
`XCULL_VERSION=vX.Y.Z` 以锁定标签。更喜欢在运行前查看?
先执行 `curl -fsSL .../install.sh | less`;它获取的二进制文件与下方
校验和相同的发行压缩包相同。
### Docker
拉取预构建的多架构镜像 (linux/amd64 + arm64),无需构建:
```
gau example.com | docker run -i --rm ghcr.io/xcull/xcull > urls.txt
```
`docker run` 首次使用时会拉取 `ghcr.io/xcull/xcull:latest`;使用 `:2.1.0` 锁定版本。
更喜欢自己构建:
```
docker build -t xcull .
gau example.com | docker run -i --rm xcull > urls.txt
```
无论哪种方式,它都是一个静态 musl 构建,被复制到一个 `scratch` 镜像中:里面
只有二进制文件,因此可以在任何地方运行且保持极小体积。
### 预编译二进制文件
从 [github.com/xcull/xcull/releases](https://github.com/xcull/xcull/releases) 下载您平台对应的最新发行版,
然后:
```
tar xzf xcull-*-linux-x86_64.tar.gz
sudo install -m755 xcull /usr/local/bin/xcull
```
发行版包含 `linux-x86_64` 和 `linux-arm64`。每个压缩包旁边都有一个 `.sha256` 文件。
验证校验和
```
curl -LO https://github.com/xcull/xcull/releases/latest/download/xcull-v1.0.1-linux-x86_64.tar.gz
curl -LO https://github.com/xcull/xcull/releases/latest/download/xcull-v1.0.1-linux-x86_64.tar.gz.sha256
sha256sum -c xcull-v1.0.1-linux-x86_64.tar.gz.sha256
```
### 从源码构建
```
git clone https://github.com/xcull/xcull
cd xcull
make
sudo make install
```
构建选项 (PREFIX, DESTDIR, CC, CFLAGS)
`make install` 遵循 `PREFIX`(默认 `/usr/local`)和用于打包的 `DESTDIR`。man 手册页以及 bash 和 zsh 补全文件与二进制文件一起安装,因此安装后 `man xcull` 有效,且 Tab 补全会识别标志。`make test` 运行黄金输出回归测试套件;`make benchmark` 额外构建 `runstat`,即基准测试使用的 fork + wait4 + getrusage 测试工具。
```
# 安装到自定义路径
make install PREFIX=$HOME/.local
# 打包到分阶段 DESTDIR(供发行版打包者使用)
make install DESTDIR=/tmp/staging PREFIX=/usr
# 使用 clang 构建
make CC=clang
# 构建时启用额外警告
make CFLAGS="-O3 -march=native -flto -Wall -Wextra"
```
卸载
```
sudo make uninstall
```
删除由 `make install` 安装的二进制文件、man 手册页和 shell 补全文件。遵循相同的 `PREFIX` 和 `DESTDIR` 变量。
## 使用方法
xcull 从 stdin 读取 URL,并将去重后的集合写入 stdout。
通常情况是无标志的单行管道。
```
cat urls.txt | xcull
```
运行 `xcull --help` 查看选项列表,`xcull --help all` 查看使用示例和退出代码,或 `man xcull` 获取完整参考。完整的标志列表:
```
usage: xcull [-F fold-ids][-x keep-invalid][-a keep-assets]
[-s case-sensitive][-L N subset-cmp cap]
[-k][-p][-W][-r][-v]
xcull {-h | --help [all]}
xcull {-V | --version}
-F fold object-ids (numeric/UUID/hex/stem-id segments collapse
to one witness). Default keeps every distinct id; -F is the
aggressive endpoint-discovery mode.
-x keep invalid URLs, fully raw, no cleaning.
-a keep all assets (do not filter images/fonts/css/audio/video
like .css/.png/.woff/.mp4/.mp3/.m4p/...).
-s case-sensitive path matching.
-L N cap subset-merge comparisons per inserted record (0 = off,
the default). Safety valve for adversarial multi-cardinality
antichains; when it trips, the record is kept un-merged so
output may differ from a full run.
-k keep param values and every distinct query key-set as its own
line (dedup on the full query; disables the default
query-subset merge and restores streaming output).
-p no path templating at all (also drops the title-slug fold).
-W opt out of wayback-noise handling.
-r opt out of URL canonicalization.
-v, --verbose print "xcull:
-> (peak RSS KB)" to stderr.
-h, --help print the option list (--help all adds examples and
exit codes).
-V, --version print version, build info, and license, then exit.
```
## 运行 xcull
xcull 始终从 stdin 逐行读取 URL,并将清理后的集合写入 stdout。没有 `-l` 或 `-u` 标志;输入形状由 shell 控制。容忍空行和前导/尾随空格。
### 管道输入 (stdin)
```
gau example.com | xcull
```
这是典型用法:侦察源将其输出直接管道传输给 xcull,xcull 将去重后的集合流式传输给下游。
### 文件输入
```
xcull < urls.txt
```
等同于 `cat urls.txt | xcull`。使用您的流水线读取更自然的方式。
### 多源输入
```
cat gau.txt waybackurls.txt katana.txt | xcull > urls.txt
```
xcull 对并集进行去重,因此组合多个侦察源的成本与运行一次相同。对于默认模式,顺序无关紧要:xcull 延迟发送,因此后面的记录如果其模板化路径相同,可以追溯地取消发送先前的记录。
### 预排序输入
xcull 从不假定输入已排序;输出顺序相对于给定标志集下的首次出现是确定性的。对相同输入的两次运行会产生字节完全相同的输出。
## 示例
一些常见的形状;每个标志的用例将在下一节中介绍。
```
# 从归档源清理侦察面
gau example.com | xcull > surface.txt
```
```
# 合并多个来源并进行一次性去重
cat gau.txt waybackurls.txt katana.txt | xcull | tee urls.txt
```
```
# 为参数模糊测试管道提供数据
gau example.com | xcull | qsreplace FUZZ | anew params.txt
```
```
# 展示缩减情况(统计信息输出到 stderr,数据仍输出到 stdout)
cat urls.txt | xcull -v > deduped.txt
```
## 标志用例
每个标志都有特定的用途。默认模式解决了 90% 的情况;仅在适用其特定用例时才使用标志。
### 默认(无标志)
**适用场景:** 正常的侦察过程。将存档源或爬虫器输出通过 xcull 进行流式处理,并将清理后的集合管道传输给下一个消费 URL 的工具。
```
gau example.com | xcull > urls.txt
```
在默认模式下,每个不同的对象 ID(`/user/41`、`/user/42`)、会话令牌(`;jsessionid=...`)和 GraphQL 操作(`?query={me{id}}`)都会保留,查询 URL 仅通过子集关系合并,渲染噪音资产会被丢弃,wayback/扫描器探测垃圾会被过滤。查询顺序被规范化,因此 `?id=1&token=a` 和 `?token=b&id=2` 会合并,但重复的参数名(`?id=1&id=2`)会作为不同的 HTTP 参数污染攻击面保留。
### `-F` 折叠对象 ID(路由发现)
**适用场景:** 您希望每个端点模式有一个见证,而不是每个对象。对于具体 ID 是噪音的路由扫描过程很有用。
输入:
```
https://example.com/user/41
https://example.com/user/42
https://example.com/user/43
https://example.com/file/550e8400-e29b-41d4-a716-446655440000
https://example.com/file/6ba7b810-9dad-11d1-80b4-00c04fd430c8
```
默认输出(全部五个保留,因此 IDOR/BOLA 枚举可以看到每个 ID):
```
https://example.com/user/41
https://example.com/user/42
https://example.com/user/43
https://example.com/file/550e8400-e29b-41d4-a716-446655440000
https://example.com/file/6ba7b810-9dad-11d1-80b4-00c04fd430c8
```
使用 `-F`(每个 ID 类折叠为一个见证):
```
https://example.com/user/41
https://example.com/file/550e8400-e29b-41d4-a716-446655440000
```
标准侦察模式:首先为 IDOR 过程运行默认模式,然后使用 `-F` 重新运行以获得路由覆盖。
```
gau example.com | xcull -F > routes.txt
```
### `-x` 保留无效 URL(取证/调试)
**适用场景:** 您怀疑默认清理器丢弃了本应保留的内容,或者您想在没有任何合理性检查的情况下审核原始结构去重。
输入:
```
https://example.com/api/v1/users
https://example.com/
https://example.com/api/v1/users/../../etc/passwd
https://example.com/api/v1/users?cmd=ls
```
默认输出(垃圾过滤器丢弃了 XSS 负载和路径遍历;裸端点折叠到其装饰后的同级):
```
https://example.com/api/v1/users?cmd=ls
```
使用 `-x`(一切都保留,只运行结构去重):
```
https://example.com/api/v1/users
https://example.com/
https://example.com/api/v1/users/../../etc/passwd
https://example.com/api/v1/users?cmd=ls
```
与默认运行并排使用,以查看哪些行被归类为垃圾:
```
diff <(cat dirty.txt | xcull) <(cat dirty.txt | xcull -x) | less
```
### `-a` 保留所有资产(源映射、JS、静态文件中的密钥)
**适用场景:** 您正在寻找 JS 包、源映射或静态配置文件中的密钥,而默认渲染噪音过滤器会将其丢弃。
输入:
```
https://example.com/index.html
https://example.com/static/app.js
https://example.com/static/style.css
https://example.com/img/logo.png
https://example.com/fonts/main.woff2
```
默认输出(CSS、PNG、WOFF2 作为渲染噪音被丢弃;`.js` 和规范化的根保留):
```
https://example.com/
https://example.com/static/app.js
```
使用 `-a`(每个静态资产都保留):
```
https://example.com/
https://example.com/static/app.js
https://example.com/static/style.css
https://example.com/img/logo.png
https://example.com/fonts/main.woff2
```
`.map` URL 在默认情况下已保留(源映射泄露未压缩的源代码,并且是一个常见的侦察发现),因此主要为其他静态类使用 `-a`。
```
gau example.com | xcull -a | grep -E '\.(js|json|env)$'
```
### `-s` 区分大小写的路径匹配
**适用场景:** 目标运行在大小写敏感的后端上(Java/JSP、某些 Python/Node 框架),其中 `/Admin` 和 `/admin` 是不同的路由。
输入:
```
https://example.com/Login
https://example.com/login
https://example.com/Admin/panel
https://example.com/admin/panel
```
默认输出(大小写折叠,先到先得):
```
https://example.com/Login
https://example.com/Admin/panel
```
使用 `-s`(每个大小写变体都保留):
```
https://example.com/Login
https://example.com/login
https://example.com/Admin/panel
https://example.com/admin/panel
```
在 Apache、IIS 和 PHP 目标上,默认值就是您想要的;仅当您有理由认为后端将大小写视为重要时才使用 `-s`。
### `-L N` 限制子集合并比较次数(对抗性输入)
**适用场景:** 输入中有一个端点累积了数千个不同的查询键集(模糊器转储、遥测日志、缓存清除器垃圾邮件)。默认的子集合并每个插入桶的成本是 O(K);一个对抗性的多基数反链仍然可能花费实际时间。
```
cat fuzzer_dump.txt | xcull -L 100 -v
```
`-L N` 将每个插入记录的比较次数上限设为 `N`。当达到上限时,该记录将保持未合并状态(输出可能与完整运行不同;这是一个安全阀,而不是质量旋钮)。`0`(默认)表示无上限。
这里没有小的前后对比:在正常输入上,上限永远不会触发,因此 `-L N` 产生与默认字节完全相同的输出。差异仅在单个路径累积了超过 `N` 个不同键集时出现,此时超过上限的记录将未合并地发出,而不是被折叠。
### `-k` 保留参数值和每个不同的键集(值挖掘)
**适用场景:** 您正在挖掘参数值,而不是端点。您希望看到 `?role=`、`?env=`、`?debug=` 的每个具体值,并且不希望 xcull 的查询子集合并折叠任何内容。
输入:
```
https://example.com/page?id=1
https://example.com/page?id=2
https://example.com/page?id=3
https://example.com/page?role=admin
https://example.com/page?debug=true
```
默认输出(每个不同的键集有一个见证;三个 `?id=` 变体因为键集相同而折叠):
```
https://example.com/page?id=1
https://example.com/page?role=admin
https://example.com/page?debug=true
```
使用 `-k`(每个值都保留):
```
https://example.com/page?id=1
https://example.com/page?id=2
https://example.com/page?id=3
https://example.com/page?role=admin
https://example.com/page?debug=true
```
作为副作用,`-k` 还会恢复流式输出(无延迟发送),因此它是最低 RSS 的模式。
```
# 查找管理/调试/暂存环境的参数值
gau example.com | xcull -k | grep -iE '\?(role|env|debug|admin)='
```
### `-p` 无路径模板化(字面路径)
**适用场景:** 目标的路径段是有意义的,而不是模板化的。文档站点、内容门户、知识库,其中每个 slug 是不同的内容,而不是同一路由的变体。
输入:
```
https://example.com/blog/post-title-1
https://example.com/blog/post-title-2
https://example.com/blog/post-title-3
https://example.com/blog/post-title-4
https://example.com/blog/post-title-5
https://example.com/blog/post-title-6
https://example.com/blog/post-title-7
https://example.com/blog/post-title-8
https://example.com/docs/install
https://example.com/docs/config
```
默认输出(博客 slug 组折叠为一个见证;两个文档页保持不同,因为未达到 slug 类阈值):
```
https://example.com/blog/post-title-1
https://example.com/docs/install
https://example.com/docs/config
```
使用 `-p`(每个不同的路径都保留):
```
https://example.com/blog/post-title-1
https://example.com/blog/post-title-2
https://example.com/blog/post-title-3
https://example.com/blog/post-title-4
https://example.com/blog/post-title-5
https://example.com/blog/post-title-6
https://example.com/blog/post-title-7
https://example.com/blog/post-title-8
https://example.com/docs/install
https://example.com/docs/config
```
当您更愿意将完整的路径清单交给支持文档的扫描器时使用。
```
gau docs.example.com | xcull -p > docs_paths.txt
```
### `-W` 退出 wayback 噪音处理
**适用场景:** 输入来自 wayback,但您希望主机保持与存档记录的完全一致。默认情况下,xcull 会去除 wayback 附加到主机前的开头百分号编码胶(`2f`、`3a`、`25252f` 等),因此 `2fexample.com` 会折叠到 `example.com`。`-W` 保留这些原始主机,以便您可以研究存档是如何损坏它们的。
输入:
```
https://2fexample.com/login
https://example.com/login
https://3aexample.com/admin
https://example.com/admin
```
默认输出(去除了开头的 wayback 胶,因此损坏的主机折叠到干净的主机上):
```
https://example.com/login
https://example.com/admin
```
使用 `-W`(原始主机,无折叠):
```
https://2fexample.com/login
https://example.com/login
https://3aexample.com/admin
https://example.com/admin
```
```
# 保留归档的原始主机关联信息(用于威胁情报/取证工作)
waybackurls example.com | xcull -W
```
### `-r` 退出 URL 规范化
**适用场景:** 您需要与其他工具进行字节精确比较,输入已经是 RFC 3986 规范化的,或者您正在调试编码问题,而 xcull 的规范化器可能隐藏了该问题。
输入:
```
https://example.com:443/api/users
https://example.com/login%2Fadmin
http://example.com:80/page
```
默认输出(去除默认端口,百分号编码的斜杠保留为字面字符):
```
https://example.com/api/users
https://example.com/login%2Fadmin
http://example.com/page
```
使用 `-r`(无规范化;保留默认端口):
```
https://example.com:443/api/users
https://example.com/login%2Fadmin
http://example.com:80/page
```
```
cat urls.txt | xcull -r | diff - <(cat urls.txt | xcull)
```
### `-v`, `--verbose` 统计信息(CI、监控)
**适用场景:** 您想要一行运行摘要(输入行数、输出行数、峰值 RSS)用于日志、CI 或快速合理性检查。区别在 stderr 上;stdout 与默认运行字节完全相同,因此 `-v` 可以安全地在生产流水线中保留。
默认(无 `-v`)不向 stderr 写入任何内容:
```
$ cat urls.txt | xcull > /dev/null
$
```
使用 `-v`,运行结束后一行摘要会写入 stderr:
```
$ cat urls.txt | xcull -v > /dev/null
xcull: 782143 -> 55920 (peak RSS 22612 KB)
```
```
gau example.com | xcull -v > urls.txt 2>> xcull.log
```
```
# CI 断言:去重比率至少应达到 5 倍
in=$(wc -l < urls.txt)
out=$(xcull -v < urls.txt 2>&1 > deduped.txt | awk '{print $4}')
test $(( in / out )) -ge 5 || { echo "dedup too weak"; exit 1; }
```
### `-V`, `--version` 构建信息
**适用场景:** 您需要记录具体是哪个构建产生了结果,或者提交错误报告。像 `wget --version` 一样,它会打印版本、平台、编译时的功能、实际的构建和链接标志、默认策略和许可证,然后退出。
```
$ xcull --version
xcull 2.1.0 built on Linux x86_64.
Capabilities (all compiled in, libc only, no runtime dependencies):
+idor-preserve +session-preserve +hpp-preserve +query-shape-dedup
+subset-merge +garbage-gate +wayback-clean +case-fold
+canonical
Build:
-O3 -march=native -flto
gcc 14.2.0
built May 28 2026
...
```
`-V` 和 `--version` 是等效的。
## 输出
xcull 将清理后的 URL 写入 stdout,每行一个,按首次出现的确定性顺序。stderr 保留用于诊断信息。
### stdout
去重后的 URL 集。将其管道传输到文件、从 stdin 读取的工具(`qsreplace`、`nuclei`、`ffuf`、`httpx`、`gf`),或使用 `tee` 同时传输两者。
```
cat urls.txt | xcull > deduped.txt
```
### stderr (`-v`)
使用 `-v`(或 `--verbose`)时,xcull 在运行结束后向 stderr 打印一行摘要:
```
xcull: 782143 -> 55920 (peak RSS 22612 KB)
```
数字含义为:读取的输入行数、发出的输出行数、峰值驻留集大小(千字节)。stdout 不受影响,因此 `-v` 可以安全地添加到生产流水线中。
```
cat urls.txt | xcull -v > deduped.txt
# stderr: xcull: 782143 -> 55920(峰值 RSS 22612 KB)
```
### 退出代码
| 代码 | 含义 |
|------|------|
| 0 | 成功。已写入输出。 |
| 1 | stdin 或 stdout 上的 I/O 错误。 |
| 2 | 未知标志或格式错误的参数。 |
## 流水线集成
xcull 是一个 stdin-stdout 过滤器。上游任何发出 URL 的源都可以为其提供输入;下游任何消费 URL 的源都可以从它读取。
### 与存档源配合
```
# gau (https://github.com/lc/gau)
gau example.com | xcull > urls.txt
# waybackurls (https://github.com/tomnomnom/waybackurls)
waybackurls example.com | xcull > urls.txt
# 两者合并,进行一次性去重
( gau example.com; waybackurls example.com ) | xcull > urls.txt
```
### 与主动爬虫器配合
```
# katana (https://github.com/projectdiscovery/katana)
katana -u https://example.com -silent | xcull > urls.txt
# hakrawler (https://github.com/hakluke/hakrawler)
echo https://example.com | hakrawler | xcull > urls.txt
```
### 为参数模糊器提供输入
```
# qsreplace + ffuf 风格的参数模糊测试
gau example.com | xcull | qsreplace FUZZ | sort -u > params.txt
# 仅包含查询字符串的 URL
cat urls.txt | xcull | grep '?' > queried.txt
```
### 为漏洞扫描器提供输入
```
# nuclei (https://github.com/projectdiscovery/nuclei)
cat urls.txt | xcull | nuclei -t exposures/
# 扫描前进行 httpx 存活检查
cat urls.txt | xcull | httpx -silent | nuclei
```
### 与 anew 组合
```
# 仅保留之前未见过的 URL
gau example.com | xcull | anew urls.txt
```
## 基准测试
在单个标记的 780,200 URL 输入(`D_unified.full`,55,920 个真实规范端点组)上,与 `urldedupe`、`uro`、`urless` 和 `uddup` 进行可复现的直接对比。峰值 RSS、吞吐量、完成时间、错误合并率、每个类别的 PRF 以及支持每个主张的所有 CSV 数据都位于一个单独的仓库中:
**[github.com/xcull/xcull-benchmark](https://github.com/xcull/xcull-benchmark)**
一个 99 行的并排演示(侦察工程师一眼就能注意到的差异类型:对象 ID、会话令牌、slug 折叠、查询键集合并)位于
**[xcull-benchmark/COMPARISON.md](https://github.com/xcull/xcull-benchmark/blob/main/COMPARISON.md)**。
## 问题报告
为保持问题跟踪器专注于可操作的项目,请:
- **错误报告** 通过 [问题模板](https://github.com/xcull/xcull/issues/new?template=bug_report.yml) 提交。
包括最小重现器:命令行、触发问题的最小输入、预期输出、实际输出。五行重现器处理最快。
- **功能请求** 通过 [功能模板](https://github.com/xcull/xcull/issues/new?template=feature_request.yml) 提交。
如果提案合并了当前保留的 URL,请论证为什么合并后的 URL 不代表不同的攻击面。
- **安全漏洞** 不得作为公开问题提交。请使用 GitHub 私有漏洞报告功能,地址为
[github.com/xcull/xcull/security/advisories/new](https://github.com/xcull/xcull/security/advisories/new)。
范围请参见 [SECURITY.md](SECURITY.md)。
- **使用问题** 和流水线集成帮助欢迎在 [讨论区](https://github.com/xcull/xcull/discussions) 提问。
有关补丁门槛和发布流程,请参见 [CONTRIBUTING.md](CONTRIBUTING.md)。
## 赞助

如果您想支持此项目,可以在 **[github.com/sponsors/xcull](https://github.com/sponsors/xcull)** 成为赞助者。
## 许可证
源代码在 **Xcull Source Available License (XSAL) v1.0** 下可用。
个人使用、漏洞赏金研究和非商业安全工作免费。完整条款请参见 [LICENSE](LICENSE.md)。
### 商业和 OEM 许可
在商业产品、托管服务、SaaS 平台或设备中嵌入 xcull 需要单独的许可。定价层级和联系方式在 [XCOL.md](XCOL.md) 中。
## 更新日志
发布日志请参见 [CHANGELOG.md](CHANGELOG.md)。标签:BOLA枚举, IDOR枚举, URL去重, 会话令牌测试, 侦察工具, 侦察管道, 内存优化, 安全感知, 客户端加密, 授权测试, 流式处理, 网络安全, 请求拦截, 隐私保护, 静态二进制