En resumen: Para encontrar claves SSH desconocidas debe revisar todos los archivos authorized_keys del servidor, no solo el de root. Averigüe con sshd -T qué archivos lee su servicio SSH, enumere las huellas digitales de cada clave y contrástelas con las claves conocidas. Asegure primero las entradas ajenas, elimínelas después y refuerce luego el acceso.
La clave que sobrevive a cualquier cambio de contraseña
Tras un incidente, muchos operadores cambian lo primero todas las contraseñas. Contra una de las puertas traseras más discretas eso no sirve: una clave pública adicional en un archivo authorized_keys. Quien posee la clave privada correspondiente inicia sesión sin contraseña, hasta que alguien encuentra la línea.
Las claves SSH desconocidas figuran por ello entre los indicios que siempre deberían revisarse tras una ejecución de código. Para PHP2Shell no hay indicadores de compromiso publicados y confirmados; la detección se basa en el análisis del comportamiento. Una clave ajena no es una prueba específica de PHP2Shell, pero sí una señal clara de que alguien quería un acceso permanente. Otros indicios los describe ¿Ya estoy comprometido?.
Dónde están las claves SSH en el servidor
Según la documentación, OpenSSH lee de forma predeterminada dos archivos en el directorio home de cada usuario: .ssh/authorized_keys y .ssh/authorized_keys2. El segundo se olvida a menudo. El ajuste puede modificarse y, con AuthorizedKeysCommand, el servidor puede incluso obtener claves de un programa externo. Por eso, compruebe primero qué rige realmente en su servidor:
# Mostrar la configuración SSH efectiva (solo lectura)
sudo sshd -T | grep -iE '^(authorizedkeysfile|authorizedkeyscommand|permitrootlogin|passwordauthentication|pubkeyauthentication)'
# Archivos de configuración adicionales y bloques Match
ls -la /etc/ssh/sshd_config.d/ 2>/dev/null
sudo grep -rn -iE '^\s*(Match|AuthorizedKeys)' /etc/ssh/
Un lugar especial es el directorio home del usuario del servidor web. En Debian y Ubuntu, para www-data suele ser /var/www. Si este directorio es escribible para PHP, el código inyectado puede crear allí un .ssh/authorized_keys. Que el inicio de sesión funcione entonces depende de la shell de inicio de sesión y de los ajustes de SSH, pero el propio archivo ya es una señal de alarma.
No toda entrada desconocida es malintencionada. Las herramientas de despliegue, los servicios de copia de seguridad o los proveedores anteriores dejan claves propias. Pero esas entradas deberían tener un propietario y una finalidad conocidos.
Revisar authorized_keys: paso a paso
Los siguientes comandos no modifican nada. Enumeran todos los archivos de claves de los usuarios y muestran la huella digital de cada clave:
# Todos los archivos authorized_keys en los directorios home de todos los usuarios
getent passwd | cut -d: -f1,6 | while IFS=: read -r user home; do
for f in "$home/.ssh/authorized_keys" "$home/.ssh/authorized_keys2"; do
[ -f "$f" ] && echo "== $user: $f" && sudo ls -la --time-style=full-iso "$f"
done
done
# Mostrar huellas digitales y comentarios de un archivo
sudo ssh-keygen -lf /root/.ssh/authorized_keys
# Otros archivos de claves fuera de los lugares habituales
sudo find / -xdev -name 'authorized_keys*' 2>/dev/null
Repase el resultado con esta lista de comprobación:
- ¿Pertenece cada clave a una persona o servicio conocido? Contraste las huellas digitales, no solo el comentario.
- ¿Tiene un usuario claves que no debería tener ninguna? Por ejemplo el usuario del servidor web, o cuentas de base de datos o de servicio.
- ¿Hay opciones antes de la clave? Las entradas con
command=ejecutan un comando fijo en cada inicio de sesión. Es legítimo para copias de seguridad, pero también un escondite. - ¿Cuándo se modificó el archivo por última vez? Una fecha de modificación posterior al 17 de octubre de 2026, el primer intento de ataque a PHP2Shell observado según informes de medios, merece especial atención. Pero las marcas de tiempo pueden falsificarse.
- ¿Coinciden propietario y permisos? Un archivo que pertenece a un usuario distinto del dueño del directorio home llama la atención.
Eliminar con seguridad las claves ajenas
Antes de borrar una línea, asegure el archivo con marca de tiempo y suma de comprobación fuera del servidor. Anote la huella digital de la clave ajena. Más adelante ayuda a saber si la misma clave aparece también en otros servidores, por ejemplo en una agencia con muchos sistemas de clientes.
Revise además los registros de inicio de sesión. En /var/log/auth.log (Debian, Ubuntu) o /var/log/secure (familia RHEL), el servicio SSH registra los inicios de sesión correctos con la huella digital de la clave utilizada. Así ve si la clave ajena ya se ha usado y desde qué dirección.
Piense también en el sentido contrario. Si en el servidor hay claves privadas, por ejemplo bajo ~/.ssh/id_* para despliegues o copias de seguridad, un atacante pudo copiarlas. Con ellas quizá alcance otros servidores, repositorios Git o destinos de copia. Tales claves se consideran agotadas tras una intrusión: generar claves nuevas y eliminar las antiguas en todos los sistemas de destino.
Una clave ajena rara vez es la única huella. Quien la depositó tenía permisos de escritura y quizá haya configurado otros caminos, como tareas cron o cuentas adicionales. Revise por ello también las tareas cron ocultas y las cuentas de administrador ajenas. Si el atacante tuvo derechos de root, reconstruir el servidor suele ser más fiable que limpiarlo.
Elimine las claves ajenas solo cuando PHP esté en un nivel corregido (8.5.12, 8.4.27, 8.3.36, 8.2.35 o backport confirmado) y se hayan reiniciado todos los workers. De lo contrario, el atacante puede volver a registrar la clave de inmediato a través de la misma vulnerabilidad.
Reforzar después el acceso SSH
Tras la limpieza compensa un breve refuerzo. La mayoría de los puntos se aplican rápido, pero deberían probarse siempre con una segunda sesión abierta, para no quedarse fuera.
| Medida | Ajuste o procedimiento |
|---|---|
| Inicio de sesión solo con clave | PasswordAuthentication no |
| No permitir el inicio de sesión directo de root | PermitRootLogin no, trabajo mediante cuentas personales con sudo |
| Desactivar el segundo archivo de claves | AuthorizedKeysFile .ssh/authorized_keys |
| Limitar las claves por origen | opción from= antes de la clave, por ejemplo para servidores de copia |
| Restringir las claves de servicio | opción restrict y command= fijo para automatización |
| Usuario del servidor web sin inicio de sesión | shell de inicio de sesión nologin, sin .ssh en el directorio home |
Lleve además una lista de todas las claves autorizadas con propietario y finalidad. Cuando un empleado o proveedor se va, su clave se elimina en todos los servidores. Sin esa lista, la próxima revisión vuelve a ser trabajo de detective. Directrices reconocidas de refuerzo para SSH contienen también los CIS Benchmarks.
¿Cómo ayuda PHP SOS en concreto?
- Averiguamos qué archivos de claves lee realmente su servicio SSH y contrastamos cada entrada con los propietarios conocidos.
- Aseguramos los hallazgos y analizamos los registros de inicio de sesión para aclarar si se utilizó una clave ajena.
- Limpiamos claves, cuentas y tareas cron en el orden correcto y cerramos antes la vulnerabilidad.
- En el marco del refuerzo del servidor aplicamos los ajustes de SSH y configuramos una gestión de claves con lista de propietarios.
Preguntas frecuentes
¿Cómo reconozco si una clave SSH es ajena?
Toda clave tiene una huella digital y, por lo general, un comentario al final de la línea. Contraste las huellas con las claves de sus empleados y proveedores. El comentario por sí solo no prueba nada, porque se puede elegir libremente y falsificar.
¿Sirve de algo cambiar la contraseña de root?
Contra una clave inyectada, no. El inicio de sesión con clave no necesita contraseña. Por eso hay que revisar y limpiar los propios archivos authorized_keys.
¿Por qué puede tener una clave SSH el usuario del servidor web?
El usuario del servidor web tiene su propio directorio home, en Debian y Ubuntu a menudo /var/www. Si allí hay un .ssh/authorized_keys y la cuenta tiene una shell de inicio de sesión, es posible iniciar sesión como ese usuario. Con un directorio web escribible, el código PHP inyectado puede crear un archivo así.
¿Qué es authorized_keys2?
Un segundo archivo más antiguo que OpenSSH también lee según la configuración predeterminada. Muchas guías de revisión lo pasan por alto. Compruebe con sshd -T qué archivos evalúa realmente su servidor.