Journal

Anatomy of a storefront compromise

An unpatched extension, a fake system daemon, a backdoor and a checkout skimmer. How a high-traffic store was compromised and how we took it back.

A high-traffic Magento storefront came to us after customers complained that card details used on the site were being misused. The store looked normal. It was taking orders. Nobody on the team had seen anything odd. This is what a modern eCommerce compromise looks like: quiet, profitable for the attacker, and invisible until the bank calls.

We are deliberately vague about who and which vulnerability. The pattern, though, is worth sharing because it repeats across stores we have not met yet.

How they got in

Entry was a known vulnerability in a third-party extension that had not been patched. Not the Magento core, not the hosting, not a stolen password. A plugin that shipped with the theme, that nobody remembered installing, with a fix available for weeks. From that foothold the attacker ran code as the web server user.

What they left behind

Three things, each designed to survive a casual look.

  • A fake system daemon. A binary named after a legitimate time-sync service, running from a writable directory, with a cron entry to resurrect it if killed.
  • A backdoor in a public file. A small PHP file under the store’s public directory, named like a normal helper, that let the attacker re-enter without the original exploit.
  • A checkout skimmer. A few lines of JavaScript injected into the checkout that copied card fields to a domain chosen to look like a CDN. It only fired on the payment step and only for real browsers.

Credentials stored on the box were assumed exposed: database, API keys, mail passwords. All of it had to be rotated.

What we did, in order

  1. Contain. Block the exfiltration domain at the edge, kill the implant and its cron, remove the backdoor, take the skimmer out of checkout. Customers were protected within the hour; everything after that is cleanup.
  2. Preserve evidence. Logs, file timestamps, the binaries themselves, before any rebuild. You cannot tell the client what happened if you have deleted the evidence.
  3. Establish the timeline. First malicious request, first implant, first skimmer load. The client needs dates for their bank, their payment provider and, under POPIA, potentially the regulator.
  4. Patch the entry. The extension, every other extension, the core. Then verify the exploit no longer works.
  5. Rotate everything. Every credential the attacker could have read.
  6. Re-sweep. A second full sweep of the box days later, because attackers come back to check on their work.

Hardening that stuck

Each site on the server now runs in its own PHP pool with a restricted filesystem view, so one compromised store cannot read another’s configuration. The checkout’s static files are integrity-checked. Admin and API endpoints that bots were hammering are rate-limited or closed. Patches are applied on a schedule, with a staging environment to prove they do not break checkout first. Nightly backups go to a separate host and a monthly copy leaves the building.

The uncomfortable truth

None of this was sophisticated. It was an unpatched plugin and a store nobody was watching. If you run Magento, WooCommerce or anything with a checkout and you cannot say when it was last patched and who would notice a new cron job, you are the next case. We can be the ones watching.

Got a similar problem? Talk to us.

Start a project