Someone left the safety off the upload gun, and now any visitor can aim it at your webroot
CVE-2026-63222 is a path traversal flaw in CodeIgniter 4 prior to version 4.7.4. When application code calls UploadedFile::move() without supplying a second argument (the target filename), the framework defaults to using the raw, unsanitized client-provided filename. An attacker can inject path traversal sequences like ../../public/shell.php into the filename field of a multipart upload, writing arbitrary content outside the intended upload directory. Because this is PHP, landing a .php file in a web-accessible path typically means immediate remote code execution.
The vendor's HIGH / 7.5 score is defensible at the framework level — unauthenticated, network-reachable, low complexity. However, it overstates real-world exposure because exploitation requires the application developer to have used the one-argument form of move(). Applications that explicitly pass a server-generated filename as the second argument are not affected. The CVSS vector also scores only Integrity impact (C:N/I:H/A:N), ignoring the practical RCE chain that follows arbitrary file write in PHP environments — so it simultaneously underrates the blast radius of a successful exploit while overrating the breadth of affected deployments.
4 steps from start to impact.
Identify upload endpoint
- CodeIgniter 4 < 4.7.4 application with a file upload endpoint
- Endpoint reachable over HTTP/HTTPS
- Many upload endpoints sit behind authentication
- WAFs or reverse proxies may strip path traversal sequences from filenames
Craft traversal filename
filename field in the Content-Disposition header contains path traversal sequences such as ../../../public/cmd.php. The payload is a PHP webshell or similar script. No special encoding is required — the framework passes the raw value through.- Application calls
UploadedFile::move($targetDir)without a second filename argument - Server filesystem permissions allow writing to the traversal target
- Developers who pass a hardcoded or generated filename as the second argument to
move()are NOT vulnerable open_basedirPHP restrictions may block writes outside the app root- Containerized deployments with read-only filesystem layers block the write
File lands outside upload directory
public/ webroot, a writable/ directory, or any path served by Apache/Nginx.- Web server process has write permissions on the resolved target path
- No OS-level mandatory access control (SELinux/AppArmor) blocking the write
- Hardened deployments separate the writable upload dir from the webroot
- SELinux
httpd_sys_rw_content_tlabels prevent writes to unexpected paths
.php files.Remote code execution via webshell
- Written file is in a path served by the web server with PHP execution enabled
- PHP execution not disabled in the target directory via
.htaccessor nginx config
- Security-conscious configs disable PHP execution in upload/static directories
- EDR agents on the host will flag anomalous child processes spawned by the PHP worker
www-data spawning whoami, id, or reverse shells). YARA rules for common PHP webshell signatures.The supporting signals.
| In-the-Wild Exploitation | No evidence of active exploitation as of 2026-07-31. Not listed on CISA KEV. |
|---|---|
| Proof of Concept | No public PoC exploit identified. The traversal payload is trivial to construct (../../filename.php in the upload filename field), so weaponization requires minimal effort once a vulnerable endpoint is found. |
| EPSS Score | Not yet scored — CVE disclosed today (2026-07-31). Expect low-to-moderate initial EPSS given the application-specific prerequisite. |
| KEV Status | Not listed on CISA KEV catalog. |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N — Network-accessible, no auth, no user interaction. Integrity-only impact in the vendor's assessment, but practical RCE follows in PHP environments. |
| Affected Versions | CodeIgniter 4 < 4.7.4 (all 4.x releases prior to the fix). |
| Fixed Version | CodeIgniter 4.7.4 — released 2026-07-31. Update via composer update codeigniter4/framework. |
| Exposure Data | ~120,000 live websites use CodeIgniter per BuiltWith. Subset on CI4 with the vulnerable move() pattern is significantly smaller. No Shodan/GreyNoise scanning campaigns observed. |
| Disclosure Date | 2026-07-31 (coordinated disclosure, patch available same day). |
| Advisory IDs | GHSA-c9w5-rwh3-7pm9 (primary); GHSA-hhmc-q9hp-r662 (related upload validation hardening). |
noisgate verdict.
The single most decisive factor for the downgrade from 7.5 to 6.5 is the application-specific code pattern requirement: only CodeIgniter apps that call UploadedFile::move() without a second filename argument are vulnerable, materially narrowing the exploitable population below what a blanket 7.5 implies. The verdict remains HIGH rather than MEDIUM because in the subset of apps that do use this pattern, the chain from unauthenticated upload to arbitrary file write to RCE is low-friction and high-impact.
Why this verdict
- Unauthenticated remote vector with low complexity — the CVSS base metrics (AV:N/AC:L/PR:N/UI:N) are accurate at the framework level; the vulnerable
move()call does not validate filenames at all. - Application-pattern prerequisite narrows exposure — only apps calling
move($dir)without the second argument are affected. Developers who generate server-side filenames (a common best practice) are immune. This meaningfully reduces the reachable population below what a 7.5 implies. - PHP file write = practical RCE — the vendor's Integrity-only scoring (C:N/A:N) understates the real blast radius. In PHP environments, arbitrary file write to a web-accessible path is functionally equivalent to full server compromise. This prevents a deeper downgrade.
- Role multiplier: CodeIgniter is a general-purpose PHP web framework, not a canonically high-value infrastructure component. Typical deployments are line-of-business web applications. The blast radius of a successful exploit is per-application/per-host, not fleet-scale or identity-scale. A compromised CI4 app does not directly yield domain admin, hypervisor access, or supply-chain pivot. This role profile does not trigger the HIGH/CRITICAL floor rule.
Why not higher?
The vulnerability cannot be upgraded to CRITICAL because it requires a specific application code pattern — not all CodeIgniter 4 installations are vulnerable by default. CodeIgniter is not a canonically high-value-role component (it's not an IdP, hypervisor, domain controller, or security agent), so the blast radius is limited to the individual application host. There is no evidence of active exploitation or public PoC weaponization as of disclosure day.
Why not lower?
Despite the code-pattern prerequisite, the vulnerability cannot be reduced to MEDIUM because when the pattern IS present, the attack is unauthenticated, remotely exploitable, requires no user interaction, and results in arbitrary file write that chains trivially to RCE in PHP. The traversal payload is dead simple to construct — no specialized tooling or deep target knowledge required. The absence of exploitation evidence reflects only that the CVE is hours old.
What to do — in priority order.
- Audit application code for one-argument
move()calls — Search your codebase for->move(calls that pass only a directory argument without a filename. If found, add an explicit server-generated filename as the second argument immediately. This eliminates the vulnerability regardless of framework version. Do this within the noisgate mitigation SLA of 30 days for HIGH. - Upgrade to CodeIgniter 4.7.4 — Run
composer update codeigniter4/frameworkto pull the patched version. The fix sanitizes the default filename when no second argument is provided. Target completion within the noisgate remediation SLA of 180 days, but prefer sooner given the trivial exploit path. - Disable PHP execution in upload directories — Configure your web server (Apache
.htaccesswithphp_flag engine off, or nginxlocationblock withdeny allfor.php) to prevent execution of any PHP files in upload directories. This breaks the RCE chain even if the file write succeeds. - Deploy WAF rule to strip path traversal from upload filenames — Add a WAF rule (ModSecurity, Cloudflare, AWS WAF) that blocks or sanitizes multipart upload requests where the
filenamefield inContent-Dispositioncontains../or..\sequences. This provides defense-in-depth at the network edge. - Enable file integrity monitoring on webroot — Configure FIM (Wazuh, OSSEC, Tripwire) to alert on new
.phpfiles appearing in web-accessible directories. This won't prevent the write but ensures rapid detection of successful exploitation.
- Network segmentation alone — the upload endpoint is the application's intended public surface; network controls don't help unless you can block all untrusted uploads entirely.
- Content-Type validation — the traversal is in the *filename* field, not the file content or MIME type. Validating that the upload is an image doesn't prevent the path traversal.
open_basedirin most configurations —open_basedirrestricts PHP file *reads*, butmove_uploaded_file()(which UploadedFile wraps) often operates outsideopen_basedirrestrictions depending on PHP version and SAPI.
Crowdsourced verification payload.
Run this on each target host where a CodeIgniter 4 application is deployed. Requires read access to the Composer lock file in the project root. Example: bash check_cve_2026_63222.sh /var/www/myapp
#!/usr/bin/env bash
# check_cve_2026_63222.sh — Check for CVE-2026-63222 (CodeIgniter UploadedFile path traversal)
# Usage: bash check_cve_2026_63222.sh /path/to/codeigniter4/project
# Requires: read access to composer.lock; jq installed
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN
set -euo pipefail
PROJECT_DIR="${1:-}"
if [ -z "$PROJECT_DIR" ]; then
echo "Usage: $0 /path/to/codeigniter4/project"
exit 2
fi
LOCKFILE="$PROJECT_DIR/composer.lock"
if [ ! -f "$LOCKFILE" ]; then
echo "UNKNOWN — composer.lock not found at $LOCKFILE"
exit 2
fi
if ! command -v jq &>/dev/null; then
echo "UNKNOWN — jq is required but not installed"
exit 2
fi
# Extract codeigniter4/framework version from composer.lock
CI_VERSION=$(jq -r '.packages[] | select(.name == "codeigniter4/framework") | .version' "$LOCKFILE" 2>/dev/null | sed 's/^v//')
if [ -z "$CI_VERSION" ] || [ "$CI_VERSION" = "null" ]; then
echo "UNKNOWN — codeigniter4/framework not found in composer.lock"
exit 2
fi
echo "Detected codeigniter4/framework version: $CI_VERSION"
# Compare versions: vulnerable if < 4.7.4
# Use sort -V for version comparison
MIN_SAFE="4.7.4"
OLDEST=$(printf '%s\n%s' "$CI_VERSION" "$MIN_SAFE" | sort -V | head -n1)
if [ "$CI_VERSION" = "$MIN_SAFE" ] || [ "$OLDEST" = "$MIN_SAFE" ]; then
echo "PATCHED — version $CI_VERSION >= $MIN_SAFE"
exit 0
else
echo "VULNERABLE — version $CI_VERSION < $MIN_SAFE (CVE-2026-63222)"
# Bonus: check for vulnerable code pattern
echo ""
echo "Checking for potentially vulnerable move() calls (one-argument pattern):"
grep -rn '->move(' "$PROJECT_DIR/app" --include='*.php' 2>/dev/null | grep -v ',\s*' || echo " No one-argument move() calls found in app/ (may still exist in other dirs)"
exit 1
fiIf you remember one thing.
composer audit) or the verification script above across your CodeIgniter 4 deployments to identify instances running < 4.7.4. Simultaneously, search your codebase for ->move( calls that omit the second filename argument — those are the actually exploitable instances. For any confirmed vulnerable pattern, add an explicit server-generated filename as the second argument immediately as a code-level mitigation. Under the noisgate mitigation SLA for HIGH severity, deploy compensating controls (WAF traversal rules, disable PHP in upload dirs) within 30 days. Under the noisgate remediation SLA, upgrade all instances to CodeIgniter 4.7.4 via composer update within 180 days, though given the trivial exploit chain, pushing the Composer update this week is strongly recommended. No active exploitation is known, but weaponization is trivially easy — don't wait for a scanner to tell you someone already did it.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.