Use this after a security hole in software you run, such as a vulnerable PHP version, or when something on your server looks wrong. The checks below look for the most common traces. They cannot prove that a server is clean: someone who got full control can hide their tracks. If you find something, go to the last section.
Run the commands as a user with sudo. Write down what you find, with the time, before you change anything.
Look for unknown or recently changed PHP files#
Attackers often leave a small PHP file (a web shell) in a folder the web server can write to, such as an upload folder or a cache folder.
-
List PHP files in your web folders that changed in the last 14 days:
sudo find /var/www -type f -name '*.php' -ctime -14 -ls-ctimelooks at when the file itself last changed on disk, which is harder to fake than the date shown byls. An update of your software also changes files, so compare with when you last updated. -
Look for PHP files in places where only uploads or cache should be. For example, for a Laravel app such as the Pterodactyl Panel:
sudo find /var/www -type f -name '*.php' \( -path '*/storage/*' -o -path '*/uploads/*' -o -path '*/public/*' \) -ctime -30 -lsIn a Laravel app,
publicnormally holds onlyindex.phpas PHP, and compiled views instorage/framework/viewsare normal. -
Search for code that is often used in web shells:
sudo grep -rlE 'eval\(base64_decode|eval\(gzinflate|eval\(\$_(POST|GET|REQUEST)|assert\(\$_(POST|GET|REQUEST)' /var/wwwThis finds suspicious patterns, not proof. Libraries in
vendorornode_modulescan match for harmless reasons. Look at every file it finds. -
Check temporary folders, where attackers often put programs:
sudo ls -la /tmp /var/tmp /dev/shm
Look for strange processes#
-
List what runs under the web server's user (on Debian and Ubuntu usually
www-data; also checknginxorapacheon other systems):ps -u www-data -o pid,ppid,lstart,cmdNormally you only see
php-fpmworkers (and the web server itself). A shell (sh,bash),perl,python,nc,curl,wgetor a program in/tmpor/dev/shmrunning aswww-datais a strong warning sign. -
Look for programs whose file has been deleted while they still run, a common trick:
sudo ls -l /proc/*/exe 2>/dev/null | grep deletedRight after a package update, normal services can show up here until they are restarted. Anything you do not recognise is worth a closer look.
-
Look at the busiest processes with
toporhtop. A process you do not know that uses a lot of CPU is often a cryptocurrency miner.
Look for new users and sudo rights#
-
Users with ID 0 have root rights. Only
rootshould be listed:awk -F: '$3 == 0 {print $1}' /etc/passwd -
Normal user accounts (ID 1000 and up). Check that you know each one:
awk -F: '$3 >= 1000 {print $1, $6, $7}' /etc/passwd -
Who has sudo rights:
getent group sudo sudo ls -la /etc/sudoers.d/ -
The web server's user should not be able to log in. Its shell should be
/usr/sbin/nologinor/bin/false:getent passwd www-data
Check the SSH keys that may log in#
A new line in an authorized_keys file lets someone log in again later, even after you change passwords.
sudo find / -xdev \( -name authorized_keys -o -name authorized_keys2 \) -ls
Read each file you find and check that you know every key in it:
sudo cat /root/.ssh/authorized_keys
Pay special attention to an .ssh folder in the home folder of www-data, which is /var/www on Debian and Ubuntu. Check also /etc/ssh/sshd_config and /etc/ssh/sshd_config.d/ for an AuthorizedKeysFile line you did not add.
Check cron jobs and timers#
Attackers often add a scheduled task that downloads their program again after you delete it.
sudo cat /etc/crontab
sudo ls -la /etc/cron.d /etc/cron.hourly /etc/cron.daily /etc/cron.weekly /etc/cron.monthly
sudo ls -la /var/spool/cron/crontabs
sudo crontab -l -u root
sudo crontab -l -u www-data
systemctl list-timers --all
Also look for systemd services that were added recently:
sudo find /etc/systemd/system -type f -ctime -30 -ls
And check that /etc/ld.so.preload does not exist. On a normal server it is not there; if it is, it can be used to load a program into every other program:
ls -la /etc/ld.so.preload
Check which ports are open#
-
Programs that listen for connections:
sudo ss -tulpnCheck that you know every program and port. A program that listens under
www-data, or a high port you did not open, is suspicious. On a Pterodactyl node, Wings listens on its own ports (8080 or 8443, and 2022 for SFTP) and Docker on the ports of your game servers; that is normal. -
Connections that are open right now:
sudo ss -tnp
Check who logged in#
-
Recent logins:
last -a | head -n 30On some newer releases
lastis missing or empty; use the next command instead. -
Successful SSH logins from the log:
sudo journalctl -u ssh --since "30 days ago" | grep 'Accepted'On systems that write
/var/log/auth.log, this works too:sudo grep 'Accepted' /var/log/auth.logOn Debian and Ubuntu the service is called
ssh; on some other systems it issshd. Check that every login comes from you, from a place and at a time you recognise. -
The web server's access log can show requests to a file you did not create. For NGINX:
sudo grep -E 'POST .*\.php' /var/log/nginx/access.log | tail -n 50
Check installed files against the packages#
On Debian and Ubuntu, dpkg can compare installed files with what the packages put there:
sudo dpkg --verify
Each line is a file that differs. Configuration files you edited yourself show up too (marked with c). A changed program in /usr/bin or /usr/sbin is a serious warning sign. Someone with root rights can also change the list dpkg compares with, so a clean result is not a guarantee.
If you find something#
Do not just delete the file and carry on. A web shell or a strange process is usually not the only thing an attacker left behind, and it does not tell you what else they took.
- Isolate the server. Take it off the network, or close every port except SSH from your own IP address. If your provider offers snapshots, make one first: it keeps the evidence.
- Assume every secret on the server is known. From another, clean computer, change:
- the passwords of every user on the server, and remove SSH keys you did not add;
- database passwords, and the passwords in the
.envor configuration files of your apps; - API keys and tokens stored on the server (payment providers, mail, DNS, cloud storage);
- for a Pterodactyl Panel: the passwords of administrators, the keys under Application API, and the node key under Nodes, your node, Settings, Reset Daemon Master Key.
- Reinstall from a clean source. Install the operating system again, install your software from its official source, and copy back only your data, not programs or scripts from the old server.
- Restore data from a backup made before the break-in. A backup made after it may contain the attacker's files. Check the restored data before you go online again.
- Close the hole they came through before the server is reachable again: update the software (for PHP, see PHP2Shell: patch CVE-2026-109618 and CVE-2026-109619) and secure SSH.
- Find out whether you must report it. If personal data of other people was on the server, the law may require you to report the breach. Under the GDPR in the EU, that is a report to your data protection authority within 72 hours of finding out, unless the breach is unlikely to put people at risk.