Ofuscation guide
Guía sobre ofuscación
Certificaciones
- eWPT
- eWPTXv2
- OSWE
- BSCP
Descripción
Explicación de ténicas de ofuscación
. Detallamos cómo realizar diferentes tipos de ofuscaciones
y explicamos varios casos de uso
Ofuscar ataques mediante encoding
En esta sección, mostraremos cómo podemos aprovechar la decodificación estándar
que realizan los sitios web
para evadir filtros de entrada
e inyectar payloads
en distintos ataques
Decodificación específica del contexto
Tanto del lado del cliente
como del servidor
se usan distintos tipos de codificaciones
para transmitir datos
entre sistemas
. Cuando quieren usar
realmente los datos
, a menudo deben decodificarlos
primero. La secuencia exacta de pasos de decodificación depende del contexto
en el que aparecen los datos. Por ejemplo, un parámetro de consulta
normalmente se decodifica por URL
en el servidor
, mientras que el contenido de un elemento HTML
puede decodificarse
como HTML
del lado del cliente
Al construir un ataque, debemos pensar exactamente dónde se está inyectando
nuestro payload
y si podemos inferir en cómo se decodifica nuestro input en ese contexto
. Además, podemos identificar formas alternativas de representar el mismo payload
Discrepancias en la decodificación
Los ataques de inyección suelen implicar insertar payload que usan patrones reconocibles
, como etiquetas HTML
, funciones JavaScript
o sentencias SQL
. Como normalmente no se espera que el input del usuario contenga código o lenguaje de marcado (HTML, XML, SVG ...)
suministrado por el usuario, los sitios web
suelen implementar defensas
que bloquean peticiones
con esos patrones sospechosos
Sin embargo, estos filtros de inputs
también necesitan decodificar el input
para comprobar si es seguro
o no
. Desde una perspectiva de seguridad, es vital
que la decodificación
realizada durante la comprobación
sea la misma que la que realizará el servidor
o el navegador
cuando use los datos. Cualquier discrepancia
puede permitir
a un atacante engañar al filtro
aplicando codificaciones diferentes
que luego se eliminarán automáticamente
Ofuscación mediante URL encoding
En una URL, hay una serie de caracteres reservados
que tienen significados especiales
. Por ejemplo, un ampersand (&)
se usa como delimitador
para separar parámetros en la cadena de consulta
. El problema es que las entradas basadas en URL pueden contener estos caracteres por otra razón
. ¿Qué pasaría si un usuario busca algo como Fish & Chips
?
Los navegadores URL encodean
cualquier carácter que pueda causar ambigüedad
para los analizadores
. Normalmente sustituyen esos caracteres por un carácter %
seguido de su código hexadecimal de 2 dígitos
, ya que esto asegura que el &
no se confunda
con un delimitador
. Por ejemplo:
1
[...]/?search=Fish+%26+Chips
Aunque el carácter espacio
puede codificarse
como %20
, a menudo se representa con un +
, como en el ejemplo anterior
Cualquier entrada basada en URL
se decodifica automáticamente
en el servidor
antes de asignarse
a las variables relevantes
. Esto significa que, para la mayoría de servidores
, secuencias como %22
, %3C
y %3E
en un parámetro de consulta
son sinónimas de "
, <
y >
respectivamente. En otras palabras, podemos inyectar datos URL encodeados
a través de la URL
y generalmente la aplicación del back-end
los interpretará correctamente
Ocasionalmente, puede ocurrir que WAFs u otros filtros no decodifiquen correctamente
el input
al comprobarlo. En ese caso, es posible efectuar un bypass
simplemente codificando cualquier carácter o palabra
que esté blacklisteado
. Por ejemplo, en una inyección SQL
, podríamos codificar
las palabras clave
, de modo que SELECT
se convierta en %53%45%4C%45%43%54
, etc
Ofuscación mediante doble URL encoding
Por una razón u otra, algunos servidores decodifican la URL dos veces
. Esto no es necesariamente un problema por sí mismo, siempre que los mecanismos de seguridad también decodifiquen dos veces el input al comprobarlo
. De lo contrario, esta discrepancia permite a un atacante efectuar un bypass
simplemente URL encodeando el payload dos veces
Supongamos que intentamos explotar un XSS, como <img src=x onerror=alert(1)>
mediante un parámetro de consulta
. La URL
podría verse así:
1
[...]/?search=%3Cimg%20src%3Dx%20onerror%3Dalert(1)%3E
Si un WAF URL decodea
nuestro input
al comprobar la petición
, identificará
fácilmente este payload
y bloqueará la petición
antes de que llegue al back-end
. Pero ¿y si URL encodeamos doblemente nuestro payload?
En la práctica, esto significa que los caracteres %
se reemplazan por %25
:
1
[...]/?search=%253Cimg%2520src%253Dx%2520onerror%253Dalert(1)%253E
Si el WAF
solo URL decodea una vez
, puede no ser capaz de identificar
que nuestro input
es peligroso
y si el back-end lo decodifica dos veces
posteriormente, nuestro payload
se inyectará con éxito
Ofuscación mediante HTML encoding
En documentos HTML
, ciertos caracteres deben escaparse
o codificarse
para evitar
que el navegador
los interprete incorrectamente
como parte del lenguaje de marcado
. Esto se logra sustituyendo
los caracteres problemáticos
por una referencia precedida
por un ampersand (&)
y terminada
con un punto y coma (;)
. En muchos casos, se puede utilizar un nombre
para la referencia
. Por ejemplo, la secuencia :
representa el carácter de dos puntos (:)
Alternativamente, la referencia puede proporcionarse utilizando el code point decimal o hexadecimal del carácter
. Un code point
es el número único
que identifica a cada carácter (letra, símbolo, emoji)
en la tabla universal Unicode
. En HTML
, puedes usar ese número
en decimal (:)
o hexadecimal (:)
para representar un carácter como :
sin escribirlo directamente, lo que es útil para evitar
que el navegador
lo confunda
con código
o para ocultar ataques
En ubicaciones específicas
dentro del documento HTML
, como el contenido de un elemento o el valor de un atributo
, los navegadores decodificarán automáticamente
estas referencias
cuando analicen
el documento
. Al inyectar código
en dicha ubicación
, podemos aprovecharnos de esto para ofuscar payloads en ataques del lado del cliente
, ocultándolos de cualquier defensa del lado del servidor
Si observamos detenidamente el payload
de XSS
del ejemplo anterior, vemos que se está inyectando
dentro de un atributo HTML
, concretamente en el manejador de eventos onerror
. Si las comprobaciones del lado del servidor
buscan explícitamente alert()
, podrían no detectarla
si HTML encodeamos uno o más de los caracteres
. Por ejemplo:
1
<img src=x onerror="alert(1)">
Cuando el navegador renderiza
la página web
, decodificará y ejecutará el payload inyectado
Ceros a la izquierda
Curiosamente, al utilizar HTML encoding
en formato decimal o hexadecimal
, podemos incluir
opcionalmente cualquier número de ceros a la izquierda
en los code points
. Algunos WAFs
y otros filtros de entrada no tienen esto en cuenta o cometen errores en su validación
Si nuestro payload
sigue siendo bloqueado
después de HTML encodearlo
, podemos intentar evadir el filtro añadiendo unos cuantos ceros a los code points
. Por ejemplo:
1
<a href="javascript:alert(1)">Click me</a>
Sin añadirle los ceros a la izquierda
, este sería el payload original
:
1
<a href="javascript:alert(1)">Click me</a>
Ofuscación mediante XML encoding
XML
está estrechamente relacionado con HTML
y también soporta secuencias numéricas de escape para caracteres
. Esto nos permite incluir caracteres especiales
en el contenido de los elementos
sin romper
la sintaxis
, lo cual es útil cuando testeamos un XSS
y nuestro payload
se envía
en formato XML
Aunque no necesitemos codificar caracteres para evitar errores de sintaxis
, podemos aprovechar este comportamiento para ofuscar un payload
de la misma forma que lo hacemos con HTML encoding
. La diferencia es que la decodificación
suele hacerla el servidor
, en lugar del navegador
. Esto es útil para bypassear WAFs
y otros filtros
, que podrían bloquear
nuestras peticiones
si detectan palabras clave asociadas a inyecciones SQL
1
2
3
4
5
6
7
8
<stockCheck>
<productId>
123
</productId>
<storeId>
999 SELECT * FROM information_schema.tables
</storeId>
</stockCheck>
Ofuscación mediante escapes Unicode
Las secuencias de escape Unicode
consisten en el prefijo \u
seguido del código hexadecimal de cuatro dígitos para el carácter
. Por ejemplo, \u003a
representa dos puntos (:)
. ES6
también soporta una nueva forma usando llaves (\u{3a})
Al parsear cadenas
, la mayoría de los lenguajes de programación decodifican estas secuencias Unicode
. Esto incluye el motor de JavaScript
usado por los navegadores
. Cuando inyectamos
dentro de un contexto de cadena
, podemos ofuscar
los payloads
mediante Unicode
, igual que hicimos con los escapes HTML
en el ejemplo anterior
Por ejemplo, si intentamos explotar
un DOM XSS
donde nuestro input
se pasa al sink eval()
como cadena
y nuestros intentos iniciales son bloqueados
, debemos probar a escapar
uno de los caracteres de esta forma:
1
eval("\u0061lert(1)")
Como esto permanecerá codificado del lado del servidor
, puede pasar desapercibido
hasta que el navegador
lo decodifique
de nuevo
Dentro de una cadena podemos escapar cualquier carácter
de esta forma. Sin embargo, fuera de una cadena
, escapar
algunos caracteres
provocará un error de sintaxis
, por ejemplo, los paréntesis de apertura y cierre
También es importante notar que los escapes Unicode estilo ES6
permiten ceros a la izquierda opcionales
, por lo que algunos WAFs
pueden ser engañados usando la misma técnica que empleamos en HTML encoding
. Por ejemplo:
1
<a href="javascript:\u{00000000061}alert(1)">Click me</a>
Ofuscación mediante escapes hexadecimales
Otra opción al inyectar en un contexto de cadena
es usar escapes hexadecimales
, que representan caracteres usando su code point
en hexadecimal
, prefijados con \x
. Por ejemplo, la letra minúscula a
se representa como \x61
Al igual que los escapes Unicode
, estos serán decodificados
del lado cliente
mientras el input
se evalúe
como cadena
. Por ejemplo:
1
eval("\x61lert")
Nótese que a veces también podemos ofuscar sentencias SQL
de forma similar usando el prefijo 0x
. Por ejemplo, 0x53454c454354
puede decodificarse para formar la palabra clave SELECT
Ofuscación mediante escapes octales
Los escapes octales funcionan de forma muy parecida a los hexadecimales
, excepto que usan un sistema numérico en base 8
en lugar de base 16
. Estos se prefijan con una barra invertida sola
, de modo que la letra a en minúscula
se representa como \141
. Por ejemplo:
1
eval("\141lert(1)")
Ofuscación mediante múltiples encodings
Es importante notar que podemos combinar diferentes tipos de encodings
para ocultar
nuestros payloads
detrás de varias capas
de ofuscación
. Por ejemplo:
1
<a href="javascript:\u0061lert(1)">Click me</a>
Los navegadores primero decodifican HTML \
, resultando en una barra invertida
. Esto convierte los caracteres u0061
en el escape Unicode \u0061
. El resultado sería el siguiente:
1
<a href="javascript:\u0061lert(1)">Click me</a>
Esto se decodifica
después para formar una payload XSS funcional
de la siguiente forma:
1
<a href="javascript:alert(1)">Click me</a>
Claramente, para inyectar
un payload
con éxito de esta forma, debemos entender bien qué decodificaciones
se aplican a nuestro input
y en qué orden
se realizan
Ofuscación mediante la función SQL CHAR()
Aunque no es estrictamente una forma de codificación
, en algunos casos podemos ofuscar
nuestros ataques de inyección SQL
usando la función CHAR()
. Esta acepta un code point decimal o hexadecimal
y devuelve el carácter correspondiente
. Los códigos hexadecimales
deben ir prefijados
con 0x
. Por ejemplo, tanto CHAR(83)
como CHAR(0x53)
devuelven la letra mayúscula S
Al concatenar los valores devueltos
, podemos usar este enfoque para ofuscar palabras clave bloqueadas
. Por ejemplo, incluso si SELECT
está blacklisteado
, podemos ejecutar
la siguiente intrucción
:
1
CHAR(83)+CHAR(69)+CHAR(76)+CHAR(69)+CHAR(67)+CHAR(84)
Cuando esto es procesado
como SQL
por la aplicación
se construye dinámicamente la palabra clave SELECT
y se ejecuta la consulta