GHSA-82VX-MM6R-GG8W

Vulnerability from github – Published: 2024-02-01 22:47 – Updated: 2024-02-01 22:47
VLAI?
Summary
Bref vulnerable to Body Parsing Inconsistency in Event-Driven Functions
Details

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.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "bref/bref"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.1.13"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2024-24754"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-436"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2024-02-01T22:47:29Z",
    "nvd_published_at": "2024-02-01T16:17:14Z",
    "severity": "LOW"
  },
  "details": "## Impacted Resources\n\nbref/src/Event/Http/Psr7Bridge.php:130-168\n\n## Description\n\nWhen 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.\nDuring the conversion process, if the request is a MultiPart, each part is parsed and its content added in the `$files` or `$parsedBody` arrays.\nTo 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:\n\n```php\n/**\n * Parse a string key like \"files[id_cards][jpg][]\" and do $array[\u0027files\u0027][\u0027id_cards\u0027][\u0027jpg\u0027][] = $value\n */\nprivate static function parseKeyAndInsertValueInArray(array \u0026$array, string $key, mixed $value): void\n{\n    if (! str_contains($key, \u0027[\u0027)) {\n        $array[$key] = $value;\n\n        return;\n    }\n\n    $parts = explode(\u0027[\u0027, $key); // files[id_cards][jpg][] =\u003e [ \u0027files\u0027,  \u0027id_cards]\u0027, \u0027jpg]\u0027, \u0027]\u0027 ]\n    $pointer = \u0026$array;\n\n    foreach ($parts as $k =\u003e $part) {\n        if ($k === 0) {\n            $pointer = \u0026$pointer[$part];\n\n            continue;\n        }\n\n        // Skip two special cases:\n        // [[ in the key produces empty string\n        // [test : starts with [ but does not end with ]\n        if ($part === \u0027\u0027 || ! str_ends_with($part, \u0027]\u0027)) {\n            // Malformed key, we use it \"as is\"\n            $array[$key] = $value;\n\n            return;\n        }\n\n        $part = substr($part, 0, -1); // The last char is a ] =\u003e remove it to have the real key\n\n        if ($part === \u0027\u0027) { // [] case\n            $pointer = \u0026$pointer[];\n        } else {\n            $pointer = \u0026$pointer[$part];\n        }\n    }\n\n    $pointer = $value;\n}\n```\n\nThe conversion process produces a different output compared to the one of plain PHP when keys ending with and open square bracket (`[`) are used.\n\nLet\u0027s take for example the following part:\n```\n------WebKitFormBoundary\nContent-Disposition: form-data; name=\"key0[key1][key2][\"\n\nvalue\n------WebKitFormBoundary--\n```\n\nIn plain PHP it would be converted to `Array( [key0] =\u003e Array ( [key1] =\u003e Array ( [key2] =\u003e value) ) )`, while in Bref it would be converted to `Array( [key0] =\u003e Array ( [key1] =\u003e Array ( [key2] =\u003e  ) ) [key0[key1][key2][] =\u003e value )`.\n\n## Impact\n\nBased on the application logic the difference in the body parsing might lead to vulnerabilities and/or undefined behaviors.\n\n## PoC\n\n1. Create a new Bref project.\n2. Create an `index.php` file with the following content:\n```php\n\u003c?php\n\nnamespace App;\n\nrequire __DIR__ . \u0027/vendor/autoload.php\u0027;\n\nuse Nyholm\\Psr7\\Response;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\n\nclass MyHttpHandler implements RequestHandlerInterface\n{\n    public function handle(ServerRequestInterface $request): ResponseInterface\n    {\n\n        return new Response(200, [], var_export($request-\u003egetParsedBody(),true));\n    }\n}\n\nreturn new MyHttpHandler();\n\n```\n3. Use the following `serverless.yml` to deploy the Lambda:\n```yaml\nservice: app\n\nprovider:\n    name: aws\n    region: eu-central-1\n\nplugins:\n    - ./vendor/bref/bref\n\n# Exclude files from deployment\npackage:\n    patterns:\n        - \u0027!node_modules/**\u0027\n        - \u0027!tests/**\u0027\n\nfunctions:\n    api:\n        handler: index.php\n        runtime: php-83\n        events:\n            - httpApi: \u0027ANY /upload\u0027\n```\n4. Replay the following request after having replaced the `\u003cHOST\u003e` placeholder with the deployed Lambda domain:\n```\nPOST /upload HTTP/2\nHost: \u003cHOST\u003e\nContent-Type: multipart/form-data; boundary=----WebKitFormBoundaryQqDeSZSSvmn2rfjb\nContent-Length: 180\n\n------WebKitFormBoundaryQqDeSZSSvmn2rfjb\nContent-Disposition: form-data; name=\"key0[key1][key2][\"\n\nvalue\n------WebKitFormBoundaryQqDeSZSSvmn2rfjb--\n```\n5. Notice how the body has been parsed.\n6. Create a `plain.php` file with the following content:\n```php\n\u003c?php\n\nvar_dump($_POST);\n```\n7. Start a PHP server inside the project directory (e.g. `php -S 127.0.0.1:8090`).\n8. Replay the following request after having replaced the `\u003cHOST\u003e` placeholder with the PHP server address:\n```\nPOST /plain.php HTTP/1.1\nHost: \u003cHOST\u003e\nContent-Type: multipart/form-data; boundary=----WebKitFormBoundaryQqDeSZSSvmn2rfjb\nContent-Length: 180\n\n------WebKitFormBoundaryQqDeSZSSvmn2rfjb\nContent-Disposition: form-data; name=\"key0[key1][key2][\"\n\nvalue\n------WebKitFormBoundaryQqDeSZSSvmn2rfjb--\n```\n9. Notice the differences in the parsing compared to what observed at step 5.\n\n## Suggested Remediation\n\nUse 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.",
  "id": "GHSA-82vx-mm6r-gg8w",
  "modified": "2024-02-01T22:47:29Z",
  "published": "2024-02-01T22:47:29Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/brefphp/bref/security/advisories/GHSA-82vx-mm6r-gg8w"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-24754"
    },
    {
      "type": "WEB",
      "url": "https://github.com/brefphp/bref/commit/c77d9f5abf021f29fa96b5720b7b84adbd199092"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/brefphp/bref"
    },
    {
      "type": "WEB",
      "url": "https://github.com/brefphp/bref/blob/2.1.12/src/Event/Http/Psr7Bridge.php#L130-L168"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Bref vulnerable to Body Parsing Inconsistency in Event-Driven Functions"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Sightings

Author Source Type Date

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.


Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…