reDoS

12
N-D [REDOS AL DETALLE] Por David Kotriksnov (a.k.a. SH4V) | N3t-Datagrams.net

description

Denegaciones de Servicio en la validación de Expresiones Regulares

Transcript of reDoS

Page 1: reDoS

N-D

[REDOS AL DETALLE] Por David Kotriksnov (a.k.a. SH4V) | N3t-Datagrams.net

Page 2: reDoS

N3T-DATAGRAMS [REDOS AL DETALLE]

David Kotriksnov (a.k.a. SH4V)| N3t-Datagrams.net

2

En los últimos años se ha hablado mucho de ataques de Denegación de

Servicio y más de alguno de vosotros conoceréis las famosas botnets (redes de

maquinas zombies programadas para realizar peticiones masivas a servidores

hasta causar una sobresaturación de los mismos). Existen sin embargo otro

tipo diferente de ataques de Denegación de Servicio o DoS (Denial of Service).

Un ataque contra una base de datos que eliminara el contenido de sus tablas

sería otro tipo de denegación de servicio ya que no permitiría al usuario poder

acceder a la información antes almacenada en el servidor:

DROP dataBASE

Existen muchos tipos de Denegaciones de Servicio pero la variante que vamos

a tratar en este paper son las Denegaciones de Servicio en la validación de

Expresiones Regulares, o lo que se conoce por reDoS.

En 2009 fue presentado en la Open Web Application Security Project (OWASP)

Israel Conference. En esa presentación se explicaba como una expresión

regular pobremente escrita podía ser explotada para realizar un DoS.

TIPOS DE MOTORES REGEXP:

Podemos encontrar dos tipos de motores RegExp: DFA (Deterministic Finite

Automaton) y NFA (Nondeterministic Finite Automaton). Los motores NFA son

sistemas de marcha atrás mientras que los DFA no. Los DFA evalúan cada

carácter de cada cadena como mucho una vez mientras que los NFA pueden

evaluar cada carácter todas las veces que sean necesarias hasta determinar

que la cadena ha finalizado o la coincidencia ha sido encontrada. El sistema de

marcha atrás del motor NFA tiene el pro de que pueden procesar expresiones

regulares más complejas pero tienen el inconveniente de superar en tiempo de

procesamiento a las DFA (depende de la expresión regular).

EL PROBLEMA:

Los sistemas NFA (marcha atrás) pueden confirmar con rapidez una

coincidencia, sin embargo identificar una no-coincidencia en la cadena puede

tomar mucho más tiempo ya que el motor tiene que confirmar que ninguna de

los posibles caracteres a través de la string coincide con la expresión regular.

Es decir, que todos los caracteres tienen que volver a ser testeados. Por tanto,

Page 3: reDoS

N3T-DATAGRAMS [REDOS AL DETALLE]

David Kotriksnov (a.k.a. SH4V)| N3t-Datagrams.net

3

cada vez que añadamos un carácter a la cadena, el tiempo de respuesta se

duplicará de forma exponencial

Vamos a ver un ejemplo sencillo. Utilizaremos las expresiones regulares en

Javascript porque no necesitamos más que un navegador para poder

procesarlas. Esto hace que sea más portable y cualquiera en su casa sin

necesidad de un servidor pueda probarlo:

<script language='javascript'>

var str="abcdefg";

var reg=/^(\w+)+$/;

if (reg.test(str)){

alert('Match found!');

}else{

alert('Match not found...')

}

</script>

El código se ejecuta sin problemas y nos mostrará un alert con el mensaje

"Match found!". Pero qué ocurriría si añadimos al final de la variable "str" un

carácter no alfanumérico? Probad a sustituir la línea dos por esto:

<script language='javascript'>

var str="abcdefg^";

var reg=/^(\w+)+$/;

if (reg.test(str)){

alert('Match found!');

}else{

alert('Match not found...')

}

</script>

Page 4: reDoS

