← Back to Feed CACHED · 2026-07-31 11:33:05 · CACHE_KEY CVE-2026-63223
CVE-2026-63223 · CWE-434 · Disclosed 2026-07-31

CodeIgniter is a PHP full-stack web framework.

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
Do you agree?
01 · The Real Story

CodeIgniter's bouncer checks your ID photo but never reads the name on the card

CVE-2026-63223 affects CodeIgniter 4 prior to version 4.7.4. The framework's is_image and mime_in upload validation rules classify a file solely by its content-derived MIME type (e.g., inspecting magic bytes). An attacker can prepend valid image magic bytes (GIF89a, PNG header, JFIF) to a PHP webshell, name it shell.php, and pass both is_image and mime_in validation. If the application then saves the file using the client-supplied filename via $file->move($path) into a web-accessible, script-enabled directory, the attacker requests /uploads/shell.php and achieves unauthenticated remote code execution. All CodeIgniter 4.x versions before 4.7.4 are affected.

The vendor assigned CVSS 9.8 CRITICAL, which models the theoretical worst case — unauthenticated, network-accessible, low-complexity. That score is defensible as a ceiling but overstates the median real-world deployment. Exploitation requires a specific compound of application-level coding patterns: the app must accept uploads, validate *only* with is_image/mime_in (no independent extension check), preserve the original client filename on disk, store into a web-accessible directory, and allow PHP execution from that directory. Each condition narrows the exploitable population. Many CodeIgniter applications already use $file->getRandomName(), store uploads outside the webroot, or gate uploads behind authentication. The vendor's 9.8 treats all of these as absent; real deployments rarely line up that perfectly.

"Framework upload validators trust MIME, ignore extension — RCE if app stores with client filename"
02 · The Attack Path

5 steps from start to impact.

STEP 01

Identify upload endpoint

The attacker discovers a CodeIgniter 4 application exposing a file upload form or API endpoint. This can be found through directory enumeration, spidering, or reading JavaScript source that references upload routes. The endpoint must accept user-controlled files.
Conditions required:
  • Target runs CodeIgniter 4 < 4.7.4
  • Application exposes a file upload endpoint reachable by the attacker
Where this breaks in practice:
  • Many upload endpoints require authentication (PR:N assumption fails)
  • Upload features are not universal — many CI apps are CRUD/API-only with no file upload
Detection/coverage: Web application scanners (Burp Suite, Nuclei) can fingerprint CodeIgniter version headers (X-Powered-By, Server) and enumerate upload routes.
STEP 02

Craft polyglot payload

The attacker creates a file named shell.php whose first bytes are valid image magic bytes (e.g., GIF89a followed by <?php system($_GET['cmd']); ?>). This polyglot passes is_image (which checks MIME via finfo) and mime_in (which checks the content-derived type matches an allowed list) while retaining the .php extension in the filename.
Conditions required:
  • Knowledge of allowed MIME types configured in the app's validation rules
Where this breaks in practice:
  • Trivial to construct — no real friction here
  • Public PoC patterns for GIF89a+PHP polyglots are widely documented
Detection/coverage: WAF rules inspecting upload bodies for PHP tags (<?php, <?=) can catch naive payloads. Yara rules for polyglot detection exist.
STEP 03

Upload and bypass validation

The attacker submits the polyglot via the upload endpoint. CodeIgniter's is_image rule calls getimagesize() or finfo_file(), both of which read magic bytes and return a valid image MIME type. The mime_in rule confirms the MIME matches the allowed list. Neither rule inspects the client-supplied filename extension. The upload is accepted.
Conditions required:
  • Application validates with is_image or mime_in only, without an independent ext_in or manual extension check
Where this breaks in practice:
  • Applications that also apply ext_in validation (even the pre-4.7.3 version) add a partial barrier
  • Custom validation callbacks checking extension would block this
