symptom
The host’s antivirus flagged the account. Opening the store in a browser showed nothing wrong, and neither did curl, PageSpeed or an uptime monitor. The cloak carried a list of user agents to stay quiet for — curl, wget, python, Ahrefs, Semrush, Lighthouse, PageSpeed, GTmetrix, UptimeRobot, ChatGPT, Claude. Only a request that looked like Googlebot got the casino page.
how they got in: the map
On 28 August a script went through known holes in third-party OpenCart themes, one request each, checking after every try whether a shell had appeared. The fourth try was this store’s theme: a “load more” controller passed POST data straight to unserialize(). Twenty-four seconds from the first request to a working shell. Seven hours later the script came back to install a file manager; that evening a person, from another IP and a real browser, uploaded a backdoor panel. The cloak went in on 1 September.
The same line sat in five places — four theme controllers and a blog cookie. One had been used; all five were open.
The two footholds did not depend on each other. One was a block injected at the top of index.php, after which the attacker reset the file’s modification date with touch — the change time, which cannot be forged without root, gave it away. The other was a .user.ini that loaded the cloak before every request, whatever index.php contained. Removing either one alone would have changed nothing for Googlebot.
what was done
- The way in found in the access log, not guessed: the request at 04:49:05 that created the first shell, and the theme endpoints the script had tried before it.
- The cloak switched off through its own kill switch, and checked by fetching the homepage as Googlebot — casino title before, the store’s title after.
- All five
unserializecalls locked to plain data, including the generated copy of one controller that OpenCart actually runs: patching only the original would have left the busiest endpoint open. “Load more” checked working afterwards; an object payload checked rejected. - Shells and the backdoor panel moved to quarantine with their hashes, then deleted. The injected
index.phpcut back and compared with a clean copy — identical byte for byte. - The whole hosting account swept for the campaign’s markers, prepend files and recently changed PHP. Nothing left.
what it did not need
No rebuild, no new theme, no security-plugin subscription. The store stayed open the whole time.
what came after
Handed over to the owner: rotate the database, FTP and admin passwords — the attacker had a file manager for days and could read the config; check Search Console for an owner nobody added, because a verification file appeared three minutes after the cloak; forbid PHP execution in the image and upload folders. Afterwards every store the agency maintains was checked for the same class of hole — 22 stores, listed in the index as the security audit.