N3T-DATAGRAMS [REDOS AL DETALLE]

David Kotriksnov (a.k.a. SH4V)| N3t-Datagrams.net

4

El navegador se trabará. Algunos navegadores están preparados para este tipo

de situaciones así que no os extrañéis si os sale un mensaje de alerta

preguntando si quieres detener el script. Pero la pregunta es: ¿Por qué? es

entonces cuando llega el turno de bucear por el conocimiento para descubrir la

respuesta al enigma que de forma superficial parece cosa de brujería, pero que

en el fondo no es más que pura matemática.

RESOLUCIÓN DEL PROBLEMA:

Por partes, analicemos la expresión regular:

\w: Cualquier carácter alfanumérico.

+: 1 o más repeticiones de caracteres alfanuméricos.

(): Toma los caracteres dentro del paréntesis como un grupo.

El problema surge cuando incluimos caracteres que pueden repetirse dentro de

un grupo que a su vez puede repetirse. Esto crea un bucle que crece de forma

exponencial a medida que añadimos caracteres. Como se dijo anteriormente,

los sistemas NFA son motores de marcha atrás. Esto es, que empiezan

determinando la longitud de la cadena y comienzan a analizarla desde el final

hasta el inicio. Tomemos el siguiente ejemplo:

<script>

var str="abcdefg";

var reg=/^w+$/;

reg.test(str);

</script>

El script comenzaría por calcular la longitud, empezando por a, b, c ... hasta

llegar a g. Una vez llegado a g, el sistema de marcha atrás del motor NFA

vuelve hacia atrás, en el siguiente orden: abcdefg, abcdef, abcde, abcd, abc,

ab, a. El número de rutas es de 7. Esta expresión regular no sería vulnerable a

reDoS. Sin embargo la cosa cambia cuando la expresión regular es esta:

<script>

var str="abcdefg";

var reg=/^(w+)+$/;

reg.test(str);

Page 5: reDoS

N3T-DATAGRAMS [REDOS AL DETALLE]

David Kotriksnov (a.k.a. SH4V)| N3t-Datagrams.net

5

</script>

Como veis, estamos metiendo entre paréntesis un grupo de caracteres

alfanuméricos que puede repetirse como unidad y como grupo. Ahí es donde

está el peligro. Veamos como interpretaría esto el motor NFA:

A. Llega hasta el final de la cadena de la siguiente manera: a, ab, abc,

abcd, abcde, abcdef, abcdefg.

B. Comienza la marcha atrás. Analizaremos a partir de aquí diferenciando

entre adyacentes y grupos. Como hacerlo en un procesador de textos es

muy complicado, haré en papel los 4 primeros pasos. A partir de ahí

pensad y si es necesario sacad bolígrafo y papel para entenderlo. Si

compagináis la tabla de abajo con la imagen tendréis más facilidad a la

hora de comprender.

Adyacentes Grupos Resultado de Rutas/Ciclos

abcdef + (g) - g es tomado como grupo. 1 ruta/ciclo.

abcde + (fg) - fg es tomado como grupo (+1) que su

vez se divide en un adyacente (f) y un

grupo (g) (+1). 1+1=2 rutas/ciclos.

abcd + (efg) - efg es tomado como grupo (+1), lo que

significa que será analizado como grupo

efg. efg a su vez será dividido en un

adyacente (ef) y un grupo (g) (+1).

Siguiendo la línea de la marcha atrás, se

dará un paso a la izquierda quedando el

adyacente (e) y el grupo (fg)(+1).

Finalmente el grupo (fg) se analiza

quedando un adyacente (f) y un grupo (g)

(+1). 1+1+1+1=4 rutas/ciclos.

abc + (defg) [...] 8 rutas/ciclos.

ab + (cdefg) [...] 16 rutas/ciclos.

a + (bcdefg) [...] 32 rutas/ciclos.

- + (abcdefg) [...] 64 rutas/ciclos.

TOTAL: 127 RUTAS + 1 ruta de identificación inicial= 128

