In short: If PHP-FPM, Apache or another web server process starts a shell such as sh or bash, that is one of the strongest warning signs of a break-in through the website. Check the process tree, the command line and the working directory of the shell before you terminate anything. Individual short shells can be legitimate; long-running shells with network tools almost never are.
Why this pattern says so much
A web server answers requests. PHP-FPM, the process manager that runs PHP scripts for the web server, normally starts no further programs for this. A shell under the web server means that PHP code has deliberately called a program. Either the application does this on purpose, or someone else has injected code.
For PHP2Shell there are no published, confirmed indicators of compromise, that is, no list of fixed detection markers. Detection relies on behavior. According to the reports available to us, web server or PHP-FPM processes that start a shell are among the strongest general warning signs. That applies to every vulnerability through which attackers can execute code on the server. An overview of further signs is given in Am I already compromised?.
Why is the shell so important for an attacker? With a vulnerability, he can at first only issue individual commands through the PHP process. A shell turns that into a comfortable workplace: he can look around, download tools, read credentials from configuration files and set himself up permanently. Whoever spots the shell therefore often spots the attacker at the moment he moves from the first access to the actual plan.
PHP-FPM starts bash: reading the process tree
Every process on Linux has a parent process. The question is whether a shell runs as a child of a web server process. The following commands only read:
# Process tree with user, parent PID and start time
ps -eo user,pid,ppid,lstart,cmd --forest | grep -B2 -A4 -E "[p]hp-fpm|[a]pache2|[h]ttpd|[l]sphp"
# Shells running under typical web server users
ps -eo user,pid,ppid,lstart,cmd | grep -E "^(www-data|apache|nginx) " | grep -E "[ /](ba|da)?sh( |$)"
# Tree starting from the oldest PHP-FPM process
pstree -alp "$(pgrep -o php-fpm)"
If you have found a suspicious shell, the entries under /proc give further hints, likewise read-only:
PID=12345 # PID of the suspicious shell
tr '\0' ' ' < /proc/$PID/cmdline; echo # full command line
ls -l /proc/$PID/cwd /proc/$PID/exe # working directory and program file
ls -l /proc/$PID/fd 2>/dev/null | grep socket # open network connections
Process names on Debian and Ubuntu are often php8.3-fpm or similar; adjust the search patterns. Working directories in /tmp, /dev/shm or in upload folders are a clear sign.
Legitimate exceptions and real warning signs
Some applications call programs via PHP. A shell can briefly arise in the process, for example when PHP hands over mail via sendmail_path or an application processes images with an external tool. Such shells live only seconds and always call the same program. Document these expected cases cleanly once; then every deviation stands out immediately instead of drowning in a flood of known messages.
| Observation | Rather harmless if | Suspicious if |
|---|---|---|
| Short shell under PHP-FPM | it starts a known program of the application | target and purpose are unclear |
| Longer-running shell | never | it runs for minutes or hours |
| Network tools as a child process | known update or API query of the application | downloads into temporary directories follow |
| Working directory | the application's project folder | /tmp, /dev/shm, upload folder |
| Time | fits a known action, such as sending mail | at night, without user activity, clustered |
If you do not know whether your application calls programs, ask the developer or search the code for functions such as exec, shell_exec, system, passthru, proc_open and popen. Hits in your own code are normal, hits in unknown files are not.
Monitor permanently instead of looking once
A snapshot with ps catches only shells that are running right now. Short-lived processes slip through. It is more reliable to log program starts by the web server user permanently. On Linux, the audit service auditd does this with a rule for program starts by the web server user. Evaluation is read-only:
# Show logged program starts under a rule key (key as an example)
sudo ausearch -k web_exec --start today -i | grep -E "exe=|proctitle="
What the rule looks like depends on the distribution and user ID; test it first, because on heavily loaded servers many entries arise.
Two additions make monitoring dependable. First, the logs should additionally go to a central system, because an attacker with root rights can delete local logs. Second, alerting is needed: a program start by the web server user that is not on a short list of expected programs should trigger a notification, not just a log entry. Anyone who already uses an endpoint or server detection solution can map the same rule there. As a preventive measure, disable_functions can block what the application does not need. That does not close a vulnerability in the interpreter, but it makes an attacker's next step harder. Details are at Using disable_functions correctly.
What to do if you find a shell under the web server
- Document: Save the output of
ps, command line, working directory and open connections in a file outside the web directory. - Do not clean up: Delete no files and terminate no processes before the traces are secured.
- Isolate: Take the server off the network or restrict access heavily.
- Find the cause: Check logs, new files and persistence such as cron jobs, see Finding suspicious processes on Linux servers.
- Change credentials: All passwords and keys that were on the server are considered known.
If the application runs in containers, the same principle applies on a small scale. A shell in the PHP container is just as suspicious as on a classic server, and container images often contain one only because the base image brings it along. Before restarting the container, secure its state, because the traces in the volatile file system disappear with the container.
Also check the neighborhood. An attacker with a shell on a web server often tries to reach other systems from there, such as the database, backup storage or internal administration interfaces. Credentials in the application's configuration files make that easy for him.
A long-running shell under PHP-FPM on a server with customer data is a security incident. Treat it as one: secure, isolate, work through it in a structured way, have notification duties checked.
How does PHP SOS help?
- Classify the finding: We check process tree, command lines and connections and separate application behavior from break-in traces.
- Secure traces: We secure the system state in a traceable way before anything is changed.
- Handle the incident: With incident response, we lead from containment to a documented close.
- Secure for the long term: We set up logging of program starts and limit PHP functions to what is necessary. How PHP-FPM works internally is explained in PHP-FPM explained.
Frequently asked questions
Is every shell under PHP-FPM an attack?
Not every one. Some applications deliberately call programs via PHP, for example for image processing or sending mail, and a shell can briefly arise in the process. It becomes suspicious if the shell runs for a longer time, starts network tools or works from atypical directories.
Should I terminate the suspicious process immediately?
Document first, then act. Record process data, command line, working directory and network connections. Then isolate the server. Terminating hastily destroys traces and does not prevent the attacker from coming back.
Are there confirmed detection markers for PHP2Shell?
No. There are no published, confirmed indicators of compromise. Shells under the web server are a general warning sign of break-ins through web applications, not specific to PHP2Shell.
How do I prevent PHP from starting shells in the first place?
With disable_functions, functions such as exec, shell_exec, system and proc_open can be blocked if the application does not need them. That does not close a vulnerability in the PHP interpreter, but it makes exploitation harder.