Security
Gehackte WordPress-site herkennen: audit in vijf minuten
Een klant mailt op vrijdagmiddag. Google heeft de site geflagd. Dit is de audit van zeven commando's die we draaien vóór de trein van 17:30, op volgorde.

Een klant mailt op vrijdag om 16:40. Google heeft hun homepage geflagd als deceptive site ahead. De site is een zes jaar oude WordPress-installatie waar hun ticketverkoop voor evenementen op draait. Ze willen twee dingen weten vóór ze hun advocaat bellen: zijn we gehackt, en hoe erg is het. Jij hebt nog één koffie over en een trein om 17:30.
Dit is de audit die we draaien. Het kost ongeveer vijf minuten als je SSH en een databasewachtwoord hebt, en het beantwoordt de enige vraag die aan het begin telt: is de site gecompromitteerd, ja of nee.
Triage in 90 seconden
Voordat je SSH-t, bekijk de site van buitenaf. Open hem in een privévenster. Zoek het domein in Google met site:domain.com en scroll voorbij de eerste pagina. Een gecompromitteerde WordPress-installatie krijgt vrijwel altijd SEO-spampagina's: Japanse apotheek, replicatassen, nepessays. Als die onder het domein van je klant verschijnen, is de compromittering echt en waarschijnlijk al weken oud.
Check daarna Google's transparency report op transparencyreport.google.com en plak het domein erin. Als Safe Browsing het heeft geflagd, heb je een tijdstempel van wanneer Google het opmerkte. Dat is genoeg om te weten of je doorgaat. Meestal is het antwoord ja.
Adminusers en rollen
SSH erin. Als wp-cli geïnstalleerd is (en dat is meestal het geval op elke hosting die het overwegen waard is), is het eerste commando:
wp user list --role=administrator --fields=user_login,user_email,user_registeredJe zoekt twee dingen. Accounts die je niet herkent, en aanmaakdatums die rond hetzelfde uur clusteren. Aanvallers die RCE krijgen, maken vrijwel altijd een back-up-admin aan met een geloofwaardige naam (wpadmin, support, wp_user_42). Soms passen ze in plaats daarvan het e-mailadres van een bestaande admin aan, dus vergelijk de e-mailadressen met het echte team van je klant.
Als wp-cli niet beschikbaar is, ga je direct op de database af:
SELECT u.user_login, u.user_email, u.user_registered
FROM wp_users u
JOIN wp_usermeta m ON u.ID = m.user_id
WHERE m.meta_key = 'wp_capabilities'
AND m.meta_value LIKE '%administrator%';Pas de wp_-prefix aan. Veel oudere sites gebruiken een eigen prefix, en dat is op zichzelf al een aanwijzing als die niet matcht met wat de live config zegt.
Wijzigingstijden van bestanden in wp-content
Het snelste signaal van een compromittering is bestanden die zijn gewijzigd na de laatst bekende deploy van de site. Vanaf de WordPress-root:
find . -type f -mtime -14 \
-not -path './wp-content/cache/*' \
-not -path './wp-content/uploads/202*/*.jpg' \
-not -path './wp-content/uploads/202*/*.png' \
| sortDat geeft je alles wat in de laatste veertien dagen is aangeraakt. Scan het. Alles in wp-includes of wp-admin is vrijwel zeker fout: core-bestanden veranderen niet tussen WordPress-updates door. Alles met .php binnen wp-content/uploads is fout, punt, geen uitzonderingen. WordPress schrijft in normaal gebruik nooit uitvoerbare PHP in de uploads-directory.
Een tweede ronde grenst het af tot de ergste gevallen:
find ./wp-content/uploads -type f -name "*.php"
find . -type f -name "*.php" -mtime -7 \
| xargs grep -l -E "(eval|base64_decode|gzinflate|str_rot13)\s*\("De grep is ruisig (sommige legitieme plugins gebruiken base64 voor icoontjes), maar haalt voor de hand liggende webshells in seconden naar boven. Vind je een bestand met de naam wp-login-old.php, xmlrpc-backup.php, of iets dat lijkt op een plausibel klinkend core-bestand op de verkeerde plek, dan heb je je antwoord.
Verwijder niets van wat je tijdens de audit vindt. Je hebt de tijdstempels en inhoud intact nodig voor de opschoonfase, en voor de verzekeringsclaim als de klant een cyberpolis heeft. Zet bestanden in quarantaine door ze buiten de webroot te verplaatsen, nooit met rm.
Het wp-config- en .htaccess-checkpoint
Open wp-config.php. Kijk naar de bovenkant en de onderkant. Aanvallers houden ervan om één regel voor of achter de bestaande code te plakken die een remote bestand inlaadt of een auto_prepend-handler registreert. Zie je iets vóór <?php of na de afsluitende PHP-tag (er hoort geen afsluitende PHP-tag in een schone wp-config.php te staan), dan is dát de compromittering.
Dan .htaccess in de root. Het WordPress-blok is bekend en stabiel. Alles buiten de # BEGIN WordPress- en # END WordPress-markeringen is verdacht. Let specifiek op:
RewriteCond %{HTTP_USER_AGENT} (google|bing|yandex) [NC]
RewriteRule .* http://evil.example/redirect.php [R=302,L]Dat patroon, zoekmachine-crawlers omleiden maar echte bezoekers de normale site tonen, is hoe SEO-spaminfecties zich maandenlang verstoppen voor de site-eigenaar. De eigenaar bezoekt de site en ziet zijn site. Google bezoekt de site en ziet een apotheek.
Database-options die de moeite waard zijn
Twee wp_options-rijen vertellen je veel:
SELECT option_value FROM wp_options WHERE option_name = 'siteurl';
SELECT option_value FROM wp_options WHERE option_name = 'active_plugins';Als siteurl niet matcht met het productiedomein, is de site herschreven om assets vanaf een aanvallershost te laden. Als active_plugins een slug bevat die niet op schijf bestaat in /wp-content/plugins, is het een fantoomplugin die van elders wordt geladen. Beide zijn ondubbelzinnig.
Nu je toch bezig bent:
SELECT option_name FROM wp_options
WHERE option_name LIKE '%transient%'
AND LENGTH(option_value) > 50000;Webshells verbergen soms payloads in overdreven grote transients. Een transient van 200KB zonder duidelijk doel is een rode vlag.
Geplande taken en de cron-tabel
WP-Cron is een comfortabele plek om een persistentiemechanisme te verstoppen. Lijst hem:
wp cron event listOnbekende hooks die elke vijftien minuten draaien (vooral iets dat lijkt op wp_update_check, wp_remote_call, of een hex-string van 32 tekens) zijn hoe aanvallers een opgeschoonde site binnen een dag opnieuw infecteren. Als je alleen de bestanden opruimt en de cron mist, infecteert de site zichzelf opnieuw vóór je klaar bent met de post-mortem.
Wanneer je de stekker eruit trekt
Drie bevindingen brengen de site in nu offline halen-gebied: een webshell in uploads, een aangepaste wp-config.php, of een onbekende admin met een recente login. Eén daarvan betekent dat de aanvaller code-execution heeft gehad. Twee of meer betekent dat ze die waarschijnlijk nog steeds hebben.
De reflex is om de foute bestanden te verwijderen en door te gaan. Doe het niet. De snellere zet is de site achter een onderhoudspagina plaatsen (een statisch HTML-bestand in de docroot dat een 503 teruggeeft, is genoeg), een snapshot maken van het bestandssysteem en de database voor forensisch onderzoek, en daarna de rebuild starten vanaf een bekende, schone back-up die is gemaakt vóór het vroegste verdachte bestandstijdstempel.
De vijfminutenaudit geeft je een ja of nee, geen fix. De taak ervan is beslissen of het volgende telefoontje naar de klant “we zijn oké” wordt, of “we gaan naar onderhoudsmodus terwijl we onderzoeken”.
De bredere context
De economie achter WordPress-compromitteringen is dit jaar verslechterd. Geautomatiseerde scanners draaien binnen uren, niet weken, door nieuw onthulde plugin-CVE's heen. Het gat tussen een kwetsbaarheid die in de WPScan vulnerability database belandt en de eerste opportunistische exploit die de installatie van je klant bereikt, wordt inmiddels in dezelfde werkdag gemeten. De audit hierboven voorkomt dat niet. Het zorgt er alleen voor dat je het ontdekt vóór de aanvaller tijd heeft om zich in te graven. De Sucuri-uitleg over een gehackte WordPress-site opschonen is nog steeds de juiste referentie voor de volgende fase.
Toen we deze audit vorige maand draaiden voor een Nederlands evenementenplatform, was de boosdoener één bestand van 14KB op wp-content/uploads/2022/04/index.php dat er al elf maanden stond. Het verwijderen was het makkelijke deel. Het moeilijke deel was de toegangsweg terugleiden naar een ongepatchte plugin-CVE uit eind 2022. Die checklist hebben we daarna opgenomen in onze standaard legacy-migratie-overdracht, zodat de volgende die een site overneemt hem op dag één kan draaien.
Vijf minuten investering. Sla de zeven commando's hierboven op in één shellscript op je laptop. De volgende keer dat een klant belt over een geflagde site, heb je een antwoord vóór het water kookt.
Kern
De vijfminuten-WordPress-audit bestaat om een ja of nee te leveren, geen fix: haal je de site nu offline, of ga je door met je dag?
FAQ
Kan ik gewoon WordPress core opnieuw installeren om een hack te repareren?
Core opnieuw installeren overschrijft wp-admin en wp-includes, maar laat wp-content, de database en alle toegevoegde adminusers intact. Dus nee, het repareert de meeste echte compromitteringen niet.
Hoe lang duurt een volledige opschoning na een positieve audit?
Reken op een halve dag voor een kleine site als je een schone back-up van vóór de compromittering hebt, twee tot vijf dagen zonder. De meeste tijd gaat zitten in het identificeren van de toegangsweg, zodat de site zich niet opnieuw infecteert.
Vangt een plugin als Wordfence dit voor mij op?
Soms. Wordfence en vergelijkbare plugins vangen bekende signatures en onverwachte bestandswijzigingen op, maar een goed afgestelde aanvaller kan stilletjes onder hun drempelwaarden blijven. Draai sowieso een handmatige audit.
Is er een tool die de zeven commando's automatiseert?
WPScan, Sucuri SiteCheck en het wp-cli-security-package dekken het meeste af. Wij doen de handmatige checks toch, omdat tools de contextuele signalen missen, zoals personeelsnamen en deploy-tijdstempels.