Backdoored and Abandoned Extensions: Supply-Chain Compromise of the Magento Ecosystem
Magekwik Security 5 min read
Commercial Magento extensions run with core-level privilege on every request, which makes their license-check files an ideal home for a dormant backdoor. We dissect the 2025 Tigren/Meetanshi/MGS compromise, the 2022 FishPig/Rekoobe breach, and the polyfill.io skimmer — plus the exact grep and CSP checks to find and shut them down.
What it is
Third-party extension code is included on every request through app/etc/config.php and each module's registration.php. That gives a vendor's src/ tree the same execution privilege as core. When the vendor's build pipeline, distribution server, or a hosted script they load is compromised, you inherit a remote-controlled backdoor with zero action on your part beyond composer update. This is one of the most powerful footholds on a store: it runs server-side as the web user, before any WAF or CSP applies, and it survives reindexing, cache flushes, and most reinstalls because the malicious file is the shipped file.
Two distinct shapes exist. First, a compromised distribution channel that ships tampered PHP inside an otherwise legitimate module. Second, a hosted browser script (a CDN-delivered JS include) whose ownership or infrastructure changes hands and starts serving a skimmer. Both are supply-chain compromises; the first executes on your server, the second in your customers' browsers.
Root cause
The server-side variant almost always abuses a license-check file. Commercial extensions ship a "phone home" licensing routine, and that file is the ideal host for a backdoor: it is expected to make network calls, expected to be obfuscated, and rarely reviewed because it is boilerplate. The pattern Sansec documented is a function that reads an uploaded "license" and executes its contents as PHP:
// adminLoadLicense() include()s a file written by adminUploadLicense().
// The "license" is attacker-supplied PHP, gated by a hardcoded vendor key.
public function adminLoadLicense($licenseFile) {
// request auth checked against a hardcoded secret/sign key,
// then the "license" payload is executed as code:
include($licenseFile); // arbitrary PHP → RCE
}
The deeper root cause is trust transitivity. Composer pulls signed archives, but a signature only proves the archive matches what the vendor built — not that the vendor's build was clean. There is no reproducible-build or per-file provenance check between a Magento marketplace vendor and your vendor/ directory. For hosted scripts, the root cause is loading executable JavaScript from a domain you do not control, where a domain expiry or acquisition silently replaces the payload.
How attackers abuse it
The server-side flow is patient. Backdoor code is committed to the vendor's product years before use, sits dormant through dozens of releases, then is activated remotely by sending a crafted request to the endpoint the license file exposes. Because the trigger is a hardcoded secret/sign key per vendor, only the operator can fire it, and scanners see nothing until then. Once triggered, typical post-exploitation is: create a rogue admin, disable 2FA, inject a client-side skimmer into checkout, or write a second-stage webshell to a writable path.
Dormant then weaponised
Who got hit / real examples
Tigren, Meetanshi, and MGS (Magesolution), 2025. Sansec found identical backdoor code in 21 extensions from three vendors, injected between 2019 and 2022 and activated in April 2025. The payload lived in License.php/LicenseApi.php; its adminLoadLicense function executed an uploaded file as PHP. Between 500 and 1,000 stores were compromised, including one belonging to a $40 billion multinational. Vendor responses were inconsistent — one denied the breach while allegedly still serving the tampered code.
FishPig, 2022. The vendor's own distribution server was breached on or before 19 August 2022. Injected code in Helper/License.php downloaded lic.bin — the Rekoobe RAT — from license.fishpig.co.uk whenever the FishPig control panel was opened, then masqueraded as crond/auditd in memory (C2 at 46.183.217.223). Any paid module installed or updated after that date was suspect.
polyfill.io, 2024. The client-side variant: after the domain changed ownership, cdn.polyfill.io began injecting conditional malware into 100,000+ sites, redirecting mobile users and setting up for skimming. It suppressed itself for admin users and analytics tooling to evade discovery.
How to check
Grep your deployed tree — not the marketplace listing — for the license-file pattern and for direct code execution inside vendor modules.
# License files that also execute code — the exact 2025 pattern
grep -rilE 'License(Api)?\.php$' vendor/ app/code/
# Direct execution primitives inside third-party modules
grep -rnE '\b(eval|assert|system|exec|passthru|shell_exec)\s*\(' \
vendor/tigren vendor/meetanshi vendor/mgs app/code/
# include()/require() of a runtime-written path (license payload trick)
grep -rnE '(include|require)(_once)?\s*\(\s*\$' vendor/ app/code/
# Confirm shipped files match the published package (detect tampering)
composer install --dry-run
diff -r vendor/<vendor>/<pkg> $(composer config vendor-dir)/<vendor>/<pkg>
For hosted scripts, enumerate every external origin the storefront loads and treat any script from a domain you do not own as untrusted:
grep -rhoE 'https?://[^"'\'' ]+\.js' app/design/ vendor/ pub/static/ \
| sort -u | grep -vE 'yourdomain\.com'
Cross-check running processes against known IOCs (unexpected crond/auditd children, outbound connections from PHP-FPM), and review the admin user table for accounts created outside your change window.
How to fix it
- Remove the malicious file. For the 2025 incident the fix is literally to delete the counterfeit
License.php/LicenseApi.phpand reinstall the module from a source you have verified clean — do not simplycomposer updatefrom the same compromised channel. - Assume full server compromise once a backdoor activated: rotate admin credentials, the
crypt/key, integration tokens, and API keys; kill and audit unexpected processes; rebuild from a known-good image where feasible. - Pin and vendor your dependencies. Commit
composer.lock, review diffs on every extension bump, and stage updates before production. - Eliminate hosted-script risk: self-host third-party JavaScript, apply Subresource Integrity where you cannot, and enforce a strict script-src CSP so an origin swap cannot execute new code in checkout.
- Adopt server-side integrity monitoring that diffs the deployed tree against the packaged hashes, so a tampered vendor file is flagged at deploy time rather than at breach time.
Vendor denial is not clearance
References & sources
Primary sources — advisories, vendor research, and the CVE record. Verify against these, not us.
- Sansec — License backdoor: 21 Magento extensions from Tigren, Meetanshi and MGS
- BleepingComputer — Magento supply chain attack compromises hundreds of e-stores
- Security Affairs — Sansec uncovered a supply chain attack via 21 backdoored Magento extensions
- Sansec — Magento vendor FishPig hacked, Rekoobe backdoor added
- Sansec — Polyfill supply chain attack hits 100K+ sites