# REPRO-2026-00108: Zumba JSON Serializer PHP Object Injection ## Summary Status: published Severity: high Type: security Confidence: Unknown ## Identifiers REPRO ID: REPRO-2026-00108 GHSA: GHSA-v7m3-fpcr-h7m2 CVE: CVE-2026-27206 ## Package Name: zumba/json-serializer Ecosystem: composer Affected: < 3.2.3 Fixed: 3.2.3 ## Root Cause # Root Cause Analysis Report ## GHSA-v7m3-fpcr-h7m2: Zumba Json Serializer PHP Object Injection via @type ### Summary The `zumba/json-serializer` library prior to version 3.2.3 contains a PHP Object Injection vulnerability in its JSON deserialization functionality. The `JsonSerializer::unserialize()` method accepts a special `@type` field in JSON that specifies which PHP class to instantiate. In vulnerable versions, there is no validation or restriction on which classes can be specified, allowing an attacker to instantiate arbitrary PHP classes. When combined with classes that have dangerous magic methods like `__wakeup()` or `__destruct()`, this can lead to gadget chain attacks and potentially Remote Code Execution (RCE). ### Impact **Package:** zumba/json-serializer **Affected Versions:** < 3.2.3 **Patched Version:** 3.2.3 **CVE:** CVE-2026-27206 **CVSS Score:** 8.1 (HIGH) **Risk Level:** High - This vulnerability allows arbitrary class instantiation when attacker-controlled JSON is passed to the deserializer. If the application or its dependencies contain classes with dangerous magic methods (such as `__wakeup()` or `__destruct()`), this may lead to: - Remote Code Execution (RCE) via gadget chains - Data exfiltration - File manipulation - Denial of service **Prerequisites for Exploitation:** 1. Untrusted/attacker-controlled JSON passed to `JsonSerializer::unserialize()` 2. Presence of exploitable classes (gadget chains) in the application or dependencies ### Root Cause The vulnerability exists in the `unserializeObject()` method in `src/JsonSerializer/JsonSerializer.php`. When deserializing JSON containing an `@type` field: 1. The method extracts the class name from `@type` without any validation 2. It checks if the class exists using `class_exists($className)` 3. If the class exists, it instantiates the class directly using `new $className()` or `$ref->newInstanceWithoutConstructor()` 4. No check is performed to restrict which classes may be instantiated **Vulnerable code pattern (v3.2.2):** ```php protected function unserializeObject($value) { $className = $value[static::CLASS_IDENTIFIER_KEY]; // @type value unset($value[static::CLASS_IDENTIFIER_KEY]); // ... recursion handling ... if (!class_exists($className)) { throw new JsonSerializerException('Unable to find class ' . $className); } // No allowed class check - ANY class can be instantiated! $ref = new ReflectionClass($className); $obj = $ref->newInstanceWithoutConstructor(); // ... if (method_exists($obj, '__wakeup')) { $obj->__wakeup(); // Magic method called! } } ``` **The Fix (v3.2.3):** The fix introduces a `setAllowedClasses(?array $allowedClasses)` method that allows applications to specify which classes may be instantiated during deserialization. When configured, the `unserializeObject()` method validates the class name against the allowed list before instantiation. **Fix commit:** `88b44c5 Adding method to restrict classes allowed on unserialize` ### Reproduction Steps The reproduction script is located at: `repro/reproduction_steps.sh` **What the script does:** 1. Clones the zumba/json-serializer repository 2. Checks out the vulnerable version 3.2.2 3. Installs PHP dependencies via composer 4. Creates and executes a PHP test script that: - Instantiates the JsonSerializer - Creates a malicious JSON payload with `@type` specifying an arbitrary class - Calls `unserialize()` with the malicious payload - Verifies that the arbitrary class is instantiated - Confirms that `__wakeup()` is called (demonstrating the danger) - Checks for the absence of `setAllowedClasses()` method **Expected Evidence:** ``` [TEST] Malicious JSON payload: {"@type":"VulnerableTarget","data":"injected"} [VULNERABILITY CONFIRMED] __wakeup() was called on arbitrary class! [RESULT] Class instantiated: VulnerableTarget [RESULT] __wakeup() called: YES [CHECK] setAllowedClasses() method: MISSING (vulnerable) === CONCLUSION === VULNERABILITY CONFIRMED: Arbitrary class instantiation works! ``` ### Evidence **Log Location:** `logs/repro_output.log` **Key Excerpts:** ``` === GHSA-v7m3-fpcr-h7m2 Reproduction Script === [TEST] Malicious JSON payload: {"@type":"VulnerableTarget","data":"injected"} [VULNERABILITY CONFIRMED] __wakeup() was called on arbitrary class! [RESULT] Class instantiated: VulnerableTarget [RESULT] Data value: injected [RESULT] __wakeup() called: YES [CHECK] setAllowedClasses() method: MISSING (vulnerable) VULNERABILITY CONFIRMED: Arbitrary class instantiation works! ``` **Environment Details:** - PHP Version: 8.4.18 - Package: zumba/json-serializer v3.2.2 - OS: Linux (Debian-based) ### Recommendations / Next Steps **Immediate Actions:** 1. **Upgrade** to version 3.2.3 or later immediately 2. **Configure allowlist:** After upgrading, use `setAllowedClasses()` to restrict which classes may be deserialized: ```php $serializer = new JsonSerializer(); $serializer->setAllowedClasses(['AllowedClass1', 'AllowedClass2']); ``` 3. **Audit** applications using this library to ensure they don't process untrusted JSON input **Testing Recommendations:** 1. Add unit tests that attempt to instantiate disallowed classes 2. Implement input validation before passing JSON to the deserializer 3. Consider using a Web Application Firewall (WAF) rule to detect `@type` fields in untrusted input 4. Review dependency tree for exploitable gadget chains (using tools like PHPGGC) **Long-term:** - Consider using alternative serialization formats with built-in type safety - Implement defense-in-depth with additional input validation layers ### Additional Notes **Idempotency Confirmation:** The reproduction script has been run twice consecutively with identical results: - Run 1: Exit code 0 (vulnerability confirmed) - Run 2: Exit code 0 (vulnerability confirmed) **Edge Cases:** - The vulnerability only affects applications that deserialize untrusted JSON input - Applications that only deserialize trusted/internal data are not affected - The impact depends on available gadget chains in the application or its dependencies **Limitations:** - This reproduction demonstrates the class instantiation capability but does not demonstrate actual RCE - Full RCE would require a suitable gadget chain in the target application dependencies - The fix in v3.2.3 is opt-in (requires calling `setAllowedClasses()`) for backward compatibility ## Reproduction Details Reproduced: 2026-02-20T15:03:21.151Z Duration: 686 seconds Tool calls: 93 Turns: 61 Handoffs: 2 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00108 pruva-verify GHSA-v7m3-fpcr-h7m2 pruva-verify CVE-2026-27206 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00108&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00108/artifacts/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - GitHub Advisory: https://github.com/advisories/GHSA-v7m3-fpcr-h7m2 - NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-27206 ## Artifacts - repro/rca_report.md (analysis, 6495 bytes) - repro/reproduction_steps.sh (reproduction_script, 2689 bytes) - bundle/source.json (other, 8178 bytes) - bundle/ticket.json (other, 12518 bytes) - bundle/ticket.md (ticket, 3581 bytes) - logs/repro_output.log (log, 503 bytes) - logs/variant_advanced_test.log (log, 909 bytes) - logs/variant_fixed_test.log (log, 1290 bytes) - logs/variant_tests_advanced.log (log, 909 bytes) - logs/variant_tests_fixed.log (log, 1290 bytes) - logs/variant_tests_vulnerable.log (log, 469 bytes) - logs/variant_vulnerable_test.log (log, 469 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00108 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00108/artifacts/repro/reproduction_steps.sh - Web: https://pruva.dev/r/REPRO-2026-00108 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://pruva.dev