Security
Headlines
HeadlinesLatestCVEs

Headline

GHSA-82vx-mm6r-gg8w: Bref vulnerable to Body Parsing Inconsistency in Event-Driven Functions

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:

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

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();

  1. Use the following 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 /upload'
  1. 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--
  1. Notice how the body has been parsed.
  2. Create a plain.php file with the following content:
<?php

var_dump($_POST);
  1. Start a PHP server inside the project directory (e.g. php -S 127.0.0.1:8090).
  2. 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--
  1. Notice the differences in the parsing compared to what observed at step 5.

Suggested Remediation

Use the PHP function parse_str to parse the body parameters to mimic the plain PHP behavior.

ghsa
#vulnerability#web#git#php#aws#webkit

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:

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

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();

  1. Use the following 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 /upload’

  1. 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–

  1. Notice how the body has been parsed.

  2. Create a plain.php file with the following content:

  3. Start a PHP server inside the project directory (e.g. php -S 127.0.0.1:8090).

  4. 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–

  1. Notice the differences in the parsing compared to what observed at step 5.

Suggested Remediation

Use the PHP function parse_str to parse the body parameters to mimic the plain PHP behavior.

References

  • GHSA-82vx-mm6r-gg8w
  • https://nvd.nist.gov/vuln/detail/CVE-2024-24754
  • brefphp/bref@c77d9f5
  • https://github.com/brefphp/bref/blob/2.1.12/src/Event/Http/Psr7Bridge.php#L130-L168

ghsa: Latest News

GHSA-hxf5-99xg-86hw: cap-std doesn't fully sandbox all the Windows device filenames