用于大规模端口发现、主机发现、横幅抓取以及跨内部或外部网络的基于NSE的漏洞扫描的masscan和nmap的Python封装。
此脚本是 masscan 和 nmap 的封装工具。nmap 负责主机发现以及(对于较小规模的扫描)端口发现、服务横幅抓取和 NSE 脚本。Masscan 用于大规模端口发现,在需要原始速度的场景下使用。请从您常用的包管理器或从源代码安装两者。
需要 Python 3.8+(pyproject.toml 中的 requires-python;CI 最低要求为 3.8)。
SpooNMAP 可以直接从代码检出运行——无需安装步骤,请参阅下方的“用法”——或者使用 uv 安装为独立命令:
uv tool install git+https://github.com/trustedsec/spoonmap
这会将一个 spoonmap 可执行文件放到你的 PATH 中,因此你可以在任意目录下以 spoonmap 调用它,而无需克隆仓库并运行 ./spoonmap.py。没有 PyPI 包——此项目从未发布到 pypi.org,上面的命令直接从 git 仓库安装。请使用上面的完整 git+https://... 形式,而不是裸的 uv tool install spoonmap;无论该名称在 PyPI 上现在或将来解析为什么,都不是此项目。
要更新到最新提交:
uv tool upgrade spoonmap
以这种方式安装并不会使 SpooNMAP 成为自包含的扫描器:
masscan 和 nmap 仍然是独立的系统工具,必须单独安装
(参见上文“依赖项”),与从检出目录运行时完全一样——uv tool install
只打包 SpooNMAP 自身的 Python 代码及其捆绑的 NSE 脚本,
而不包含它通过 shell 调用的外部二进制文件。
以这种方式安装前值得了解的一点是:已安装的
spoonmap 的 Python 模块位于 uv 放置其托管工具环境的任何位置——
而不是你会想到去查找配置文件或扫描输出的目录。
这正是下文“文件存放位置”一节所讨论的场景——阅读该节以了解
哪些内容会相对于当前目录解析以及原因;通过 uv tool install 安装
并不会改变这一规则,只是让这一规则变得重要,因为不再有检出目录
可供配置或输出路径习惯性地回退。
直接执行脚本会提示你输入所有必需的选项。
config.json、目标/排除文件以及扫描输出都会相对于
你运行命令的目录进行解析——如果你的输出不在预期位置,
请参阅下文“文件存放位置”,尤其是当你习惯从 SpooNMAP 自身目录之外
通过路径调用它时。
如果你使用 uv,无需单独的虚拟环境即可运行:
uv run spoonmap.py
或者如果脚本可执行,则直接调用:
# ./spoonmap.py
________ _____ _______ _________________
__ ___/______________________ | / /__ |/ /__ |__ __ \
_____ \___ __ \ __ \ __ \_ |/ /__ /|_/ /__ /| |_ /_/ /
____/ /__ /_/ / /_/ / /_/ / /| / _ / / / _ ___ | ____/
/____/ _ .___/\____/\____//_/ |_/ /_/ /_/ /_/ |_/_/
/_/
Service Categories (comma-separated numbers, default: All)
(1) Web [80, 443, 7001, 7002, 8000, 8080, 8081, 8443, 8888, 9090, 10443]
(2) Database [1433, U:1434, 1521, 3306, 5432, 6379, 9200, 27017]
(3) Remote Management [22, 23, 3389, 5900, 5901, 6129, 1723, 5985, 5986]
(4) Email [25, 110, 143, 465, 587, 993, 995]
(5) LDAP [389, 636]
(6) Network Infrastructure [53, 179, U:500, U:161, U:623, U:631, U:1194, 1194]
(7) File Transfer [21, 111]
(8) SMB [445, 135, 139, U:137]
(9) Specialized [1090, 3300, 4786, 6970, 2375, 4243, 9100, 8530, 8531]
(10) Containers & Debuggers [2377, 10250, 8001, 9229, 2345, 5005, 61616, 8009, 6000]
(11) Local LLM [11434, 1234, 7860, 5000, 5001, 1337, 3000, 8000, 8080]
(12) Full Port Scan [1-65535, TCP only — no UDP]
(c) Custom Port Scan [enter your own comma-separated ports]
(The Full Port Scan number increments automatically with the number of categories.)
**Full Port Scan is TCP only.** It sweeps TCP 1-65535 and runs no UDP discovery at
all, so every `U:` port listed in the categories above — SNMP (U:161), IKE (U:500),
IPMI (U:623), IPP (U:631), OpenVPN (U:1194), NetBIOS (U:137), SQL Browser (U:1434) —
is skipped, along with the NSE scripts and findings that depend on them. It is
*wider* than All on TCP and *narrower* on UDP. For both, run All and Full as two
passes, or use the Custom option with the UDP ports appended
(e.g. `1-65535,U:161,U:500,U:623`).
Which categories would you like to scan (e.g. 1,3 — default: All)?
Would you like to enumerate service banners for any identified services (default: Yes)?
Would you like to run NSE security scripts on identified services (default: No)?
Target Scan
(1) External
(2) Internal
Is this an internal or external scan (default: External)?
How fast would you like to scan (default: 20000 packets/second)?
Please enter the full path for the file containing target hosts (default: /opt/spoonmap/ranges.txt):
Would you like to exclude any hosts? (default: No)
Run host discovery before port scanning (default: Yes)?
Tune advanced settings (nmap threads, masscan batch size, nmap work-unit threshold)? (default: No)
你也可以创建一个 config.json 文件(基于 config.json.sample)来跳过所有提示:
{
"scan_categories": ["Web", "Database", "Remote Management"],
"banner_scan": "True",
"script_scan": "False",
"host_discovery": "True",
"target_scan": "Internal",
"max_rate": "2000",
"target_file": "ranges.txt",
"output_path": "./",
"exclusions_file": "exclusions.txt",
"nmap_threads": 5,
"masscan_batch_size": 5,
"nmap_threshold": 5000000
}
要扫描所有类别,请设置 "scan_categories": "All"。
要扫描全部 65535 个 TCP 端口,请设置 "scan_categories": "Full" —— 注意这仅限 TCP,不执行任何 UDP 发现。
如需完全自定义端口列表,请省略 scan_categories,改用 "dest_ports": ["80","443","U:53"]。
UDP 端口使用 U: 前缀指定(例如 "U:53")。
当你回答交互式提示时,所选的选项会在扫描开始前写入 config.json。生成的文件会像 config.json.sample 一样记录每个可编辑字段,并带有一个 __generated_by_prompts__ 标记键。这意味着被中断的交互式扫描可以像配置驱动的扫描一样恢复——只需使用 --resume 重新运行,所有提示都会被跳过。
如果在 output_path 中检测到先前扫描的输出,工具会提供三个选项:[d]elete(删除先前的输出并重新开始)、[a]ppend(保留先前的输出但重新运行所有阶段),或 [r]esume(保留先前的输出并跳过已完成的工作,与 --resume 标志的行为完全一致)。
重新运行时更改选项。 选择 [d]elete 或 [a]ppend 会重新询问每个选项,并将保存的 config.json 值预填为默认值——因此一路按 Enter 即可重现先前的扫描,你也可以只更改你关心的端口、速率或目标。[r]esume 会跳过提示,因为它是在继续同一次扫描。重新提示仅适用于工具生成的 config.json(即带有 __generated_by_prompts__ 的那个);你手写的配置会保持上述严格跳过所有提示的行为。删除该键即可退出此行为,或完全删除 config.json 以从头开始。
重新回答提示会重写 config.json,是合并而非覆盖:你手动添加到文件中的任何键都会被保留。
要在不编辑目标文件的情况下扫描单个地址(或简短列表),请使用 --target:
./spoonmap.py --target 10.0.0.5
./spoonmap.py --target 10.0.0.0/24
./spoonmap.py --target 10.0.0.5,10.0.1.0/24,10.0.2.1-10.0.2.9
该值接受目标文件行所接受的任何内容——裸 IP、CIDR、A-B 范围或 address netmask——以逗号分隔。它在扫描开始前进行验证:格式错误或 IPv6 地址会以非零退出码中止,并指出有问题的条目,而不是在空目标集上顺利运行。
--target 优先于 config.json 的 target_file 和交互式提示,因此它也能在原本完全非交互的运行中工作。这些地址会写入 output_path 中的 cli_targets.txt;ranges.txt 永远不会被修改,因此你的交战范围文件保持完整,并且磁盘上会记录每次运行实际针对的目标。
--target=10.0.0.5(标志与值之间用 =)现在也能正常工作,与 --target 10.0.0.5 相同——--cleanup=/path/to/output 也是如此。在此工具迁移到 argparse 之前,--flag=value 形式不匹配任何手写的 '--flag' in sys.argv 检查,因此 --target=10.0.0.5 会被完全静默忽略,扫描会针对 ranges.txt 运行,而不是你实际请求的地址——这是旧 bug 唯一不只是忽略标志、而是静默改变范围的情况。
要在没有任何提示的情况下恢复中断的扫描,请使用 --resume 标志:
./spoonmap.py --resume
# or
uv run spoonmap.py --resume
--resume 会复用已完成的 主机发现 和 端口发现 结果,前提是其输出比 resolved_targets.txt 更新,加载已存在的存活主机列表,并从上次中断处继续。由于 resolved_targets.txt 仅在解析后的目标集合实际发生变化时才会被重写,因此在 ranges.txt 未变的情况下恢复运行会完全跳过发现阶段(不重新扫描)。如果自上次运行以来 ranges.txt 发生了变化,resolved_targets.txt 会以更新的时间戳被重写,任何现在早于它的发现输出——主机和端口——都会自动重新运行,以确保新添加的范围不会被遗漏。nmap banner/script 结果始终会被复用(已存在的 nmap_results/portN.xml 文件会被无条件跳过)。恢复运行也可以通过 config.json 中的 "resume": "True" 来启用。
要以非交互方式删除扫描数据,请使用 --cleanup 标志:
# Path taken from output_path in config.json
./spoonmap.py --cleanup
# or
uv run spoonmap.py --cleanup
# Or specify the directory explicitly
./spoonmap.py --cleanup /path/to/output
打印已安装的版本:
spoonmap --version
# -v and -V are accepted as short forms of --version and behave identically
spoonmap -v
spoonmap -V
版本来自已安装包的元数据,该元数据在构建时从仓库的 git 标签派生而来。直接从克隆目录运行 ./spoonmap.py 不会安装任何内容,因此会打印 unknown (running from source) —— 这是预期行为,而非错误。
要检查是否存在更新的版本:
./spoonmap.py --check-update
SpooNMAP 从不自行检查更新。 除非你明确选择启用,否则它除了扫描本身之外不会建立任何网络连接,因为它通常在客户网络内部的跳板机上运行,在这些环境中,未经提示就调用 api.github.com 会被视为来自参与主机的多余流量。--check-update 会按需执行一次检查。要在每次启动时启用该检查,请在 config.json 中设置 "check_for_updates": true;该键默认值为 false,完全省略该键也意味着 false。仅会报告稳定版本——夜间发布的候选版本绝不会作为更新进行通告。
要查看所有标志的摘要,请使用 --help(或 -h):
./spoonmap.py --help
命令行使用 Python 的 argparse 进行解析,因此无法识别的标志(例如拼写错误 --verison,或任何未在上面列出的内容)会被拒绝,并显示用法信息并以非零状态退出,而不是被静默忽略——拼写错误的标志必须终止运行,而不能落入扫描流程。如果同时给出 --version 和 --check-update,--version 优先,不执行更新检查。缩写标志(例如用 --targ 代替 --target,用 --re 代替 --resume)不被接受——半截标志是用法错误,而不是猜测,因此每个标志都必须完整拼写。