Selected work

WordPress incident response

The site looked fine. Google saw casino links for five and a half months.

A Polish legal and accounting firm suspected that its WordPress sites had been hacked. We backed everything up, traced the attack, removed the spam and closed every path the attacker had used.

WordPress page editor with hidden spam blocks highlighted and a two-factor login prompt
Hidden blocks pushed off-screen with position:absolute. Invisible to visitors, visible to search engines.

The request: find out what happened before touching anything

The firm runs three WordPress sites on one shared hosting account: a corporate site, a services site and a help centre. Nobody knew how deep the problem went, and nobody wanted a cleanup that would destroy the evidence.

“The sites are hacked, at least one. Back them up, scan files and databases. Do not change anything yet.”

01

What the investigation found

5.5 months
hidden spam on the home page and the services page
8 links
to casino, payday loan and crypto exchange sites, in 7 hidden blocks
6,000+
password guesses through xmlrpc.php in six weeks
3.8 GB
of old site archives that anyone could download by URL

02

The attacker logged in with a real administrator password

Every spam edit came from a genuine administrator account after a normal login that succeeded on the first try. Brute force was running too, but the attacker already had the password. The two clean sites shared the same administrators.

  1. May 28

    First hidden link inserted on the home page.

  2. July

    Payday loan, casino and crypto exchange links added in two more waves.

  3. September 28

    The services page gets a block of Lithuanian casino links.

  4. November 3

    Last edit to the home page, from an AWS IP address.

  5. November 12

    Backup, investigation and cleanup begin.

What we did, in order

  1. 01

    Backup first

    Files, databases, access logs and old archives from all three sites, about 10 GB, copied locally before any analysis. The databases were saved byte for byte so Polish and Cyrillic text could be restored exactly.

  2. 02

    Investigate

    WordPress core was compared file by file with the official release. Plugins, themes, uploads and mu-plugins were scanned for web shells, databases for hidden blocks and foreign admins, logs for logins and edits. We also checked what Googlebot sees to rule out cloaking.

  3. 03

    Clean

    30 spam blocks removed from 11 database records, including 9 old revisions, so the spam cannot come back through a revision restore. Each update was checked against the record hash, and the visible page text was left unchanged.

  4. 04

    Close the doors

    Public archives deleted, a fake plugin and a backdoor folder removed, xmlrpc.php blocked, security keys rotated to end every session, database passwords changed. A stranger’s email that received password resets was replaced in 15 places.

  5. 05

    Patch and harden

    Remote code execution in WPML (CVSS 9.9) and arbitrary file read in Smart Slider 3 Pro closed with one-line patches. A crashed admin panel fixed. Updates reconfigured, and two-factor login made mandatory for every admin and editor.

03

Rules we worked by on a live production site

Evidence before cleanup

Nothing changed on the server until the backup was verified and the findings were reported.

Approval for every write

Each change was proposed, approved by the client and only then applied.

A backup next to every edit

Every edited file and table was saved first, so each step can be reverted in minutes.

Reversible by design

When the client wanted a removed file manager back, it was restored from the backup in a minute.

04

The most dangerous finding was on a “clean” site

Two old archives in the public web root of the main site returned HTTP 200 to anyone who guessed the URL. Archives like these usually contain wp-config.php with database passwords. We cannot prove this is how the admin password leaked, but it was the most likely candidate. The archives now return 404.

Technical notes

Access was SSH plus the cPanel UAPI. Work was done through WP-CLI and direct SQL over a latin1 connection, with byte-level replacement and MD5 checks so multilingual content stayed intact.

The spam was hidden with position:absolute and offsets such as left:-503011px, plus white or 8–12 px text. Two copies had been pasted as escaped HTML, so visitors could see raw code on the home page.

Patches: WPML no longer renders language-switcher shortcode content as a Twig template (CVE-2024-6386). Smart Slider 3 Pro export now requires edit permission and a security token (CVE-2026-3098).

Two-factor login uses an authenticator app (TOTP) with backup codes and a 30-day grace period. Admins received a one-page PDF guide with screenshots and the QR codes blurred.

The investigation was run by an AI coding agent working over SSH under human control. The agent did the log analysis, file comparison and database work. People made every decision and approved every change.

Suspect your WordPress site has been compromised?

We will find out what happened before anything gets deleted.

Request an investigation