You are on page 1of 12

N-D

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

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,

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


N3T-DATAGRAMS [REDOS AL DETALLE]

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...')


3
}

</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>

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


N3T-DATAGRAMS [REDOS AL DETALLE]

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,
4
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);

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


N3T-DATAGRAMS [REDOS AL DETALLE]

</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 5
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.

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


N3T-DATAGRAMS [REDOS AL DETALLE]

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.

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


N3T-DATAGRAMS [REDOS AL DETALLE]

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])+$/= 7
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.
David Kotriksnov (a.k.a. SH4V)| N3t-Datagrams.net
N3T-DATAGRAMS [REDOS AL DETALLE]

<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 :-)
8
-->
<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;
David Kotriksnov (a.k.a. SH4V)| N3t-Datagrams.net
N3T-DATAGRAMS [REDOS AL DETALLE]

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{ 9

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>

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


N3T-DATAGRAMS [REDOS AL DETALLE]

<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;
10
}

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,"+");

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


N3T-DATAGRAMS [REDOS AL DETALLE]

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... :)'); 11
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>

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


N3T-DATAGRAMS [REDOS AL DETALLE]

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."); 12
}
else
{
MessageBox.Show("Good password.");
}

LECTURAS RECOMENDADAS:
 http://www.owasp.org/index.php/Regular_expression_Denial_of_Se
rvice_-_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.

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

You might also like