# [CRIT] [GHSA / CRITICAL] CVE-2026-48062: CodeIgniter4 has a validation bypass when uploading file extensions via `ext_in` rule

**Source:** GitHub Security Advisories
**Published:** 2026-06-11
**Article:** https://github.com/advisories/GHSA-2gr4-ppc7-7mhx

## Threat Profile

CodeIgniter4 has a validation bypass when uploading file extensions via `ext_in` rule

### Impact
The `ext_in` upload validation rule checked the MIME-derived guessed extension instead of the client-provided filename extension. As a result, an uploaded file named `shell.php` containing GIF-like content could pass validation such as:
```
uploaded[avatar]|is_image[avatar]|mime_in[avatar,image/gif]|ext_in[avatar,gif]
```
because the detected MIME type maps to `gif`, even though the uploaded filenam…

## Indicators of Compromise (high-fidelity only)

- **CVE:** `CVE-2026-48062`

## MITRE ATT&CK Techniques

- **T1190** — Exploit Public-Facing Application
- **T1505.003** — Server Software Component: Web Shell
- **T1036.008** — Masquerading: Masquerade File Type
- **T1059.001** — Command and Scripting Interpreter: PowerShell
- **T1059.003** — Command and Scripting Interpreter: Windows Command Shell
- **T1059.004** — Command and Scripting Interpreter: Unix Shell

## Kill chain phases observed

_(none detected from narrative keywords)_

## Recommended hunts

### Webserver process writes PHP-executable file to public web-root or upload directory (CVE-2026-48062)

