For nørder: Incident response & hardening af et hacket WordPress-site
Denne artikel er til dig, der vil forstå hvor angribere gemmer sig, hvordan WordPress loader kode, og hvordan man rydder korrekt op uden at efterlade persistence.
Det er ikke en “scan & håb”-guide, men en praktisk gennemgang baseret på reelle kompromitteringer.
1. Hvor angribere oftest gemmer sig (og hvorfor)
wp-content/uploads/
Det mest misbrugte område.
Hvorfor
- Skrivbar mappe
- Fyldt med filer (støj)
- Ofte overset
Røde flag
- PHP-filer i uploads
- Filer som image.php, wp.php, admin-ajax.php
- jpg.php, png.php
Tjek
find wp-content/uploads -type f -name "*.php"
wp-content/mu-plugins/ (kritisk område)
MU-plugins indlæses automatisk og kan ikke deaktiveres i admin.
Hvorfor attraktivt for angribere
- Kører altid
- Overlever plugin-oprydning
- Overlever tema-skift
- Overses ofte
Tjek
ls -la wp-content/mu-plugins
Vigtigt: Ikke alle MU-plugins er malware
MU-plugins bruges legitimt i bl.a.:
- hosting-setup
- enterprise-setup
- page builders (fx Elementor)
Eksempel: elementor-safe-mode.php
Denne fil:
- er legitim
- bruges af Elementor til fejlsøgning
- ligger korrekt i mu-plugins/
- indeholder læsbar kode uden obfuscation
Det er ikke et tegn på hack i sig selv.
Mistænkeligt hvis
- sitet ikke bruger Elementor
- koden er obfuskeret
- filen loader eksterne scripts
- den genskabes efter sletning uden forklaring
wp-includes/ (core manipulation)
Angribere ændrer kernefiler for dyb persistence.
Tjek altid
wp core verify-checksums
Hvis én fil ikke matcher, betragtes sitet som kompromitteret.
Aktivt tema (functions.php, footer.php)
Klassisk sted for injektion.
Tegn
- Obfuskeret kode nederst
- add_action(‘wp_footer’, …)
- eval(base64_decode())
.htaccess
Bruges til:
- redirects
- cloaking
- mobile-only infektioner
Røde flag
RewriteCond %{HTTP_USER_AGENT}
RewriteRule ^ https://spam.example [R,L]
wp-config.php
Sjældnere, men kritisk.
Tjek
- kode efter /* That’s all, stop editing! */
- require_once til ukendte paths
Databasen (ofte overset)
Malware findes i databasen i ca. 20–35 % af reelle hacks.
Bruges primært til
- SEO-spam
- JavaScript-injektion
- persistence / reinfektion
Typiske tabeller
- wp_options
- wp_posts
- wp_usermeta
Databasen bruges sjældent alene, men ofte som supplement til filbaseret malware.
2. Hvad er MU-plugins teknisk?
MU-plugin = Must-Use Plugin.
- Ligger i wp-content/mu-plugins/
- Indlæses automatisk af WordPress
- Kræver ingen aktivering
- Kræver ingen konfiguration i wp-config.php
Hvis en PHP-fil ligger direkte i mappen, bliver den eksekveret på hvert request.
Minimum
wp-content/mu-plugins/test.php
<?php
error_log('MU-plugin loaded');
Denne kode kører altid.
Vigtige MU-plugin-regler
- Kun PHP-filer i roden indlæses automatisk
- Undermapper kræver manuel require
- Fejl i MU-plugin = fatal fejl for hele sitet
- Kan kun fjernes via filsystemet
3. Hurtig teknisk triage
Find nyligt ændrede PHP-filer
find . -type f -name "*.php" -mtime -7
Scan for klassisk obfuscation
grep -RIn -E "eval\(|base64_decode\(|gzinflate\(|str_rot13\(|assert\(" .
4. Reparer WordPress core korrekt (wp-cli)
Tjek kernefiler
wp core verify-checksums
OK
Success: WordPress installation verifies against checksums.
Kompromitteret
Warning: File doesn't verify against checksum
Reparer
wp core download --force
5. Stop de mest almindelige angrebsveje
Blokér PHP i uploads
<FilesMatch "\.php$">
Require all denied
</FilesMatch>
location ~* /wp-content/uploads/.*\.php$ {
deny all;
}
Luk xmlrpc.php (hvis muligt)
<Files xmlrpc.php>
Require all denied
</Files>
define('DISALLOW_FILE_EDIT', true);
6. Cron jobs – persistence-mekanismen
wp cron event list
Mistænkelige hooks:
- tilfældige navne
- kører hvert minut
- loader eksterne scripts
Slet:
wp cron event delete suspicious_hook
7. Database-tjek (minimum)
wp db query "
SELECT ID FROM wp_posts
WHERE post_content LIKE '%casino%'
OR post_content LIKE '%viagra%';
"
wp option get siteurl
wp option get home
8. MU-plugin til “incident mode” (hurtig lockdown)
<?php
if (!defined('ABSPATH')) exit;
remove_action('wp_head', 'wp_generator');
add_filter('xmlrpc_enabled', '__return_false');
add_filter('rest_authentication_errors', function () {
if (!is_user_logged_in()) {
return new WP_Error(
'rest_disabled',
'REST API locked',
['status' => 401]
);
}
});
9. Den korte incident-checkliste
- Skift alle adgangskoder
- Force logout
wp user session destroy --all
- Blokér PHP i uploads
- Reparer WordPress core
- Fjern og geninstallér plugins/temaer
- Gennemgå MU-plugins
- Tjek cron jobs
- Tjek database
- Aktivér WAF og file monitoring
Den vigtigste pointe
Oprydning uden forståelse for load order, MU-plugins og persistence er midlertidig. Hvis MU-plugins, cron og database ikke er gennemgået, er oprydningen ufuldstændig – også selvom scanneren siger “clean”.