Ejecución remota de código (RCE): cómo se llega y cómo se evita
Guía en castellano sobre ejecución remota de código (RCE): vías habituales como deserialización, inyección de comandos o subida de ficheros, y cómo se previenen.
Qué es
Una ejecución remota de código (RCE, Remote Code Execution) significa que el atacante consigue que el servidor, o el programa de la víctima, ejecute instrucciones suyas. Es casi siempre el escenario más grave: desde ahí se pueden leer datos, robar credenciales o moverse a otros sistemas.
Rara vez es un fallo aislado. Suele ser el final de una cadena que empieza con otra vulnerabilidad.
Vías habituales
- Inyección de comandos: un dato del usuario acaba en una orden del sistema (
ping,convert,git…) sin separarse de ella. - Deserialización insegura: la aplicación reconstruye objetos a partir de datos que controla el atacante (por ejemplo con
pickleen Python, la serialización nativa de Java ounserializeen PHP). - Inyección de plantillas en el servidor (SSTI): el texto del usuario se procesa como plantilla en lugar de como dato.
- Subida de ficheros: un fichero ejecutable que el servidor guarda en una ruta desde la que lo interpreta.
- Dependencias vulnerables: librerías o servicios con fallos conocidos que no se han actualizado.
- Procesado de ficheros: imágenes, documentos o archivos comprimidos tratados por programas con fallos de memoria.
Cómo se busca
- Identifica dónde la aplicación llama a programas externos, procesa ficheros o convierte formatos.
- Revisa las versiones de software expuestas y busca fallos públicos que les afecten.
- Prueba entradas con separadores de comandos o sintaxis de plantillas y observa si el resultado cambia.
- Para demostrarlo usa una orden inocua que pruebe la ejecución sin tocar nada, como
ido una petición a un servidor propio.
Antes de ir más allá, lee las reglas del programa: muchos piden detenerse en cuanto la ejecución queda demostrada.
Cómo se evita
- No llamar al intérprete de órdenes con datos del usuario. Usar APIs que pasen los argumentos por separado.
- No deserializar datos no confiables con formatos que puedan crear objetos arbitrarios. Preferir JSON con un esquema fijo.
- Guardar los ficheros subidos fuera de rutas ejecutables y con nombres generados por el servidor.
- Mantener las dependencias al día y ejecutar los servicios con los mínimos privilegios, aislados del resto.
Al reportarlo
Una prueba mínima y limpia vale más que una demostración espectacular. Explica la cadena completa y qué privilegios obtiene el código ejecutado.
Casos reales de RCE
Ver los 11 →DuckDuckGo: un workflow con pull_request_target permitía ejecutar código y envenenar los scripts de todos sus navegadores
Un workflow de GitHub Actions de content-scope-scripts ejecutaba código de PR ajenas con acceso a secretos. Cualquiera podía robar una clave de API y acabar colando una versión maliciosa en los navegadores de DuckDuckGo.
3 min
DLL side-loading en Sony Music Center for PC 2.7.2: ejecución de código vía una DLL del PATH
Sony Music Center for PC 2.7.2 buscaba al arrancar una biblioteca inexistente, z-bes.dll, por las carpetas del PATH. Quien pudiera dejar ahí un archivo con ese nombre lograba que la aplicación cargara y ejecutara su código.
2 min
DuckDuckGo: un workflow con pull_request_target permitía ejecutar código y robar un PAT de privacy-configuration
Un workflow de GitHub Actions de duckduckgo/privacy-configuration ejecutaba código de forks con acceso a secretos, lo que permitía robar un token capaz de aprobar cambios en la configuración de privacidad de todos los navegadores de DuckDuckGo.
3 min
Ejecución de scripts Painless en Elasticsearch a través del argumento sort_query del GraphQL de HackerOne
El argumento `sort_query` de `Query.search` en el GraphQL de HackerOne llegaba sin validar a Elasticsearch, lo que permitía a una cuenta autenticada ejecutar scripts Painless por documento dentro del clúster.
6 min
RCE sin autenticación en Taskcluster de Mozilla a través del argumento filter de GraphQL (sift $where)
Un argumento filter de GraphQL sin validar llegaba a la librería sift, que compilaba con new Function el operador $where. Una única petición POST anónima ejecutaba JavaScript en el servidor y exponía todos los secretos del entorno.
5 min
Inyección de comandos en install_packages() del SDK de AWS Bedrock AgentCore vía flags de pip
Una validación por lista negra incompleta en install_packages() del SDK de Bedrock AgentCore permitía inyectar flags de pip como --index-url o -r, logrando lectura de ficheros, SSRF y ejecución de comandos en el sandbox del Code Interpreter.
4 min