DEV Community

Cover image for Another day, another VPS breach
ALI MANSOOR
ALI MANSOOR

Posted on

Another day, another VPS breach

I woke up to two emails that immediately caught my attention.

One was from my website monitoring service (I use UptimeRobot, no affiliation) reporting that a client's website was down. The other was from my VPS provider informing me that they had suspended my VPS due to abuse.

I logged into the control panel and immediately noticed a massive CPU spike. The server had gone from its usual 15–20% CPU usage to a sustained 100% for nearly four hours before the provider shut it down under their fair usage policy.

My first clue was xmlrpc.php. It was consuming a significant amount of resources, so I started researching it. I'm not primarily a WordPress/PHP developer, and I was surprised to learn that XML-RPC exposes functionality for remote management of WordPress.

I disabled XML-RPC, brought the VPS back online, and thought the problem was solved.

It wasn't.

The next day I woke up to the exact same two emails.

This time my VPS provider had already imposed CPU limits on the server. I noticed a few kernel-looking processes consuming CPU, assumed they were related to the throttling, and restarted the VPS.

A few hours later, it was offline again.

At that point I knew I was dealing with a compromise rather than a performance issue.

I began investigating the WordPress installation and immediately found obvious signs of infection. There were numerous malicious PHP files (index.php, cache.php, etc.) buried inside recursively nested directories such as:

image/image/image/image/cache.php
Enter fullscreen mode Exit fullscreen mode

The deeper I looked, the worse it became.

The attackers had created:

  • A rogue WordPress administrator account
  • An unauthorized SSH key
  • A root-level user on the VPS
  • An administrator account inside CyberPanel

This wasn't just a compromised website anymore.

It was a full VPS compromise.

My working theory was that the attackers exploited a vulnerable WordPress component (likely allowing arbitrary PHP upload or remote code execution), established persistence, and pivoted into the operating system.

Then I found a suspicious process called libnet-cache.

At first glance it looked like a legitimate kernel process, but it wasn't.

Every time I killed it:

kill -9 <pid>
Enter fullscreen mode Exit fullscreen mode

it immediately reappeared.

Something else was respawning it.

I started tracing persistence mechanisms.

Systemd? Found one malicious service.

Cron? Found multiple malicious cron jobs.

I tried deleting them.

Permission denied.
Enter fullscreen mode Exit fullscreen mode

That made no sense.

I was root.

After some research I discovered the files had the immutable (i) attribute set.

Normally you remove it with:

chattr -i <file>
Enter fullscreen mode Exit fullscreen mode

Except...

that also returned permission denied.

Even the chattr binary itself had been tampered with. Fortunately its permissions were recoverable, allowing me to restore it and finally remove the immutable attribute.

From there the cleanup became much more methodical:

  • Removed immutable attributes
  • Deleted malicious services
  • Removed malicious cron jobs
  • Deleted unauthorized users
  • Removed rogue SSH keys
  • Reinstalled WordPress core using WP-CLI
  • Deleted every malicious PHP file from wp-content/uploads
  • Rotated credentials
  • Reviewed the rest of the server for persistence

The root cause appeared to be a compromised WordPress site that allowed the attackers to gain remote code execution, escalate their foothold, establish persistence, and eventually deploy a cryptocurrency miner.

My VPS had become someone else's mining rig.

One compromised website was all it took to lose an entire VPS.

Here is a list of commands which I used (thanks to ChatGPT)

Here are some of the most useful commands from the investigation, grouped by purpose.

Initial Investigation

top
htop

ps aux --sort=-%cpu | head -20
ps -ef

pstree -p

lsof -p <PID>
ss -tulpn
netstat -tulpn
Enter fullscreen mode Exit fullscreen mode

Finding Suspicious PHP Files

find /home -type f \( -name "*.php" -o -name "*.phtml" \)

find /home/*/public_html/wp-content/uploads \
  -type f \( -name "*.php" -o -name "*.phtml" \)

find /home -type d | grep "image/image"

find /home -type f -mtime -7
Enter fullscreen mode Exit fullscreen mode

WordPress Investigation

wp core verify-checksums

wp core download --force

wp plugin list

wp user list

wp option list

wp cron event list
Enter fullscreen mode Exit fullscreen mode

Finding Persistence

systemctl list-units --type=service

systemctl list-unit-files

systemctl status <service>

crontab -l

cat /etc/crontab

ls -la /etc/cron.*

find /var/spool/cron -type f -exec cat {} \;
Enter fullscreen mode Exit fullscreen mode

Finding Unauthorized Users

cat /etc/passwd

last

lastlog

getent passwd
Enter fullscreen mode Exit fullscreen mode

SSH Investigation

cat ~/.ssh/authorized_keys

find /home -name authorized_keys

grep "ssh" /var/log/secure
Enter fullscreen mode Exit fullscreen mode

Process Investigation

ps auxf

readlink -f /proc/<PID>/exe

ls -l /proc/<PID>/exe

strings /proc/<PID>/exe

lsof -p <PID>
Enter fullscreen mode Exit fullscreen mode

Finding Immutable Files

lsattr <file>

lsattr -R /etc

chattr -i <file>
Enter fullscreen mode Exit fullscreen mode

Finding Recently Modified Files

find / -mtime -2

find / -ctime -2

find / -newermt "2026-07-22"
Enter fullscreen mode Exit fullscreen mode

One command that often surprises people during an incident is:

readlink -f /proc/<PID>/exe
Enter fullscreen mode Exit fullscreen mode

It tells you the actual executable backing a running process. Malware frequently disguises itself with names like kworker, kthreadd, libnet-cache, or systemd, but this command reveals the real binary on disk, making it invaluable during Linux incident response.

Top comments (0)