GHSA-82vx-mm6r-gg8w
Bref vulnerable to Body Parsing Inconsistency in Event-Driven Functions
Quick fix
GHSA-82vx-mm6r-gg8w — bref/bref: upgrade to the fixed version with the command below.
composer require bref/bref:^2.1.13Details
## Impacted Resources
bref/src/Event/Http/Psr7Bridge.php:130-168
## Description
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 and its content added in the `$files` or `$parsedBody` arrays. To do that, the following method is called with as first argument the result array (`$files` or `$parsedBody`), as second argument the part name, and as third argument the part content:
```php /** * Parse a string key like "files[id_cards][jpg][]" and do $array['files']['id_cards']['jpg'][] = $value */ private static function parseKeyAndInsertValueInArray(array &$array, string $key, mixed $value): void { if (! str_contains($key, '[')) { $array[$key] = $value;
return; }
$parts = explode('[', $key); // files[id_cards][jpg][] => [ 'files', 'id_cards]', 'jpg]', ']' ] $pointer = &$array;
foreach ($parts as $k => $part) { if ($k === 0) { $pointer = &$pointer[$part];
continue; }
// Skip two special cases: // [[ in the key produces empty string // [test : starts with [ but does not end with ] if ($part === '' || ! str_ends_with($part, ']')) { // Malformed key, we use it "as is" $array[$key] = $value;
return; }
$part = substr($part, 0, -1); // The last char is a ] => remove it to have the real key
if ($part === '') { // [] case $pointer = &$pointer[]; } else { $pointer = &$pointer[$part]; } }
$pointer = $value; } ```
The conversion process produces a different output compared to the one of plain PHP when keys ending with and open square bracket (`[`) are used.
Let's take for example the following part: ``` ------WebKitFormBoundary Content-Disposition: form-data; name="key0[key1][key2]["
value ------WebKitFormBoundary-- ```
In plain PHP it would be converted to `Array( [key0] => Array ( [key1] => Array ( [key2] => value) ) )`, while in Bref it would be converted to `Array( [key0] => Array ( [key1] => Array ( [key2] => ) ) [key0[key1][key2][] => value )`.
## Impact
Based on the application logic the difference in the body parsing might lead to vulnerabilities and/or undefined behaviors.
## PoC
1. Create a new Bref project. 2. Create an `index.php` file with the following content: ```php <?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, [], var_export($request->getParsedBody(),true)); } }
return new MyHttpHandler();
``` 3. Use the following `serverless.yml` to deploy the Lambda: ```yaml 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 /upload' ``` 4. Replay the following request after having replaced the `<HOST>` placeholder with the deployed Lambda domain: ``` POST /upload HTTP/2 Host: <HOST> Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryQqDeSZSSvmn2rfjb Content-Length: 180
------WebKitFormBoundaryQqDeSZSSvmn2rfjb Content-Disposition: form-data; name="key0[key1][key2]["
value ------WebKitFormBoundaryQqDeSZSSvmn2rfjb-- ``` 5. Notice how the body has been parsed. 6. Create a `plain.php` file with the following content: ```php <?php
var_dump($_POST); ``` 7. Start a PHP server inside the project directory (e.g. `php -S 127.0.0.1:8090`). 8. Replay the following request after having replaced the `<HOST>` placeholder with the PHP server address: ``` POST /plain.php HTTP/1.1 Host: <HOST> Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryQqDeSZSSvmn2rfjb Content-Length: 180
------WebKitFormBoundaryQqDeSZSSvmn2rfjb Content-Disposition: form-data; name="key0[key1][key2]["
value ------WebKitFormBoundaryQqDeSZSSvmn2rfjb-- ``` 9. Notice the differences in the parsing compared to what observed at step 5.
## Suggested Remediation
Use the PHP function [`parse_str`](https://www.php.net/manual/en/function.parse-str.php) to parse the body parameters to mimic the plain PHP behavior.
Are you affected?
Enter the version of the package you're using.
Affected packages
References
- https://github.com/brefphp/bref/security/advisories/GHSA-82vx-mm6r-gg8w[WEB]
- https://nvd.nist.gov/vuln/detail/CVE-2024-24754[ADVISORY]
- https://github.com/brefphp/bref/commit/c77d9f5abf021f29fa96b5720b7b84adbd199092[WEB]
- https://github.com/brefphp/bref[PACKAGE]
- https://github.com/brefphp/bref/blob/2.1.12/src/Event/Http/Psr7Bridge.php#L130-L168[WEB]