Detection/coverage: Application-level logging of uploaded filenames can flag .php extensions in upload directories.
STEP 04

File lands with .php extension in webroot

The application saves the uploaded file using the client-supplied filename (e.g., $file->move('/var/www/html/uploads/')) without renaming. The file is now on disk as /var/www/html/uploads/shell.php and is directly accessible via HTTP.
Conditions required:
  • Application preserves original client filename on disk
  • Upload directory is under the web root
  • PHP execution is enabled in the upload directory (no .htaccess / nginx location block denying PHP)
Where this breaks in practice:
  • CodeIgniter docs recommend $file->getRandomName() — many apps follow this guidance
  • Security-conscious deployments store uploads outside webroot or on object storage (S3)
  • Many Apache/nginx configs disable PHP execution in upload directories via php_flag engine off or location ~ /uploads/ { deny all; }
Detection/coverage: File integrity monitoring (OSSEC, Wazuh, auditd) watching webroot for new .php files. EDR agents monitoring php-fpm/apache2 child processes.
STEP 05

Trigger webshell for RCE

The attacker requests https://target.com/uploads/shell.php?cmd=id via a browser or curl. The web server processes the file as PHP, executing the embedded code with the privileges of the web server user (typically www-data or apache). From here, the attacker has command execution on the host.
Conditions required:
  • Web server processes .php files in the upload directory
Where this breaks in practice:
  • PHP-FPM pool restrictions, open_basedir, disable_functions can limit post-exploitation
  • SELinux/AppArmor policies on the web server process can block lateral movement
Detection/coverage: EDR detecting shell spawns from web server processes. IDS/IPS signatures for webshell traffic patterns. GreyNoise or honeypot correlation for scanning of common webshell paths.
03 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNo confirmed in-the-wild exploitation as of 2026-07-31. Not listed on CISA KEV.
Proof of ConceptNo dedicated public PoC repository identified, but the technique is trivial — GIF89a+PHP polyglot uploads are a textbook CWE-434 pattern. The GitHub issue #3184 documents the is_image validation weakness.
EPSS ScoreNot yet scored (CVE published 2026-07-31, EPSS typically lags 24-48h). Expect moderate EPSS given framework-level conditions required.
KEV StatusNot listed on CISA KEV as of 2026-07-31.
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (9.8). Network vector, no auth, no interaction. The vector does not capture application-level preconditions.
Affected VersionsCodeIgniter 4.x < 4.7.4 (all 4.x releases). CodeIgniter 3.x is a separate codebase and is not affected by this specific advisory.
Fixed Version4.7.4 (released 2026-07-31). Also addresses CVE-2026-48062 (ext_in bypass) and GHSA-hhmc-q9hp-r662 (path traversal in UploadedFile::move()).
Related CVECVE-2026-48062ext_in rule checked MIME-derived extension instead of client filename extension. Fixed in 4.7.3.
Exposure DataCodeIgniter powers ~70K-128K websites globally per Enlyft and BuiltWith. Subset running CI4 (vs legacy CI3) is smaller. No Shodan/GreyNoise scanning campaigns identified yet.
DisclosurePublished 2026-07-31 via GHSA-mmj4-63m4-r6h5. Coordinated disclosure with patch available same day.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to HIGH (7.2/10)

The single most decisive factor driving the downgrade from CRITICAL to HIGH is the compound application-level preconditions: exploitation requires the target app to preserve client filenames, store in a script-enabled webroot directory, and lack independent extension validation — a conjunction that excludes the majority of CodeIgniter deployments. The vulnerability is real and weaponizable against misconfigured apps, but the 9.8 models a worst case that most real deployments do not present.

HIGH Vulnerability mechanics and affected version range
MEDIUM Fraction of CodeIgniter apps meeting all exploit preconditions
LOW In-the-wild exploitation status (CVE is hours old)

