- Se descubrió un caso en el que, dentro de una sesión SSH, se transmitían cientos de paquetes con una sola pulsación de tecla, y se rastreó la causa
- El análisis con
tcpdump mostró que la mayoría de los paquetes consistían en mensajes repetidos de 36 bytes, generados aproximadamente cada 20 ms
- La causa fue la función de “ofuscación del timing de las pulsaciones” (keystroke timing obfuscation) añadida a SSH en 2023, que envía múltiples paquetes “chaff” (SSH2_MSG_PING) para ocultar el momento en que el usuario escribe
- Al desactivar esta función o modificar el servidor para que no anuncie la extensión
[email protected], el uso de CPU y el ancho de banda se redujeron a menos de la mitad
- Es un caso que muestra cómo una función de seguridad de SSH puede convertirse en una carga importante en aplicaciones donde el rendimiento en tiempo real es crítico (por ejemplo, juegos)
Detección del problema
- Mientras se probaba la TUI de un juego de alto rendimiento que se ejecutaba a través de SSH, se confirmó que incluso una sola pulsación de tecla generaba 270 paquetes
- Según
tcpdump, el 66% eran mensajes de 36 bytes, el 33% eran TCP ACK y el resto correspondía a una pequeña cantidad de otros datos
- En promedio se transmitían 90 paquetes por segundo, con intervalos de unos 11 ms
- Durante las pruebas, el servidor quedó mal configurado para enviar solo el mensaje “your screen is too small”, y en ese momento el uso de CPU y de ancho de banda se redujo a la mitad
- Como no debería haberse transmitido información del juego, el uso de CPU tendría que haber estado cerca de 0%, pero seguía alrededor del 50%
- Esto llevó a plantear la posibilidad de un overhead de comunicación propio de SSH
Proceso de investigación
- Se comparó el tráfico SSH en funcionamiento normal y en estado de error usando
tcpdump
- Incluso en el estado de error, los paquetes de 36 bytes seguían apareciendo cada 20 ms
- El mismo patrón también se observó en el cliente SSH por defecto de macOS
- Al analizar el archivo pcap con Claude Code
- De un total de 413,703 paquetes, el 66% eran de 36 bytes y el 34% eran ACK de 0 bytes
- El cliente SSH estaba generando activamente los paquetes
Causa raíz
Solución
- En el cliente se puede desactivar la función con la opción
ObscureKeystrokeTiming=no
- Tras aplicarla, el uso de CPU y de ancho de banda se redujo notablemente, mientras la transmisión de datos siguió funcionando con normalidad
- Como respuesta del lado del servidor, se eliminó de la biblioteca SSH de Go el anuncio de la extensión
[email protected]
- Tras revertir el commit relacionado y hacer pruebas, se obtuvo lo siguiente
- Uso de CPU 29.9% → 11.6% ,
llamadas al sistema 3.10s → 0.66s,
operaciones criptográficas 1.6s → 0.11s,
ancho de banda 6.5Mbit/s → 3Mbit/s
- El rendimiento mejoró en más de un 50%
Experiencia de depuración con ayuda de LLM
- Usando Claude Code para automatizar el análisis de
tcpdump y tshark, se pudo acotar rápidamente la causa del problema
- Al observar en tiempo real la ejecución de los comandos, fue posible mantener un modelo mental del proceso
- También se experimentaron diferencias entre modelos, como cuando ChatGPT evaluó incorrectamente el comportamiento de SSH como “normal”
- Aunque los LLM no sustituyen todo el proceso de resolución de problemas, mostraron alta eficiencia como herramienta auxiliar de análisis
- Es un caso de resolución de un problema complejo de rendimiento de red combinando el razonamiento humano con el análisis de un LLM
1 comentarios
Comentarios de Hacker News
Me da un poco de miedo hacer un fork de la biblioteca crypto de Go
Estoy pensando cómo mantener segura mi pequeña corrección
En realidad, creo que una función así debería integrarse upstream como una opción de la biblioteca SSH
En entornos no confiables, enviar paquetes de chaff (ruido) como valor predeterminado suena bien, pero muchas veces también se quiere ahorrar ancho de banda
La solución correcta sería agregar una opción para que el servidor pueda indicarle al cliente que “no hace falta”, y que el cliente pueda aceptarlo o advertirlo
Solo se aplica en sesiones TTY, y el cliente puede desactivarlo
Solo que este caso es una situación excepcional donde el servidor ya sabe de antemano que la conexión no es importante
En la mayoría de los casos, el cliente espera que se respete la configuración ObscureKeystrokeTiming
La biblioteca crypto es una base de código con opiniones muy fuertes, al punto de que ni siquiera te deja cambiar el orden de las cipher suites de TLS
Esto parece un caso de uso muy específico de SSH
Si se expone demasiado, puede terminar en una situación de “configúralo y olvídalo” que debilite la seguridad
También me tocó comunicarme con módems de 1200 bps, y los módems de 56K en realidad eran prácticamente una exageración
Hacia 1994 trabajaba en una academia militar del Reino Unido y conocí la WWW por primera vez, pero en ese momento pensé: “qué floja”
Viéndolo ahora, de verdad sorprende cómo cambian los tiempos
No conocía esta función de ofuscación, me pareció interesante
Al depurar el comportamiento de SSH, también es buena idea parchear el cipher
Nonepara ver directamente el contenido de los paquetesSi se trata de un juego de terminal donde la seguridad no importa y el rendimiento sí, hasta valdría la pena considerar usar telnet
No sabía que SSH hacía esto
Entiendo por qué viene activado por defecto, pero en mi entorno probablemente convenga desactivarlo
Así que pienso poner
ObscureKeystrokeTiming=no. ¿Hay alguna razón por la que no debería hacerlo?(1) No siempre es fácil distinguir cuándo una persona está escribiendo un secreto, y toda la actividad puede analizarse por patrones
(2) Este es un ataque posible incluso a nivel de laboratorio universitario — ver el paper de USENIX y este caso de investigación
(3) En un internet dominado por tráfico de video, renunciar a seguridad por ahorrar unos pocos bytes de pulsaciones no tiene sentido
Si un atacante analiza el timing de las pulsaciones, podría inferir tanto el comando como el patrón de la contraseña cifrada
Claro, como la clave de sesión cambia cada vez, descifrarlo sería difícil, pero la posibilidad existe
Yo también copio y pego la mayoría de mis contraseñas desde un gestor de contraseñas
La mayoría de la gente siente que no pasa nada aunque desactive funciones de seguridad de SSH, pero eso solo significa que ha tenido suerte
Si de verdad necesitas rendimiento, usa Telnet; si de verdad necesitas seguridad, conviene una combinación de ContainerSSH + OAuth2
En 2004 hice investigación para inferir comandos analizando la latencia entre pulsaciones en sesiones SSH
Véase este análisis de la época
El parche de 2023 finalmente vino a resolver ese problema
Material de la presentación
De verdad pasa muy rápido el tiempo
No estoy seguro de que Claude haya ayudado de verdad en la depuración
El autor ya sabía hacia dónde iba, y Claude solo parecía seguirle la corriente
Que Claude diga cosas como “Holy Cow!” me resulta un poco molesto
A mí también me pasa que al depurar cómo funciona un sistema con Claude, aunque no me dé una respuesta directa, sí me ayuda a ordenar la comprensión y mantener la motivación
Wiki de Rubber Duck Debugging
Si el autor puso en su blog una reacción como “holy cow”, parece que Claude leyó bien el ambiente
Usar TCP_CORK permite reducir la cantidad de paquetes sin introducir latencia
Desactivar TCP_NODELAY también es una opción, pero el costo es más latencia
Al hacer cork en el socket, el kernel guarda los datos en buffer y los envía cuando se hace uncork o se llega al MSS
Es decir, agrupa paquetes antes de enviarlos
Referencia
Seguiría recibiendo ping, pero quizá podría reducir la cantidad de envíos de pong
Ya probé TCP_NODELAY, pero aumentó demasiado la latencia y no me sirvió para mi juego
Publicación anterior en HN
Con fines de ofuscación, probablemente no se pueda hacer coalescing de latencia
La expresión “The smoking gun!” me dio risa
No soy hablante nativo de inglés, pero Claude la usa seguido y la aprendí así
Ahora de verdad se está propagando como muletilla
Me decepciona la dependencia de los LLM
Esto probablemente se habría resuelto más rápido viendo una captura de paquetes en Wireshark
El dissector de SSH está bastante maduro
Incluso si capturas una sola pulsación con tcpdump, salen cientos de paquetes cifrados
Al final, gracias al LLM el autor aprendió algo interesante y lo compartió, así que tuvo sentido
Después del mensaje NEWKEYS ya no puede parsear, e incluso parcheando cifrado
noneno interpreta completamente el flujoHay margen de mejora
Aprender usando herramientas también tiene valor
Con una simple captura de paquetes es difícil obtener información significativa
No veo por qué eso sería algo lamentable
En 2023 SSH agregó una función de ofuscación del timing de pulsaciones
Como es posible inferir caracteres por la velocidad de tecleo, SSH mezcla paquetes de chaff para que el atacante no pueda distinguirlos
Pero esto parece un enfoque equivocado
Si de verdad se quisiera, bastaría con enviar todas las pulsaciones a intervalos de 50 ms
La implementación actual agrupa en unidades de 20 ms, pero deja de enviar chaff después de cierto tiempo sin actividad
El punto central de SSH es la seguridad, pero si no necesitas seguridad, queda la duda de por qué usar SSH
Por ejemplo, netcat(nc) viene instalado por defecto en la mayoría de las plataformas
En SSH también hay otras consideraciones como rendimiento y comodidad
El autor solo dijo que no necesita la función de ofuscación de pulsaciones (privacy)
Puede seguir queriendo cifrado e integridad
O sea, conservar la mayor parte de las funciones de seguridad de SSH y desactivar solo una parte