VYPR
High severityNVD Advisory· Published Jan 4, 2022· Updated Apr 23, 2025

Deserialization of Untrusted Data in Codeigniter4

CVE-2022-21647

Description

CodeIgniter is an open source PHP full-stack web framework. Deserialization of Untrusted Data was found in the old() function in CodeIgniter4. Remote attackers may inject auto-loadable arbitrary objects with this vulnerability, and possibly execute existing PHP code on the server. We are aware of a working exploit, which can lead to SQL injection. Users are advised to upgrade to v4.1.6 or later. Users unable to upgrade as advised to not use the old() function and form_helper nor RedirectResponse::withInput() and redirect()->withInput().

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Deserialization of untrusted data in CodeIgniter4's old() function allows remote attackers to inject arbitrary objects, potentially leading to SQL injection.

Vulnerability

A deserialization of untrusted data vulnerability exists in CodeIgniter4 versions prior to v4.1.6. The old() function in the form_helper insecurely unserializes user-supplied data from session-flashed input. The function checks if the value is a string starting with 'a:' or 's:' and calls unserialize() on it, allowing injection of auto-loadable arbitrary objects. Affected versions include all CodeIgniter 4 releases before v4.1.6. [1][2]

Exploitation

An attacker must be able to send crafted HTTP POST parameters to an endpoint that uses old() to repopulate form fields, or trigger a RedirectResponse::withInput() or redirect()->withInput() call. The framework stores the serialized input in the session and later unsafely unserializes it. A known working exploit demonstrates that this can be leveraged for SQL injection. No special privileges are required beyond network access to the application. [1][2]

Impact

Successful exploitation allows a remote, unauthenticated attacker to inject arbitrary serialized PHP objects. This can result in execution of existing PHP code on the server (PHP object injection). The known exploit path leads to SQL injection, which could allow data theft, modification, or destruction. The impact is high, potentially leading to full compromise of the database and server. [1][2]

Mitigation

The vulnerability is fixed in CodeIgniter v4.1.6, released on 2021-12-30. Users should upgrade immediately to v4.1.6 or later. For those unable to upgrade, the recommended workaround is to avoid using the old() function and form_helper, as well as avoiding RedirectResponse::withInput() and redirect()->withInput(). No KEV listing is available for this CVE. [1][2]

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
codeigniter4/frameworkPackagist
< 4.1.64.1.6

Affected products

3

Patches

1
ce95ed576525

Merge pull request from GHSA-w6jr-wj64-mc9x

5 files changed · +48 12
  • SECURITY.md+1 0 modified
    @@ -20,6 +20,7 @@ This person will coordinate the fix and release process, involving the following
     - Confirm the problem and determine the affected versions.
     - Audit code to find any potential similar problems.
     - Prepare fixes for all releases still under maintenance. These fixes will be released as fast as possible.
    +- Publish security advisories at https://github.com/codeigniter4/CodeIgniter4/security/advisories
     
     ## Comments on this Policy
     
    
  • system/Common.php+0 5 modified
    @@ -810,11 +810,6 @@ function old(string $key, $default = null, $escape = 'html')
                 return $default;
             }
     
    -        // If the result was serialized array or string, then unserialize it for use...
    -        if (is_string($value) && (strpos($value, 'a:') === 0 || strpos($value, 's:') === 0)) {
    -            $value = unserialize($value);
    -        }
    -
             return $escape === false ? $value : esc($value, $escape);
         }
     }
    
  • tests/system/CommonFunctionsTest.php+35 2 modified
    @@ -301,15 +301,48 @@ public function testOldInput()
             $_GET     = ['foo' => 'bar'];
             $_POST    = [
                 'bar'    => 'baz',
    -            'zibble' => serialize('fritz'),
    +            'zibble' => 'fritz',
             ];
     
             $response = new RedirectResponse(new App());
             $response->withInput();
     
             $this->assertSame('bar', old('foo')); // regular parameter
             $this->assertSame('doo', old('yabba dabba', 'doo')); // non-existing parameter
    -        $this->assertSame('fritz', old('zibble')); // serialized parameter
    +        $this->assertSame('fritz', old('zibble'));
    +    }
    +
    +    /**
    +     * @runInSeparateProcess
    +     * @preserveGlobalState  disabled
    +     */
    +    public function testOldInputSerializeData()
    +    {
    +        $this->injectSessionMock();
    +        // setup from RedirectResponseTest...
    +        $_SERVER['REQUEST_METHOD'] = 'GET';
    +
    +        $this->config          = new App();
    +        $this->config->baseURL = 'http://example.com/';
    +
    +        $this->routes = new RouteCollection(Services::locator(), new Modules());
    +        Services::injectMock('routes', $this->routes);
    +
    +        $this->request = new MockIncomingRequest($this->config, new URI('http://example.com'), null, new UserAgent());
    +        Services::injectMock('request', $this->request);
    +
    +        // setup & ask for a redirect...
    +        $_SESSION = [];
    +        $_GET     = [];
    +        $_POST    = [
    +            'zibble' => serialize('fritz'),
    +        ];
    +
    +        $response = new RedirectResponse(new App());
    +        $response->withInput();
    +
    +        // serialized parameters are only HTML-escaped.
    +        $this->assertSame('s:5:&quot;fritz&quot;;', old('zibble'));
         }
     
         /**
    
  • tests/system/HTTP/IncomingRequestTest.php+7 5 modified
    @@ -107,8 +107,10 @@ public function testMissingOldInput()
             $this->assertNull($this->request->getOldInput('pineapple.name'));
         }
     
    -    // Reference: https://github.com/codeigniter4/CodeIgniter4/issues/1492
    -    public function testCanGetOldInputArray()
    +    /**
    +     * @see https://github.com/codeigniter4/CodeIgniter4/issues/1492
    +     */
    +    public function testCanGetOldInputArrayWithSESSION()
         {
             $_SESSION['_ci_old_input'] = [
                 'get'  => ['apple' => ['name' => 'two']],
    @@ -119,13 +121,13 @@ public function testCanGetOldInputArray()
             $this->assertSame(['name' => 'foo'], $this->request->getOldInput('banana'));
         }
     
    -    // Reference: https://github.com/codeigniter4/CodeIgniter4/issues/1492
    -
         /**
    +     * @see https://github.com/codeigniter4/CodeIgniter4/issues/1492
    +     *
          * @runInSeparateProcess
          * @preserveGlobalState  disabled
          */
    -    public function testCanSerializeOldArray()
    +    public function testCanGetOldInputArrayWithSessionService()
         {
             $locations = [
                 'AB' => 'Alberta',
    
  • user_guide_src/source/changelogs/v4.1.6.rst+5 0 modified
    @@ -9,6 +9,11 @@ Release Date: Not released
         :local:
         :depth: 2
     
    +SECURITY
    +********
    +
    +- *Deserialization of Untrusted Data* found in the ``old()`` function was fixed. See the `Security advisory <https://github.com/codeigniter4/CodeIgniter4/security/advisories/GHSA-w6jr-wj64-mc9x>`_ for more information.
    +
     BREAKING
     ********
     
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.