Why this verdict

  • Compound preconditions narrow the exploitable population. The chain requires four independent application-level choices to align: upload endpoint exists, no extension check, client filename preserved, PHP execution enabled in upload directory. Each condition roughly halves the eligible target set, compounding to a small fraction of CI4 deployments.
  • No authentication requirement is assumed, not guaranteed. The CVSS vector sets PR:N, but many real upload endpoints sit behind login walls, session checks, or CSRF tokens. The vector overstates the unauthenticated attack surface.
  • Role multiplier: CodeIgniter is a general-purpose web application framework. (a) *Low-value role:* dev/staging instances — minimal blast radius. (b) *Typical role:* line-of-business web apps — compromise yields one application's data and host-level access as www-data. (c) *High-value role:* a CI4 app serving as an internal admin panel or customer portal with access to databases holding PII/financial data. In this case the blast radius is tenant-to-host, potentially reaching database credentials and lateral movement. However, CI4 is NOT canonically a high-value-role component (it is not a DC, hypervisor, IdP, PAM, backup server, or network edge appliance). The fraction of CI4 installs in high-value roles is modest (<10%). The floor is therefore HIGH, not CRITICAL.
  • Polyglot payloads are trivial to construct. GIF89a+PHP webshells are commodity knowledge. No exploit development skill is required, lowering the attacker-capability bar significantly.
  • Patch is same-day. The fix (4.7.4) shipped concurrently with disclosure, giving defenders a clear remediation path and reducing the window of exposure for teams that act quickly.

Why not higher?

CRITICAL would require either active exploitation (KEV/campaign evidence), a canonical high-value-role component, or a condition-free attack path. None apply here. The vulnerability requires multiple application-coding decisions to align, and CodeIgniter is a general-purpose web framework — not an identity provider, hypervisor, or network edge device. The exploitable subset of the installed base is meaningfully smaller than the total.

Why not lower?

MEDIUM would understate the risk because when the preconditions DO align, the outcome is unauthenticated RCE — the most severe impact class. The polyglot technique is trivial, no exploit development is needed, and the pattern (upload + execute) is one of the most commonly targeted by automated scanners and botnets. A single vulnerable instance discovered by a scanner leads directly to host compromise with no further interaction.

05 · Compensating Control

What to do — in priority order.

  1. Store uploads outside the web root or on object storage — Move upload targets to a non-web-accessible path (e.g., /var/lib/app/uploads/) or use S3/GCS with signed URLs. This completely breaks step 5 of the attack chain. Deploy within 30 days per the noisgate mitigation SLA for HIGH severity.
  2. Randomize filenames on save — Replace $file->move($path) with $file->move($path, $file->getRandomName()) or use $file->store(). This strips the .php extension and prevents direct execution even if stored in the webroot. A code-level change deployable in a hotfix.
  3. Disable script execution in upload directories — Add php_flag engine off in .htaccess (Apache) or a location block returning 403 for .php in nginx upload paths. This is a server-config change that requires no application code modification.
  4. Add explicit extension validation — Append ext_in[field,jpg,png,gif,webp] to your validation rules alongside is_image/mime_in. On 4.7.4+ this checks the client filename extension. On older versions, combine with a manual check: pathinfo($file->getClientName(), PATHINFO_EXTENSION).
  5. Deploy WAF rules blocking PHP in upload payloads — Configure ModSecurity, Cloudflare WAF, or AWS WAF to inspect multipart upload bodies for PHP opening tags (<?php, <?=, <? ). This catches naive polyglots but can be bypassed with encoding.
What doesn't work
  • MIME-type validation alone — this IS the vulnerability. is_image and mime_in both check content-derived MIME types, which the attacker controls by prepending magic bytes.
  • File size limits — a PHP webshell can be as small as 30 bytes. Size restrictions do not mitigate this attack.
  • CSRF tokens — CSRF protection prevents cross-site request forgery but does not stop a direct attacker uploading to the endpoint with a valid session or when the endpoint is unauthenticated.
