kabiri-labs/sshfinder

GitHub: kabiri-labs/sshfinder

sshfinder 是一个快速并行的 SSH 服务发现与安全审计工具,支持跨主机和 CIDR 范围扫描任意端口上的 SSH 服务,并标记认证方法、弱加密算法、Terrapin 漏洞及共享主机密钥。

Stars: 2 | Forks: 0

# sshfinder [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/kabiri-labs/sshfinder/actions/workflows/ci.yml) ![version](https://img.shields.io/badge/version-2.10.0-blue) `sshfinder` 是一个快速、可靠的工具,用于在一个或多个目标中发现开放的 **SSH** 服务。 它会扫描开放的 TCP 端口,然后确认其中哪些端口实际上在使用 SSH —— 这两个阶段会并发执行以提升速度。 ## 功能 - **多目标** — 在单次运行中扫描多个 IP、主机名和 CIDR 网络(例如 `10.0.0.0/24`),或者从文件中加载它们。 - **非阻塞扫描引擎** — 每个进行中的连接都通过操作系统事件循环(`epoll`/`kqueue`/`select`)由单个线程驱动,因此并发成本仅相当于一个文件描述符,而非操作系统线程。完整的 1–65535 端口扫描速度比之前的线程池引擎快约 6 倍,并且目标主机只需解析一次,而不是每个端口解析一次。 - **SSH 端口优先** — SSH 实际驻留的少数几个端口(22、2222、22222……)会在每次扫描的最开始进行探测,因此即使扫描覆盖了所有 65535 个端口,通常也能在远低于一秒的时间内确认服务。 - **自适应超时** — 探测会根据网络路径的实际情况等待,而不是固定等待两秒。来自 RFC 6298 的平滑往返时间估算器 —— 也就是 TCP 本身使用的算法 —— 会根据每个被响应的探测进行调整,并在整个扫描过程中共享,因此 `--timeout` 变成了一个上限,而不是固定成本。在存活但大部分端口被过滤的主机上,这在发现结果相同的情况下能带来约 5 倍的速度提升(扫描 8000 个端口从 34 秒降至 6 秒);使用 `--no-adaptive-timeout` 可恢复固定的等待时间。 - **两种扫描后端**: - `connect` — 便携的 TCP connect 扫描,无需特殊权限(默认)。 - `syn` — 通过 Scapy 进行的半开 SYN 扫描(速度更快,需要 root 权限)。 - **可靠的 SSH 验证** — 完整执行 RFC 4253 的身份识别交换,而不是仅仅查看线路上的前几个字节:无论是先打印法律声明横幅的服务器,还是等待客户端先表明身份的服务器,亦或是横幅被拆分在多个 TCP 段中到达的情况,都能被正确识别。同时也提供可选的完整 Paramiko 握手验证。 - **流式输出 (`--stream`)** — 在 stdout 上输出以换行符分隔的 JSON 事件,在每个端口开放和每个 SSH 服务被确认时即刷新输出,使得流水线可以在扫描仍在运行时就对第一个结果采取行动。 - **SSH 安全审计 (`--audit`)** — 将发现转化为渗透测试人员的攻击面情报:枚举接受的认证方法(并标记密码认证),列出提供的 KEX/加密/MAC/主机密钥算法并标记脆弱/已弃用的算法,检测 **Terrapin (CVE-2023-48795)**,并**关联跨目标的共享主机密钥**,以揭示克隆或负载均衡的基础设施。 - **后量子准备度 (`--pq-report`)** — 为整个资产提供一个指标:有多少 SSH 服务仍然无法协商后量子密钥交换,以及具体是哪些。OpenSSH 10.0 将 `mlkem768x25519-sha256` 设为默认选项,而 10.1 则警告经典会话容易受到*现在截获,以后解密*的威胁。它只读取 KEXINIT,因此不需要第三方库。它还会区分提供**标准化前**混合算法(已撤销的 `sntrup4591761`、Kyber 草案)的服务 —— 这些算法在转储列表中看起来像后量子算法,但在与当前所有客户端协商时使用的却是经典加密。 - **精心维护的算法评估** — 每个被标记的密钥交换、加密、MAC 和主机密钥均来自一个明确的表格,该表格包含严重级别(`critical` 或 `weak`)和具体原因,而不是一连串的子字符串匹配测试。名称会首先进行规范化处理,因此供应商的后缀无法让某种算法逃避检查 —— 无论由谁提供,`rijndael-cbc@lysator.liu.se` 始终是 CBC。诸如 `kex-strict-s-v00@openssh.com` 的协商标记也绝不会被视为算法进行评估。策略门控和基线比较最终都依赖于该表格。 - **策略门控 (`--policy`)** — 根据基线检查每个 SSH 服务,并在**违规时以非零状态退出**,从而使得扫描可以用于拦截 CI 或定时任务。内置了 `baseline`、`strict` 和 `pq`,或者可以使用自定义的 JSON 策略。规则带有 `fail`/`warn` 严重级别,并由 `--fail-on` 决定哪一种级别用于拦截。未识别的检查、字段或严重级别将被视为硬错误,绝不会是静默跳过的规则 —— 错字绝不能让一个未通过检查的资产显示为合格。 - **基线比较 (`--baseline`)** — 每晚运行该命令并与昨天的 `--json` 报告进行对比,仅查看发生变动的部分:**主机密钥发生改变**(最重要的信号 —— 通常仅在系统重建或密钥轮换后预期发生)、密码登录被新启用、加密被削弱、后量子准备度丧失、服务与端口的出现或消失。仅对在*两次*扫描中都存在的主机进行比较,且任何一次扫描都未测量的字段绝不会报告为变更,因此扫描一个机柜不会导致其他所有机柜被视作下线。 - **零必需依赖** — 默认的 connect 扫描和横幅验证仅依靠 Python 标准库即可运行。 - **设计上的边界限制** — 源自文件描述符限制的全局 socket 预算可防止大型扫描耗尽描述符,并避免将存活服务误报为被过滤;同时,目标扩展机制会拒绝实现大于 `--max-targets` 设定范围的扫描。 - **死机主机的提前退出** — 如果某台主机在最初几百次探测中没有任何响应,将被报告为无响应,而无需为每个剩余端口消耗一次超时时间。因为 SSH 的端口会被优先扫描,所以总能最先发现存活服务;`--no-early-exit` 可强制执行全范围扫描。 - **流水线化的身份识别** — 每个开放端口背后的服务会在发现端口的瞬间(与端口扫描的其余部分并发)被识别(并审计)。SSH 服务无需等待整个扫描完成即可被确认,并且每个开放端口都会被标记为 `[SSH]` / `[not ssh]`,因此开放端口绝不会与 SSH 端口混淆。 - **实时的单 socket 发现** — 开放端口和确认的 SSH 服务会在被发现的瞬间以 `host:port` 的格式打印出来,因此在扫描多台主机时,始终可以清楚地分辨哪个结果属于哪个目标。 - **实时进度与稳健的 Ctrl+C** — 实时进度指示器会显示扫描正在进行中。即使在 Windows 上(通常无限制的线程等待会吞掉该信号),Ctrl+C 也能被响应:第一次按下将优雅地停止并返回部分结果,第二次按下则会强制立即退出。 - **清晰的主机状态** — 区分开放、关闭和*被过滤 (filtered)* 的端口,因此被防火墙拦截或不可达的主机会被如实报告,而不是看起来像程序卡死。 - **机读输出** — `--format text|json|sarif|csv`,可选择写入文件。面向安全工具的 **SARIF 2.1.0**(已通过 OASIS 模式验证;发现结果锚定于 `host:port` 逻辑位置,并带有稳定的指纹,使得使用者可以在多次运行中追踪同一个问题)。面向电子表格的 **CSV**:每个确认的 SSH 服务占据一行,这正是资产清册实际进行过滤和排序时所需的格式。 ## 安装 ``` git clone https://github.com/kabiri-labs/sshfinder.git cd sshfinder # 安装 paramiko(推荐:启用完整的 --audit 深度检查和 # --validate paramiko): pip install -r requirements.txt # 可选,仅用于半开 SYN 扫描(需要 root): pip install scapy>=2.5 ``` 核心的 connect 扫描、横幅验证以及 `--audit` 的无依赖部分(算法清单、弱加密标记、Terrapin)无需任何第三方软件包即可运行;而 paramiko 则用于解锁主机密钥指纹、认证方法枚举和共享密钥关联。 要求 **Python 3.9+**。 ## 用法 ``` python sshfinder.py [targets ...] [options] ``` ### 选项 | 选项 | 描述 | | ------ | ----------- | | `targets` | 一个或多个 IP、主机名或 CIDR 网络。 | | `-iL, --target-file FILE` | 从文件中读取目标(每行一个,允许使用 `#` 添加注释)。 | | `-p, --ports SPEC` | 要扫描的端口,例如 `22,80,1000-2000`(默认:`1-65535`)。 | | `--scan-method {auto,connect,syn}` | 扫描后端(默认:`auto`)。 | | `--validate {banner,paramiko,none}` | SSH 验证策略(默认:`banner`)。 | | `--audit` | 审计每个 SSH 服务(算法、主机密钥、认证方法、Terrapin、后量子准备度、共享密钥关联)。 | | `--pq-report` | 报告整个资产的后量子密钥交换准备度。仅读取 KEXINIT —— 无需第三方库。 | | `--policy NAME_OR_PATH` | 根据策略(`baseline`、`strict`、`pq` 或 JSON 文件)检查每个 SSH 服务,并在违规时以 `3` 退出。 | | `--fail-on {fail,warn,never}` | 哪种策略严重级别用于拦截退出状态码(默认:`fail`)。 | | `--baseline FILE` | 与之前的 `--json` 报告进行对比,并列出更改的内容。 | | `--fail-on-drift` | 当对比产生警报时以 `4` 退出。服务的出现或消失不会触发拦截。 | | `-t, --timeout SECONDS` | 探测的最长等待时间(默认:`2.0`)。 | | `--min-timeout SECONDS` | 自适应探测超时的下限(默认:`0.1`)。 | | `--no-adaptive-timeout` | 对每个探测等待完整的 `--timeout` 时间,而不是根据测量的往返时间进行自适应调整。 | | `-w, --workers N` | 每台主机的并发连接数(默认:`512`)。 | | `--max-sockets N` | 整个扫描过程中同时打开的探测 socket 数量上限(默认:从文件描述符限制中推导得出)。 | | `--host-concurrency N` | 并行扫描的主机数量(默认:`16`)。 | | `-r, --retries N` | 超时探测的重试次数(默认:`0`)。 | | `--max-targets N` | 拒绝大于此数量的目标列表(默认:`65536`)。 | | `--no-early-exit` | 即使主机完全没有响应,也扫描所有端口。 | | `--format {text,json,sarif,csv}` | 输出格式(默认:`text`)。 | | `--json` | `--format json` 的简写。 | | `--stream` | 随着结果的发现,在 stdout 上输出以换行符分隔的 JSON 事件。 | | `-o, --output FILE` | 将结果写入文件而不是 stdout。 | | `--no-progress` | 禁用实时进度指示器。 | | `-v` | 详细的(调试)日志。 | | `-q, --quiet` | 抑制进度和信息类日志。 | ### 退出代码 | 代码 | 含义 | | ---- | ------- | | `0` | 成功。什么都没找到也算成功 —— 空资产不是错误。 | | `1` | 硬错误:所有目标均扫描失败,或无法写入输出文件。 | | `2` | 错误的调用(未知的标志、无效的端口范围、格式错误的策略)。 | | `3` | 达到或超过 `--fail-on` 设定的策略违规。仅在使用 `--policy` 时返回。 | | `4` | 基线漂移警报。仅在使用 `--baseline --fail-on-drift` 时返回。 | | `130` | 被 Ctrl+C 中断。 | 硬错误的优先级高于策略判定,而策略判定的优先级高于漂移:如果没有任何目标可达,说明扫描未能证明任何关于合规性的问题,因此会返回 `1` 而不是误导性的通过或失败;并且未达到规定的基线要求是比“某些内容发生了变化”更具体的发现。 `auto` 会在以 root 身份运行且安装了 Scapy 时选择 SYN 扫描,否则将回退到不需要权限的 connect 扫描。 ## 示例 在常见的 SSH 端口上扫描单个主机: ``` python sshfinder.py 192.168.1.1 -p 22,2222 ``` 扫描整个子网并输出 JSON: ``` python sshfinder.py 10.0.0.0/24 -p 22,2222 --json -o results.json ``` 使用快速的 SYN 扫(以 root 身份)扫描来自文件的多个目标: ``` sudo python sshfinder.py -iL targets.txt --scan-method syn ``` 通过完整的握手严格验证 SSH: ``` python sshfinder.py example.com -p 22 --validate paramiko ``` 在发现结果时将其流式传输到流水线中,无需等待扫描完成: ``` python sshfinder.py 10.0.0.0/24 --stream -q | jq -c 'select(.event=="ssh")' ``` ``` {"event":"ssh","elapsed":0.164,"host":"10.0.0.5","port":22,"banner":"SSH-2.0-OpenSSH_9.6"} {"event":"ssh","elapsed":0.881,"host":"10.0.0.9","port":2222,"banner":"SSH-2.0-dropbear"} ``` 跨子网审计 SSH 攻击面(认证方法、弱加密、Terrapin、共享主机密钥): ``` python sshfinder.py 10.0.0.0/24 -p 22,2222 --audit ``` 检查整个资产的后量子准备度(无需额外依赖): ``` python sshfinder.py 10.0.0.0/24 -p 22,2222 --pq-report ``` ``` Post-quantum readiness: 1/3 service(s) negotiate post-quantum key exchange with a current client [!] no PQ key exchange offered (1): 10.0.0.2:22 [!] pre-standard PQ only (1) - looks post-quantum but is not: 10.0.0.3:22 2 service(s) exposed to store-now-decrypt-later capture; upgrade to OpenSSH 9.0+ (10.0+ preferred) ``` 将 CI 任务或定时扫描交由基线进行拦截 —— 如果任何服务违规,则以 `3` 退出: ``` python sshfinder.py 10.0.0.0/24 -p 22,2222 --policy baseline ``` ``` Policy 'baseline': No password login, no Terrapin exposure, no weak algorithms. 1/3 service(s) pass [FAIL] 1 service(s): 10.0.0.3:22 - password_auth: password login accepted: publickey, password - terrapin: vulnerable to Terrapin (CVE-2023-48795) - post_quantum (warn): post-quantum readiness is absent, ready required [warn] 1 service(s): 10.0.0.2:22 - post_quantum (warn): post-quantum readiness is absent, ready required ``` 编写自己的 JSON 策略: ``` { "name": "house-rules", "description": "What we expect of every SSH service.", "rules": [ {"check": "password_auth", "severity": "fail"}, {"check": "terrapin", "severity": "fail"}, {"check": "post_quantum", "require": "ready", "severity": "warn"}, {"check": "forbid", "field": "ciphers", "algorithms": ["3des-cbc", "arcfour"], "severity": "fail"}, {"check": "require", "field": "kex_algorithms", "algorithms": ["curve25519-sha256"], "severity": "fail"} ] } ``` 可用的检查项目包括:`password_auth`、`terrapin`、`weak_algorithms`、`post_quantum`(配合 `require`:`ready`、`legacy`、`absent`),以及针对 `kex_algorithms`、`host_key_algorithms`、`ciphers` 或 `macs` 某个 `field` 的 `forbid` / `require`。在策略加载时,其他任何内容都会被拒绝。 将 SSH 清单导出到电子表格,每个服务占据一行: ``` python sshfinder.py 10.0.0.0/24 -p 22,2222 --audit --format csv -o ssh.csv ``` 为安全工具输出 SARIF 2.1.0: ``` python sshfinder.py 10.0.0.0/24 -p 22,2222 --policy baseline --format sarif \ -o sshfinder.sarif ``` 每个发现结果都锚定在一个 `host:port` 的**逻辑位置**上 —— 这是 SARIF 中专为不与源文件绑定的结果设计的部分 —— 并且包含一个稳定的 `partialFingerprints` 条目,使得使用者可以在多次运行中追踪同一个发现,而不是每天晚上都触发一个新的警报。当提供了 `--policy` 时,策略违规*即是*发现的结果;反之,则报告内置的审计发现。无论哪种方式,每个结果只会出现一次。 随着时间推移跟踪资产 —— 先捕获一份报告,然后与之对比: ``` # 每晚在 cron 中运行: python sshfinder.py 10.0.0.0/24 -p 22,2222 --audit --json -o today.json python sshfinder.py 10.0.0.0/24 -p 22,2222 --baseline yesterday.json \ --fail-on-drift ``` ``` Baseline drift (vs yesterday.json): [alert] 2 change(s): 10.0.0.5:22 SHA256:T/ZM4jO... -> SHA256:9aKm2Qx...; expected only after a rebuild or key rotation 10.0.0.3:22 password login is now accepted [added] 1 change(s): 10.0.0.9:2222 new SSH service (SSH-2.0-OpenSSH_9.6) [improved] 1 change(s): 10.0.0.7:22 post-quantum readiness rose from absent to ready ``` 比较过程会自动匹配基线的深度:如果保存的基线包含主机密钥指纹,则本次扫描也会执行深度探测,因此浅层的重新扫描绝不会显示为所有的密钥都消失了。 审计输出示例: ``` === 10.0.0.5 === open: 10.0.0.5:22 SSH 10.0.0.5:22 (SSH-2.0-OpenSSH_7.4) host key: ssh-ed25519 SHA256:T/ZM4jOL4amTsO5K3AaCdg2... auth: publickey, password [!] password auth enabled [!] Terrapin (CVE-2023-48795): VULNERABLE [!] weak ciphers: aes128-cbc aes128-cbc [weak]: CBC mode is vulnerable to the SSH plaintext-recovery attack (CVE-2008-5161) and, with Encrypt-then-MAC, to Terrapin Shared SSH host keys (possible shared/cloned hosts): SHA256:T/ZM4jOL4amTsO5K3AaCdg2... -> 10.0.0.5:22, 10.0.0.9:22 ``` 算法清单、弱加密标记和 Terrapin 检查无需任何依赖即可工作。主机密钥指纹、认证方法枚举和共享密钥关联使用 Paramiko(`pip install paramiko`)。 ## 开发 测试套件仅需标准库,因此可以在纯净的解释器上运行: ``` python -m unittest discover -s tests ``` 安装运行时依赖还可以运行基于 Paramiko 的审计测试,如果缺少 Paramiko,这些测试会自动跳过: ``` pip install -r requirements-dev.txt python -m unittest discover -s tests ``` ## 法律声明 仅扫描您拥有或被明确授权测试的系统。未经授权的扫描在您所在的司法管辖区可能是违法的。
标签:Python, SSH, 云存储安全, 插件系统, 无后门, 漏洞审计, 网络扫描, 逆向工具