
Herramienta de descubrimiento de servicios SSH en paralelo y auditor de seguridad que escanea cualquier puerto, valida banners SSH y audita métodos de autenticación, criptografía débil, vulnerabilidad Terrapin y claves de host reutilizadas entre hosts y rangos CIDR.
Encuentra cada servicio SSH en tu red, determina si cumple con tu estándar, y recibe una notificación cuando eso cambie.
sshfinder es un único archivo Python sin dependencias requeridas. Apúntalo a
un rango CIDR y descubre SSH dondequiera que esté realmente escuchando — no solo
en el puerto 22 — confirma que cada uno realmente habla SSH, evalúa su postura
criptográfica, y devuelve un código de salida distinto de cero cuando algo
falla en tu política.
La mayoría de los equipos no pueden responder tres preguntas sobre su propio parque SSH:
Las herramientas existentes responden cada una a parte de esto y se detienen:
| Herramienta | Descubre SSH | Lo evalúa | En toda una flota |
|---|---|---|---|
nmap | sí | superficial, mediante scripts NSE | sí |
ssh-audit | no — le das un solo host | en profundidad | no |
masscan / zmap | a escala de internet | no | sí |
sshfinder | sí | sí | sí |
Esa brecha — descubrimiento y evaluación y un veredicto, en un solo artefacto — es
lo que esta herramienta existe para llenar. Si solo necesitas auditar un host que ya
conoces, usa ssh-audit; profundiza
más en un solo servicio que esto.
Los testers de penetración encontrarán útiles la auditoría y el pivote SOCKS, pero la herramienta está diseñada en torno a ejecutar el mismo escaneo repetidamente contra un parque que posees, no en torno a un compromiso puntual.
git clone https://github.com/kabiri-labs/sshfinder.git
cd sshfinder
python sshfinder.py 10.0.0.0/24 -p 22,2222
Sin instalación, sin dependencias. Requiere Python 3.9+.
Las tres cosas que hace, en tres comandos:
# 1. INVENTARIO — ¿qué SSH hay ahí fuera?
python sshfinder.py 10.0.0.0/24 --audit --format csv -o ssh-inventory.csv
# 2. VEREDICTO — ¿cumple con nuestro estándar? (sale con 3 si no)
python sshfinder.py 10.0.0.0/24 -p 22,2222 --policy baseline
# 3. DERIVA — ¿qué cambió desde anoche?
python sshfinder.py 10.0.0.0/24 -p 22,2222 --baseline yesterday.json \
--fail-on-drift
Escanear los 65535 puertos es el valor predeterminado, porque un servicio SSH en un puerto no estándar es precisamente el que nadie ha documentado. Cada puerto abierto está etiquetado, por lo que un puerto abierto nunca se cuenta silenciosamente como uno 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)
La confirmación es un intercambio de identificación RFC 4253 real, no un vistazo a los primeros bytes en el cable. Los servidores que imprimen un banner legal primero, que esperan a que el cliente se identifique, o cuyo banner llega dividido entre segmentos TCP se reconocen correctamente — cada uno de esos es un falso negativo en una implementación ingenua.
Añade --audit para la imagen completa de cada servicio:
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, …
Claves de host SSH compartidas (posibles hosts compartidos/clonados):
SHA256:T/ZM4jOL4amTsO5K3AaCdg2...
-> 10.0.0.5:22, 10.0.0.9:22
Ese último bloque vale la pena conocerlo: una clave de host reutilizada entre máquinas normalmente significa VMs clonadas o una imagen compartida, y significa que comprometer un host compromete la identidad de todos ellos.
OpenSSH 10.0 hizo de mlkem768x25519-sha256 el intercambio de claves predeterminado, y 10.1
advierte que las sesiones clásicas están abiertas a la captura de almacena ahora, descifra después.
--pq-report responde la pregunta a nivel de flota directamente, usando solo el
KEXINIT — por lo que no necesita ninguna biblioteca de terceros:
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+
La categoría pre-standard es la que atrapa a la gente. Un servidor
que anuncia [email protected] o un borrador de Kyber parece
post-cuántico en un volcado de algoritmos, pero OpenSSH eliminó ese conjunto de
parámetros retirado en 2020 — por lo que un cliente actual no encuentra ningún método común y
vuelve a la criptografía clásica. Contado como listo, sería peor que no mirar
en absoluto.
Un informe describe un problema. Una política afirma uno, y puede hacer fallar una compilación:
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
Tres políticas vienen integradas — baseline, strict y pq — nombradas por el
resultado que imponen en lugar de por una distribución. Las reglas llevan una severidad
fail o warn y --fail-on decide qué compuertas, por lo que un equipo puede adoptar
un estándar más estricto como advertencia primero y promoverlo después sin editar nada.
Escribe las tuyas propias como 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"}
]
}