06 · Verification

Crowdsourced verification payload.

Run this script on each CodeIgniter 4 application host as a user with read access to the Composer lock file. Invoke with: bash check_cve_2026_63223.sh /var/www/myapp where the argument is the application root directory. No elevated privileges required.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_cve_2026_63223.sh — Detect CVE-2026-63223 in CodeIgniter 4
# Usage: bash check_cve_2026_63223.sh /path/to/ci4/app
set -euo pipefail

APP_ROOT="${1:-}"
if [ -z "$APP_ROOT" ]; then
  echo "Usage: $0 /path/to/ci4/app"
  exit 3
fi

LOCK_FILE="$APP_ROOT/composer.lock"
if [ ! -f "$LOCK_FILE" ]; then
  echo "UNKNOWN — composer.lock not found at $LOCK_FILE"
  exit 2
fi

# Extract codeigniter4/framework version from composer.lock
CI_VERSION=$(python3 -c "
import json, sys
with open('$LOCK_FILE') as f:
    data = json.load(f)
for pkg in data.get('packages', []) + data.get('packages-dev', []):
    if pkg['name'] == 'codeigniter4/framework':
        print(pkg['version'].lstrip('v'))
        sys.exit(0)
print('NOT_FOUND')
" 2>/dev/null || echo 'PARSE_ERROR')

if [ "$CI_VERSION" = "NOT_FOUND" ]; then
  echo "UNKNOWN — codeigniter4/framework not found in composer.lock"
  exit 2
fi

if [ "$CI_VERSION" = "PARSE_ERROR" ]; then
  echo "UNKNOWN — failed to parse composer.lock (python3 required)"
  exit 2
fi

echo "Detected codeigniter4/framework version: $CI_VERSION"

# Compare version — vulnerable if < 4.7.4
REQUIRED="4.7.4"
if python3 -c "
from packaging.version import Version
import sys
try:
    if Version('$CI_VERSION') < Version('$REQUIRED'):
        sys.exit(0)  # vulnerable
    else:
        sys.exit(1)  # patched
except Exception:
    # Fallback: simple string split comparison
    v = list(map(int, '$CI_VERSION'.split('.')[:3]))
    r = list(map(int, '$REQUIRED'.split('.')[:3]))
    sys.exit(0 if v < r else 1)
" 2>/dev/null; then
  echo "VULNERABLE — codeigniter4/framework $CI_VERSION is below $REQUIRED (CVE-2026-63223)"
  exit 1
else
  echo "PATCHED — codeigniter4/framework $CI_VERSION >= $REQUIRED"
  exit 0
fi
07 · Bottom Line

If you remember one thing.

TL;DR
CVE-2026-63223 is a same-day disclosure with a same-day patch. Update all CodeIgniter 4 applications to 4.7.4 immediately. Under the noisgate mitigation SLA for HIGH severity, deploy compensating controls (disable PHP execution in upload directories, randomize filenames, or move uploads out of the webroot) within 30 days. Under the noisgate remediation SLA, apply the vendor patch (Composer update to codeigniter4/framework:^4.7.4) within 180 days, though given the simplicity of exploitation when preconditions align, pushing the patch to production this week is strongly recommended. Audit your codebase for any upload handler that calls $file->move() with the client-supplied name — those paths are your highest-priority targets. If you cannot patch quickly, the single highest-value compensating control is adding a server-level directive to block PHP execution in upload directories.

Sources

  1. GHSA-mmj4-63m4-r6h5 — CodeIgniter4 Security Advisory
  2. CodeIgniter4 Security Advisories
  3. CVE-2026-48062 — ext_in Bypass (GitLab Advisory)
  4. GitHub Issue #3184 — is_image validation vulnerability
  5. CodeIgniter Market Share — Enlyft
  6. CWE-434: Unrestricted Upload of File with Dangerous Type
  7. CodeIgniter4 on CVE Details
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.