Back to portfolio
Bezpečnost webů Joomla

Backdoor and stored XSS in one incident

An anonymized case where both the Joomla file system and database layer were compromised.

A local healthcare website. This case shows well why malware removal cannot be settled by swapping files: the file layer and the database layer were compromised simultaneously, and either one on its own could have restored the infection.

What we found

In the site root sat a standalone script that downloaded and wrote further payloads, and beside it a second, unauthenticated upload backdoor disguised as a helper file from an entirely different CMS — a file that looks at first glance like a leftover from an old migration.

The database also held a persistent JavaScript injection in the main menu settings. The payload stored its code in the browser’s localStorage and reinserted it into the page repeatedly. In practice that means the script would have returned even after the files were cleaned.

The webroot additionally contained an outdated installation package and older backups reachable from the internet. The installation package was never confirmed as the entry point — but it was a risky leftover that did not belong in a restored site. With no access logs available, the attacker’s first request could not be established retrospectively.

How we handled it

We treated the file and database halves of the incident separately. We removed both the deployment and the upload backdoor, cleaned the specific database parameter, and checked the menu, the modules and the page-builder content for the same pattern elsewhere.

Old installation artefacts and backups were removed from the public webroot; users, sessions, permissions and access were reviewed; every secret was rotated. After deploying the clean restored copy we verified frontend behaviour — with XSS stored in the database, a visual check after recovery is essential — and closed the work with a security scan.

Outcome

The case required cleaning the webroot and the database at the same time. Replacing the files alone would have left the XSS in the administration; deleting the database payload alone would have left the mechanism that redeployed the malware.

The takeaway

A compromise can span several layers at once: files, database, accounts and publicly reachable backups. A recovery has to verify all of them and end with a check against reinfection. And publicly reachable backups and installation packages belong out of the webroot even when they are not proven to have been the entry point.