
Параллельный инструмент обнаружения SSH-сервисов и аудитор безопасности, который сканирует любые порты, проверяет SSH-баннеры и проводит аудит методов аутентификации, слабой криптографии, уязвимости Terrapin и повторного использования ключей хостов на хостах и в CIDR-диапазонах.
Найдите каждый SSH-сервис в вашей сети, оцените, соответствует ли он вашим стандартам, и узнайте, когда это изменится.
sshfinder — это один Python-файл без обязательных зависимостей. Укажите ему
диапазон CIDR, и он обнаружит SSH везде, где он реально прослушивается — не только
на порту 22 — подтвердит, что каждый из них действительно говорит по SSH, оценит его
криптографическую позицию и вернёт ненулевой код выхода, когда что-то не соответствует вашей политике.
Большинство команд не могут ответить на три вопроса о собственном SSH-хозяйстве:
Существующие инструменты отвечают на часть этих вопросов и останавливаются:
| Инструмент | Обнаруживает SSH | Оценивает его | По всей инфраструктуре |
|---|---|---|---|
nmap | да | поверхностно, через NSE-скрипты | да |
ssh-audit | нет — вы даёте ему один хост | глубоко | нет |
masscan / zmap | в масштабе интернета | нет | да |
sshfinder | да | да | да |
Этот пробел — обнаружение и оценка и вердикт в одном артефакте — именно то,
для заполнения чего существует этот инструмент. Если вам нужно проверить только один хост, который
вы уже знаете, используйте ssh-audit; он углубляется
в один сервис глубже, чем этот.
Пентестеры найдут аудит и SOCKS-проксирование полезными, но инструмент создан для повторного запуска одного и того же сканирования против инфраструктуры, которой вы владеете, а не для разового задания.
git clone https://github.com/kabiri-labs/sshfinder.git
cd sshfinder
python sshfinder.py 10.0.0.0/24 -p 22,2222
Никакой установки, никаких зависимостей. Требуется Python 3.9+.
Три вещи, которые он делает, в трёх командах:
# 1. ИНВЕНТАРИЗАЦИЯ — какой SSH есть снаружи?
python sshfinder.py 10.0.0.0/24 --audit --format csv -o ssh-inventory.csv
# 2. ВЕРДИКТ — соответствует ли он нашему стандарту? (выход 3, если нет)
python sshfinder.py 10.0.0.0/24 -p 22,2222 --policy baseline
# 3. ДРЕЙФ — что изменилось с прошлой ночи?
python sshfinder.py 10.0.0.0/24 -p 22,2222 --baseline yesterday.json \
--fail-on-drift
Сканирование всех 65535 портов — это значение по умолчанию, потому что SSH-сервис на нестандартном порту — это именно тот, о котором никто не записал. Каждый открытый порт помечается, поэтому открытый порт никогда молча не засчитывается как SSH:
=== 10.0.0.5 ===
open: 10.0.0.5:22 [SSH], 10.0.0.5:8080 [not ssh]
SSH 10.0.0.5:22 (SSH-2.0-OpenSSH_7.4)
Подтверждение — это реальный обмен идентификацией по RFC 4253, а не взгляд на первые байты в канале. Серверы, которые сначала выводят легальный баннер, которые ждут, пока клиент представится, или чей баннер приходит разделённым по TCP-сегментам, распознаются корректно — каждый из них является ложным отрицательным результатом в наивной реализации.
Добавьте --audit для полной картины каждого сервиса:
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, …
Общие ключи хостов SSH (возможные общие/клонированные хосты):
SHA256:T/ZM4jOL4amTsO5K3AaCdg2...
-> 10.0.0.5:22, 10.0.0.9:22
Этот последний блок стоит знать: ключ хоста, используемый на нескольких машинах, обычно означает клонированные виртуальные машины или общий образ, и это означает, что компрометация одного хоста ставит под угрозу идентичность всех остальных.
OpenSSH 10.0 сделал mlkem768x25519-sha256 обменом ключей по умолчанию, а 10.1
предупреждает, что классические сеансы открыты для захвата сохрани сейчас, расшифруй позже.
--pq-report отвечает на вопрос уровня инфраструктуры напрямую, используя только
KEXINIT — поэтому ему не нужна сторонняя библиотека:
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+
Категория pre-standard — это та, которая застаёт людей врасплох. Сервер,
рекламирующий [email protected] или черновик Kyber, выглядит
постквантовым в дампе алгоритмов, но OpenSSH отказался от этого отозванного
набора параметров в 2020 году — поэтому текущий клиент не находит общего метода и возвращается
к классической криптографии. Если считать его готовым, это было бы хуже, чем вообще не смотреть.
Отчёт описывает проблему. Политика утверждает её и может провалить сборку:
python sshfinder.py 10.0.0.0/24 -p 22,2222 --policy baseline; echo "exit $?"
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
exit 3
Три политики встроены — baseline, strict и pq — названные по
результату, который они обеспечивают, а не по дистрибутиву. Правила несут серьёзность fail или
warn, а --fail-on решает, какие из них являются шлюзами, поэтому команда может принять
более строгую планку сначала как предупреждение и повысить её позже без редактирования чего-либо.
Напишите свою собственную в 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"}
]
}