`UC_43_1` · phase: **install** · confidence: **High** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=true count min(_time) as first_seen max(_time) as last_seen values(Filesystem.file_hash) as sha256 values(Filesystem.process) as process_cmd from datamodel=Endpoint.Filesystem where Filesystem.action IN ("created","modified","renamed") AND Filesystem.process_name IN ("php-fpm.exe","php-cgi.exe","httpd.exe","nginx.exe","w3wp.exe","caddy.exe","php.exe") AND (Filesystem.file_name="*.php" OR Filesystem.file_name="*.phar" OR Filesystem.file_name="*.phtml" OR Filesystem.file_name="*.phps" OR Filesystem.file_name="*.pht") AND (Filesystem.file_path="*\\public\\*" OR Filesystem.file_path="*\\webroot\\*" OR Filesystem.file_path="*\\htdocs\\*" OR Filesystem.file_path="*\\www\\*" OR Filesystem.file_path="*\\uploads\\*" OR Filesystem.file_path="*\\public_html\\*" OR Filesystem.file_path="*/public/*" OR Filesystem.file_path="*/webroot/*" OR Filesystem.file_path="*/htdocs/*" OR Filesystem.file_path="*/www/*" OR Filesystem.file_path="*/uploads/*" OR Filesystem.file_path="*/public_html/*") by Filesystem.dest Filesystem.user Filesystem.process_name Filesystem.file_path Filesystem.file_name | `drop_dm_object_name(Filesystem)` | sort - last_seen
```

**Defender KQL:**
```kql
DeviceFileEvents
| where Timestamp > ago(7d)
| where ActionType in ("FileCreated","FileRenamed","FileModified")
| where InitiatingProcessFileName in~ ("php-fpm.exe","php-cgi.exe","httpd.exe","nginx.exe","w3wp.exe","caddy.exe","php.exe")
| where FileName endswith ".php" or FileName endswith ".phar" or FileName endswith ".phtml" or FileName endswith ".phps" or FileName endswith ".pht"
| where FolderPath has_any (@"\public\", @"\webroot\", @"\htdocs\", @"\www\", @"\uploads\", @"\public_html\", "/public/", "/webroot/", "/htdocs/", "/www/", "/uploads/", "/public_html/")
| where InitiatingProcessAccountName !endswith "$"
| project Timestamp, DeviceName, FolderPath, FileName, SHA256, FileSize, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName, InitiatingProcessFolderPath
| order by Timestamp desc
```

### Webserver / PHP interpreter spawns shell or LOLBin — post-upload RCE indicator

`UC_43_2` · phase: **exploit** · confidence: **High** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=true count min(_time) as first_seen max(_time) as last_seen values(Processes.process) as cmdline values(Processes.parent_process) as parent_cmd from datamodel=Endpoint.Processes where Processes.parent_process_name IN ("php-fpm.exe","php-cgi.exe","httpd.exe","nginx.exe","w3wp.exe","caddy.exe","php.exe") AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","sh.exe","bash.exe","wscript.exe","cscript.exe","mshta.exe","certutil.exe","bitsadmin.exe","curl.exe","wget.exe","whoami.exe","net.exe","net1.exe","reg.exe","hostname.exe","ipconfig.exe","systeminfo.exe") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name | `drop_dm_object_name(Processes)` | sort - last_seen
```

**Defender KQL:**
```kql
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("php-fpm.exe","php-cgi.exe","httpd.exe","nginx.exe","w3wp.exe","caddy.exe","php.exe")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","sh.exe","bash.exe","wscript.exe","cscript.exe","mshta.exe","certutil.exe","bitsadmin.exe","curl.exe","wget.exe","whoami.exe","net.exe","net1.exe","reg.exe","hostname.exe","ipconfig.exe","systeminfo.exe","tasklist.exe")
| project Timestamp, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, FileName, ProcessCommandLine, FolderPath, SHA256
| order by Timestamp desc
```

### HTTP multipart upload: image Content-Type with PHP/executable filename extension (CVE-2026-48062 exploit shape)

`UC_43_3` · phase: **delivery** · confidence: **High** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
`waf` OR `cef` request_method=POST (uri_path="*/upload*" OR uri_path="*/avatar*" OR uri_path="*/uploads*" OR uri_path="*/file*" OR uri_path="*/profile*" OR uri_path="*/image*") (file_mime="image/gif" OR file_mime="image/jpeg" OR file_mime="image/png" OR file_mime="image/webp" OR file_mime="image/bmp" OR content_type="image/*")
| regex filename="(?i)\.(php|phar|phtml|phps|pht)$"
| stats count min(_time) as first_seen max(_time) as last_seen values(filename) as filenames values(file_mime) as mimes values(user_agent) as user_agents by src_ip dest http_host uri_path
| sort - last_seen
```

### Vulnerable CodeIgniter4 framework inventory (CVE-2026-48062, < 4.7.3)

`UC_43_4` · phase: **recon** · confidence: **High** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=true count min(_time) as first_seen max(_time) as last_seen values(Vulnerabilities.signature) as signature values(Vulnerabilities.severity) as severity values(Vulnerabilities.cvss) as cvss from datamodel=Vulnerabilities.Vulnerabilities where Vulnerabilities.cve="CVE-2026-48062" OR (Vulnerabilities.signature="*codeigniter4*" AND Vulnerabilities.signature="*ext_in*") by Vulnerabilities.dest | `drop_dm_object_name(Vulnerabilities)` | sort - last_seen
```

**Defender KQL:**
```kql
DeviceTvmSoftwareVulnerabilities
| where CveId =~ "CVE-2026-48062"
   or (SoftwareVendor =~ "codeigniter" and SoftwareName has "codeigniter4")
| join kind=leftouter DeviceInfo on DeviceId
| project DeviceId, DeviceName, OSPlatform, OSVersion, IsInternetFacing, SoftwareVendor, SoftwareName, SoftwareVersion, CveId, VulnerabilitySeverityLevel, RecommendedSecurityUpdate, RecommendedSecurityUpdateId
| order by IsInternetFacing desc, DeviceName asc
```

### IOC-driven hunts (use shared templates)

These are standard IOC-substitution hunts — the canonical SPL and KQL live once in [`_TEMPLATES.md`](../_TEMPLATES.md), so we don't repeat the same boilerplate on every CVE / hash / network-IOC briefing.

- **Asset exposure — vulnerability matches article CVE(s)** ([template](../_TEMPLATES.md#asset-exposure)) — phase: **recon**, confidence: **High**
  - CVE(s): `CVE-2026-48062`


## Why this matters

Severity classified as **CRIT** based on: CVE present, 5 use case(s) fired, 6 technique(s) inferred. Read the full article for actor attribution, tooling details, and any defanged IOCs in the body that aren't visible in the RSS summary.