RUTAS/CICLOS.

Page 6: reDoS

N3T-DATAGRAMS [REDOS AL DETALLE]

David Kotriksnov (a.k.a. SH4V)| N3t-Datagrams.net

6

Cada vez que añadamos un carácter más, el número de rutas o repeticiones

será el doble, es decir, crecerá en sentido exponencial al cuadrado, por lo que

el número de rutas será igual a:

N= 2n

Donde N es el número de rutas y n es el número de caracteres antes del fallo.

Page 7: reDoS

N3T-DATAGRAMS [REDOS AL DETALLE]

David Kotriksnov (a.k.a. SH4V)| N3t-Datagrams.net

7

EXPLOTACIÓN:

Vamos a ver varios ejemplos de RegExp vulnerables a una Denegación de

Servicio (algunas de ellas sacadas de OWASP) y como explotarlas:

/^(\w+)+$/= abcdefgasdfASDlkja_slf123leic_vaskjefhasjefh^ //Ingresamos

caracteres alfanuméricos y/o "_" y finalizamos la cadena con una

excepción. 2⁴⁴ rutas.

/^(a+)*$/= aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^

//Ingresamos "a" y finalizamos con una excepción. 2³⁹ rutas.

/^([a-z0-9]+)*$/= aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^ // 2³⁹

rutas.

/^(1|r)+=$/= 1111111111111111111111111111111111111111^ //2⁴⁰

rutas.

/^(([a-z])+.)+[A-Z]([a-z])+$/=

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^ //2³⁹ rutas.

/^([a-zA-Z0-9])(([\-.]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-

z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$/=

12345678901234567890123456789012345678901234567890^ // 2⁵⁰

rutas.

/^(.*a){x} | for x > 10^/ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^

//Esta es interesante. 2³⁹ rutas.

Como podéis observar, se explotan todas del mismo modo: introduciendo

caracteres válidos y añadiendo finalmente uno que no entre en el grupo de

caracteres que deberían venir después.

LOCALIZACIÓN:

Ponernos a analizar una expresión regular en busca de posibles DoS puede

ser algo tedioso si no estás familiarizado con las expresiones regulares. No

cuesta nada ponerse y es hasta divertido. Para ahorrar la tarea de analizar

manualmente he desarrollado un programa en JavaScript que analiza mediante

RegExp si hay alguna falla de reDoS. Analizar una expresión regular por medio

de RegExp de forma perfecta es muy complicado por lo que esta aplicación no

es perfecta y puede dar falsos resultados. Aún así tiene un porcentaje de

acierto muy alto, en torno al 95%. Sólo tienes que guardarlo en un archivo de

texto y renombrarlo con extensión htm/html.

Page 8: reDoS

N3T-DATAGRAMS [REDOS AL DETALLE]

David Kotriksnov (a.k.a. SH4V)| N3t-Datagrams.net

8

<html>

<!--

[-] RegExp DoS Analyzer.

[-] Programmed by SH4V.

[-] Visit http://n3t-datagrams.net/ &&

http://foro.undersecurity.net/

[-] Gr33tz to Pr0x, Protos, Lix, OzX, C1c4Tr1Z, N0b0dy,

Yoyahack, S[e]C, Seth, 1995, Dynamique and Undersec

members.

[-] Realizar un programa que localice RegExp

vulnerables a un DoS mediante RegExp es extremadamente

difícil por lo que el programa no es perfecto. En

ocasiones da falsos positivos/negativos.4

[-] Enjoy it :-)

-->

<head>

<meta http-equiv='Content-type'

Content='text/html;charset=utf-8'>

<style type="text/css">

#title {

text-align:center;

padding-top:15px;

padding-bottom:15px;

font-family: Arial, Georgia;

font-size: 35px;

color: white;

background-color: #01356e;

}

#form {

background-color: #819ab8;

Page 9: reDoS

N3T-DATAGRAMS [REDOS AL DETALLE]

