This guide is for developers and server administrators of OPD websites that have been defaced or injected with online gambling content (judol). The example commands use Linux with the docroot /var/www/situs; adjust the paths, user names, database names, and log locations to your server. Work in order: secure the evidence first, then clean up.
1. Recognize the signs
Judol injections are usually made so that administrators do not notice them. Common patterns:
- User-agent cloaking: gambling content is served only when the visitor is Googlebot, bingbot, or Google-InspectionTool, while regular browsers see the normal site.
- Hidden HTML blocks using
display:noneor off-screen positioning, containing rows of links to gambling sites. - Text or links inserted after the
</html>tag at the end of the page. - A
custom_htmlwidget (WordPress) or a footer text block containing paid-link network markers such asMagenetMonetization. - Unfamiliar owners or users in Google Search Console, usually random Gmail accounts, along with a new
google*.htmlverification file in the docroot. - Unfamiliar
.phpfiles in upload or asset folders, sometimes named like legitimate files such assitemap.phpor*.min.php. .htaccessis modified withRewriteCond %{HTTP_USER_AGENT}or%{HTTP_REFERER}rules.- Mobile visitors or visitors arriving from Google are redirected to a gambling site, while desktop visitors who type the address directly are not.
Check from outside by comparing the responses for a regular browser, Googlebot, and a mobile phone:
SITUS=https://namadomain.kutaitimurkab.go.id/
POLA='slot|gacor|togel|toto|maxwin|casino|judi|rtp'
# Jumlah baris yang cocok: browser biasa vs Googlebot
curl -s -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" "$SITUS" | grep -ciE "$POLA"
curl -s -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" "$SITUS" | grep -ciE "$POLA"
# Pengalihan untuk pengunjung HP yang datang dari Google
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' \
-A "Mozilla/5.0 (Linux; Android 14; Mobile) AppleWebKit/537.36 Chrome/130.0 Mobile Safari/537.36" \
-e "https://www.google.com/" "$SITUS"
# Apa pun yang ada sesudah </html>
curl -s -A "Googlebot" "$SITUS" | sed -n '/<\/html>/,$p' | head -20
Some cloakers check Google's IP addresses, so curl with a Googlebot user agent still receives the normal page. The most reliable method is URL Inspection in Google Search Console: run a live URL test, then open the crawled HTML to see the page exactly as Googlebot receives it.
2. Secure evidence before changing anything
Unfamiliar files, logs, and their modification times are needed to find the point of entry. If they are deleted straight away, the vulnerability is hard to find and the site will be broken into again. Keep a copy of the evidence outside the server once you are done.
B=/root/bukti-$(date +%Y%m%d-%H%M)
mkdir -p "$B"
# Salinan docroot lengkap dengan atribut waktu dan kepemilikan
tar --numeric-owner -cpzf "$B/docroot.tgz" -C /var/www situs
# Daftar berkas dengan mtime, ctime, dan pemilik
find /var/www/situs -type f -printf '%TY-%Tm-%Td %TH:%TM %CY-%Cm-%Cd %CH:%CM %u %p\n' | sort > "$B/daftar-berkas.txt"
# Log akses dan error, termasuk arsip .gz
cp -a /var/log/nginx /var/log/httpd /var/log/apache2 "$B/" 2>/dev/null
# Dump database
mysqldump --single-transaction nama_db > "$B/db.sql"
# Hash agar keutuhan bukti bisa dibuktikan
find "$B" -type f ! -name SHA256SUMS -exec sha256sum {} + > "$B/SHA256SUMS"
On cPanel servers, per-domain access logs are in /usr/local/apache/domlogs/ and their archives in /home/<account>/logs/.
3. Isolate the site
- Put up a maintenance page with HTTP status 503 or disable the virtual host during the cleanup. A 503 status tells Google that the site is temporarily unavailable. Nginx example:
return 503;in the site's server block, thennginx -t && systemctl reload nginx. - In cPanel/WHM, the hosting account can be suspended from WHM.
- Block the attacker IP addresses found in the logs (step 4) on the server firewall.
4. Look for webshells and modified files
D=/var/www/situs
# Berkas yang berubah dalam 14 hari terakhir (mtime)
find "$D" -type f -mtime -14 -printf '%TY-%Tm-%Td %TH:%TM %p\n' | sort
# ctime ikut dicek, karena mtime mudah dipalsukan dengan touch
find "$D" -type f -ctime -14 -printf '%CY-%Cm-%Cd %CH:%CM %p\n' | sort
# Skrip di folder upload dan aset (isinya seharusnya gambar, dokumen, CSS, JS)
find "$D" \( -path '*/upload*' -o -path '*/images/*' -o -path '*/assets/*' -o -path '*/storage/*' -o -path '*/media/*' \) \
-type f \( -iname '*.php*' -o -iname '*.phtml' -o -iname '*.phar' -o -iname '*.cgi' -o -iname '*.pl' -o -iname '*.py' \)
# Pola kode yang lazim di webshell dan backdoor
grep -rlE --include='*.php' --include='*.phtml' --include='*.inc' \
'eval\s*\(|assert\s*\(|base64_decode\s*\(|gzinflate\s*\(|str_rot13\s*\(|shell_exec|passthru|proc_open|\$_(POST|GET|REQUEST|COOKIE)\s*\[[^]]*\]\s*\(' "$D"
# Berkas verifikasi Google, tag verifikasi, dan .htaccess yang membaca user agent/referer
find "$D" -maxdepth 3 -name 'google*.html'
grep -rl 'google-site-verification' "$D" --include='*.php' --include='*.html'
grep -rn --include='.htaccess' -iE 'HTTP_USER_AGENT|HTTP_REFERER|googlebot' "$D"
# Penanda sisipan judol
grep -rliE 'MagenetMonetization|slot ?gacor|maxwin|togel' "$D" --include='*.php' --include='*.html' --include='*.js'
The grep results must be reviewed one by one, because legitimate libraries also use base64_decode or eval. The -ctime results also include files that were recently chmod-ed or restored from backup. If the code is deployed from git, run git status --porcelain in the docroot: files that git does not recognize are suspicious.
For WordPress, run WP-CLI as the user that owns the site:
cd /var/www/situs
wp core verify-checksums
wp plugin verify-checksums --all
wp user list --role=administrator --fields=ID,user_login,user_email,user_registered
wp option get widget_custom_html | grep -iE 'magenet|slot|togel'
wp db search 'MagenetMonetization' --all-tables
Look for the point of entry in the access logs. The attacker's first request to the webshell, or a POST to an upload feature just before the webshell appeared, usually reveals the vulnerability:
L=/var/log/nginx/access.log
# Siapa saja yang mengakses webshell yang ditemukan (ganti nama berkasnya)
zgrep -hE 'sitemap\.php|nama-shell\.php' $L* | awk '{print $1}' | sort | uniq -c | sort -rn
# POST ke skrip di folder upload atau aset
zgrep -hE '"POST [^"]*/(uploads?|images|assets|storage)/[^" ]*\.(php|phtml|phar)' $L*
# Seluruh POST dari satu IP pelaku, urut waktu (ganti IP contoh)
zgrep -h '^203\.0\.113\.10 ' $L* | grep '"POST' | sort -k4 | head -50
5. Check accounts, cron jobs, SSH keys, and persistence
# Pengguna terbaru di database CMS (contoh WordPress; sesuaikan prefiks tabel)
mysql nama_db -e "SELECT ID,user_login,user_email,user_registered FROM wp_users ORDER BY user_registered DESC LIMIT 20;"
# Cron semua pengguna dan cron sistem
for u in $(cut -d: -f1 /etc/passwd); do crontab -l -u "$u" 2>/dev/null | sed "s/^/$u: /"; done
ls -la /etc/cron.d /etc/cron.hourly /var/spool/cron* 2>/dev/null
# Kunci SSH yang diizinkan
for f in /root/.ssh/authorized_keys /home/*/.ssh/authorized_keys; do echo "== $f"; cat "$f" 2>/dev/null; done
# Persistensi di direktori home dan berkas startup shell
ls -la /home/*/.config /home/*/.local/bin 2>/dev/null
grep -nE 'base64|curl|wget|nohup|GS_|/dev/tcp' /home/*/.bashrc /home/*/.profile /root/.bashrc 2>/dev/null
# Proses non-root yang menyamar dengan nama proses kernel, dan koneksi keluar
ps -eo user=,pid=,args= | awk '$1!="root" && $3 ~ /^\[/'
ss -tupn
Backdoors such as gsocket are often placed in ~/.config or ~/.local, restarted via a cron job containing a base64 command, and run under a fake process name in square brackets. Genuine kernel processes are always owned by root.
6. Change all credentials
- All CMS admin accounts. Delete unknown accounts after recording them as evidence.
- Database passwords. Assume that the contents of
.env,wp-config.php, or other database configuration files have been read by the attacker. - API tokens, SMTP keys, and application secrets. In WordPress, change the salts with
wp config shuffle-salts. The LaravelAPP_KEYneeds to be planned, because data encrypted with the old key is affected as well. - FTP, SSH, and hosting panel accounts (cPanel/WHM/Plesk).
- The administrator email addresses linked to the accounts above.
Change credentials after the webshell has been removed, or on a replacement server. A new password created while the webshell is still active can be read again immediately.
7. Close the vulnerability
- Update the CMS, plugins, themes, framework, and PHP version. Compare the installed versions against the CVE catalog.
- Commonly found vulnerabilities: upload features without file-type validation, admin routes that can be opened without logging in, outdated plugins, debug mode left on in production, and weak admin passwords.
- Prevent script execution in upload folders. Put the rule in the virtual host configuration; an
.htaccessfile in the upload folder can be overwritten by the attacker.
# nginx: di dalam blok server, letakkan SEBELUM location ~ \.php$
location ~* /(uploads?|storage|media)/.*\.(php|phtml|phar|pl|py|cgi)$ { deny all; }
# Apache: di <Directory> folder upload pada konfigurasi virtual host
<FilesMatch "\.(php|phtml|phar|pl|py|cgi)$">
Require all denied
</FilesMatch>
8. Restore from a clean backup
- Determine the time of first entry from the logs and the file list, then choose a backup made before that time.
- Prefer reinstalling the code from official sources (the original CMS, plugin, and theme packages, or the git repository), then copy over only content such as images and documents after scanning them with the commands in step 4.
- Check the restored database for posts, pages, widgets, and options containing gambling links.
- Rerun the checks from steps 1 and 4 on the restored site before reopening it.
9. Clean up Google Search Console
- Open Settings > Users and permissions. Record and then remove any unknown users and delegated owners (Google guide: managing owners, users, and permissions).
- An unfamiliar verified owner can only be removed by deleting their verification token: the
google*.htmlfile, thegoogle-site-verificationmeta tag, or the TXT record in DNS. As long as the token remains, that owner can come back. - For gambling URLs that still appear in search results, make sure those URLs already return 404 or 410, then submit them through the Removals tool. This removal is temporary and lasts about six months (Removals tool guide).
- Open the Security issues and Manual actions reports. If anything is found, request a review once the site is clean.
- Use URL Inspection on the home page and key pages, check the crawled HTML, then choose Request indexing.
- Remove unfamiliar sitemaps registered by the attacker and resubmit the legitimate sitemap.
10. Keep monitoring
- Repeat the step 1 checks and the
site:search every day for two weeks, then every week. - Monitor the access logs for requests to the webshell file names already found. Attackers usually try again from a different IP address.
- Store the hashes of the clean site files as a baseline and compare them regularly:
# Baseline sesudah situs bersih
find /var/www/situs -type f -exec sha256sum {} + | sort -k2 > /root/baseline-situs.sha256
# Berkas yang berubah atau hilang dibanding baseline
sha256sum -c --quiet /root/baseline-situs.sha256
# Berkas baru yang belum ada di baseline
comm -13 <(awk '{print $2}' /root/baseline-situs.sha256 | sort) <(find /var/www/situs -type f | sort)
Report
Report it to KUTIM-CSIRT via /lapor under the category Penyisipan Konten Judi/SEO (Judol) (gambling/SEO content injection), or Perusakan Situs (Defacement) if the appearance was replaced, with a severity of High or Critical. Include the list of suspicious files with their modification times, the IP addresses that appear in the logs, and the steps already taken. The form only accepts images and PDFs (up to 5 files, 5 MB each). For logs and evidence files, state in the description that the evidence is available, or send it encrypted to csirt@kutaitimurkab.go.id following the PGP guide.