David Kotriksnov (a.k.a. SH4V)| N3t-Datagrams.net

9

text-align:center;

padding-bottom:1px;

padding-top:10px;

color: white;

}

#result {

background-color:#FFFFFF;

color: #819ab8;

font-family: Arial, Georgia;

text-align:center;

padding-bottom: 400px;

}

#credits{

text-align:center;

font-family: Arial, Georgia;

color: #01356e;

font-size: 11px;

font-weight: bold;

</style>

</head>

<title>RegExp Denial of Service Analyzer</title>

<body>

<div id='title'>RegExp Denial of Service Analyzer</div>

<div id='form'><form name='formu' method='post'>

<input type='text' name='entrada' size= 37>

<input type='button' onClick='proform()'

value='Comprobar RegExp'>

</form></div>

<div id='result'><h1><br /></h1></div>

Page 10: reDoS

N3T-DATAGRAMS [REDOS AL DETALLE]

David Kotriksnov (a.k.a. SH4V)| N3t-Datagrams.net

10

<script>

var reg= /\(([\w!%&,~:;<>=@·\/\'\-

\"\\\*\+\?\|\{\[\]\}\(\)\^\$\.\#])*\)\*/;

var result=document.getElementById('result');

var per= 0;

var tru= false;

function cutpast(strg,sym){

strg=strg.split(sym);

strg=strg.join('*');

return strg;

}

function proform(){

var str = document.formu.elements["entrada"].value;

if (str.match(/script/i)){

alert('Intentó un DOM bassed XSS. Si esta medida de

seguridad interfiere en el análisis de su RegExp,

desactívela modificando el código fuente.');

}else{

countdown();

}

}

function countdown(){

if(tru){

var str = document.formu.elements["entrada"].value;

str=cutpast(str,"+");

Page 11: reDoS

N3T-DATAGRAMS [REDOS AL DETALLE]

David Kotriksnov (a.k.a. SH4V)| N3t-Datagrams.net

11

if (reg.test(str)){

control=reg.exec(str)[0];

control=cutpast(control, "|");

if(control.split('*').length){

alert("Posible reDoS! Revise las RegExp.");

var sh0w=

document.formu.elements["entrada"].value.split("\"").jo

in('').split("\'").join('');

result.innerHTML="<h2>Posible reDoS en:

"+sh0w+"</h2>";

}

}else{

alert('Sin riesgo de alerta... :)');

result.innerHTML="<h2>No se encontraron patrones

peligrosos pero se recomienda hacer una revisión

manual.</h2>";

}

}else{

per++;

if (per==100){tru=true;}

countd=setTimeout('countdown()',25);

result.innerHTML="<h1>"+per+"% Completado</h1>";

}

}

</script>

<div id='credits'>N3t-Datagrams / David Kotriksnov

(a.k.a. SH4V)</div>

</body>

</html>

Page 12: reDoS

N3T-DATAGRAMS [REDOS AL DETALLE]

David Kotriksnov (a.k.a. SH4V)| N3t-Datagrams.net

12

REDOS POR REGEXP INJECTION:

Un tipo muy interesante de reDoS es este. Consiste en provocar una cadena vulnerable a reDoS y explotarla más adelante en un mismo formulario de verificación de campos. Veamos un ejemplo sacado de OWASP:

String userName = textBox1.Text;

String password = textBox2.Text;

Regex testPassword = new Regex(userName);

Match match = testPassword.Match(password);

if (match.Success)

{

MessageBox.Show("Do not include name in

password.");

}

else

{

MessageBox.Show("Good password.");

}

LECTURAS RECOMENDADAS:

http://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS

http://es.wikipedia.org/wiki/Expresi%C3%B3n_regular

AGRADECIMIENTOS:

Gr33tz to Pr0x, Protos, Lix, OzX, C1c4Tr1Z, N0b0dy, Yoyahack, S[e]C, Seth, 1995, Dynamique and Undersec members.