Skip to content

Commit 8c4e501

Browse files
authored
Merge pull request #2087 from HackTricks-wiki/research_update_src_network-services-pentesting_pentesting-web_php-tricks-esp_php-useful-functions-disable_functions-open_basedir-bypass_disable_functions-bypass-dl-function_20260402_132743
Research Update Enhanced src/network-services-pentesting/pen...
2 parents 4fdadfd + c76b2c4 commit 8c4e501

1 file changed

Lines changed: 94 additions & 52 deletions

File tree

src/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable_functions-open_basedir-bypass/disable_functions-bypass-dl-function.md

Lines changed: 94 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,91 +2,133 @@
22

33
{{#include ../../../../banners/hacktricks-training.md}}
44

5-
**Important note:**
5+
`dl()` lets PHP load a shared extension at runtime. If you can make it load an attacker-controlled module, you can register a new PHP function that internally calls `execve`, `system`, or any other native primitive and therefore bypass `disable_functions`.
66

7-
![image](https://user-images.githubusercontent.com/84577967/174675487-a4c4ca06-194f-4725-85af-231a2f35d56c.png)
7+
This is a **real** primitive, but on modern targets it is far less common than older writeups suggest.
88

9-
**`dl`** is a PHP function that can be used to load PHP extensions. It the function isn't disabled it could be abused to **bypass `disable_functions` and execute arbitrary commands**.\
10-
However, it has some strict limitations:
9+
## Why this bypass is uncommon today
1110

12-
- The `dl` function must be **present** in the **environment** and **not disabled**
13-
- The PHP Extension **must be compiled with the same major version** (PHP API version) that the server is using (you can see this information in the output of phpinfo)
14-
- The PHP extension must be **located in the directory** that is **defined** by the **`extension_dir`** directive (you can see it in the output of phpinfo). It's very unprobeable that an attacker trying to abuse the server will have write access over this directory, so this requirement probably will prevent you to abuse this technique).
11+
The main blockers are:
1512

16-
**If you meet these requirements, continue reading the post** [**https://antichat.com/threads/70763/**](https://antichat.com/threads/70763/) **to learn how to bypass disable_functions**. Here is a summary:
13+
- `dl()` must exist and must not be disabled
14+
- `enable_dl` must still allow dynamic loading
15+
- The target SAPI must support `dl()`
16+
- The payload must be a valid PHP extension compiled for the **same target ABI**
17+
- The extension must be reachable from the configured `extension_dir`
1718

18-
The [dl function](http://www.php.net/manual/en/function.dl.php) is used to load PHP extensions dynamically during script execution. PHP extensions, typically written in C/C++, enhance PHP's functionality. The attacker, upon noticing the `dl` function is not disabled, decides to create a custom PHP extension to execute system commands.
19+
The official PHP manual is the most important reality check here: **`dl()` is only available for CLI and embed SAPIs, and for the CGI SAPI when run from the command line**. That means the technique is **usually not available in normal PHP-FPM/mod_php web requests**, so check the SAPI before spending time building a payload.
1920

20-
### Steps Taken by the Attacker:
21+
Also note that `enable_dl` is an `INI_SYSTEM` setting and, as of **PHP 8.3.0**, PHP documents it as **deprecated**, so you usually cannot flip it at runtime from attacker-controlled PHP code.
2122

22-
1. **PHP Version Identification:**
23+
If `dl()` is not viable, go back to the broader list of [module/version dependent bypasses](README.md).
2324

24-
- The attacker determines the PHP version using a script (`<?php echo 'PHP Version is '.PHP_VERSION; ?>`).
25+
## Fast triage from a foothold
2526

26-
2. **PHP Source Acquisition:**
27+
Before building anything, collect the exact parameters that the module must match:
2728

28-
- Downloads the PHP source from the official [PHP website](http://www.php.net/downloads.php) or the [archive](http://museum.php.net) if the version is older.
29+
```php
30+
<?php
31+
phpinfo();
32+
echo "PHP_VERSION=" . PHP_VERSION . PHP_EOL;
33+
echo "PHP_SAPI=" . php_sapi_name() . PHP_EOL;
34+
echo "ZTS=" . (PHP_ZTS ? "yes" : "no") . PHP_EOL;
35+
echo "INT_BITS=" . (PHP_INT_SIZE * 8) . PHP_EOL;
36+
echo "enable_dl=" . ini_get("enable_dl") . PHP_EOL;
37+
echo "extension_dir=" . ini_get("extension_dir") . PHP_EOL;
38+
echo "disabled=" . ini_get("disable_functions") . PHP_EOL;
39+
?>
40+
```
2941

30-
3. **Local PHP Setup:**
42+
What you care about:
3143

32-
- Extracts and installs the specific PHP version on their system.
44+
- `PHP_SAPI`: if this is `fpm-fcgi` or `apache2handler`, `dl()` is usually a dead end for web exploitation
45+
- `extension_dir`: the payload must be loaded from here
46+
- `PHP Version`, architecture, debug/non-debug, and ZTS/non-ZTS: your module must match them
47+
- `disable_functions`: confirm whether `dl` is absent because it is disabled or because the SAPI does not support it
3348

34-
4. **Extension Creation:**
35-
- Studies [creating PHP extensions](http://www.php.net/manual/en/zend.creating.php) and inspects the PHP source code.
36-
- Focuses on duplicating the functionality of the [exec function](http://www.php.net/manual/en/function.exec.php) located at `ext/standard/exec.c`.
49+
## Practical exploitation constraints
3750

38-
### Notes for Compiling the Custom Extension:
51+
### 1. You normally need write access to `extension_dir`
3952

40-
1. **ZEND_MODULE_API_NO:**
53+
This is the biggest bottleneck.
4154

42-
- The `ZEND_MODULE_API_NO` in `bypass.c` must match the current Zend Extension Build, retrievable with:
43-
```bash
44-
php -i | grep "Zend Extension Build" |awk -F"API4" '{print $2}' | awk -F"," '{print $1}'
45-
```
55+
`dl()` takes the **extension filename**, and PHP loads it from `extension_dir`. In practice, this means that a normal arbitrary file upload to `/var/www/html/uploads` is not enough. You still need a path to place a `.so`/`.dll` where PHP will actually load extensions from.
4656

47-
2. **PHP_FUNCTION Modification:**
48-
- For recent PHP versions (5, 7, 8), `PHP_FUNCTION(bypass_exec)` may need adjustment. The provided code snippet details this modification.
57+
Realistic situations where this becomes exploitable:
4958

50-
### Custom Extension Files:
59+
- CTFs or intentionally weak labs where `extension_dir` is writable
60+
- Shared-hosting or container mistakes that expose a writable extension path
61+
- A separate arbitrary file write primitive that already reaches `extension_dir`
62+
- Post-exploitation scenarios where you already escalated enough to drop files there
5163

52-
- **bypass.c**:
53-
- Implements the core functionality of the custom extension.
54-
- **php_bypass.h**:
55-
- Header file, defining extension properties.
56-
- **config.m4**:
57-
- Used by `phpize` to configure the build environment for the custom extension.
64+
### 2. The module must match the target build
5865

59-
### Building the Extension:
66+
Matching only `PHP_VERSION` is not enough. The extension also needs to match:
6067

61-
1. **Compilation Commands:**
68+
- OS and CPU architecture
69+
- libc/toolchain expectations
70+
- `ZEND_MODULE_API_NO`
71+
- debug vs non-debug build
72+
- ZTS vs NTS
6273

63-
- Uses `phpize`, `./configure`, and `make` to compile the extension.
64-
- Resulting `bypass.so` is then located in the modules subdirectory.
74+
If those do not match, `dl()` will fail or crash the process.
6575

66-
2. **Cleanup:**
67-
- Runs `make clean` and `phpize --clean` after compilation.
76+
### 3. `open_basedir` is not the main defense here
6877

69-
### Uploading and Executing on the Victim Host:
78+
Once you can place the module in `extension_dir` and call `dl()`, the extension code executes inside the PHP process. At that point the relevant barrier was not `open_basedir`, but the ability to land a valid shared object in the extension loading path.
7079

71-
1. **Version Compatibility:**
80+
## Building the malicious extension
7281

73-
- Ensures PHP API versions match between the attacker's and victim's systems.
82+
The classic route is still valid:
7483

75-
2. **Extension Loading:**
84+
1. Recreate the victim build as closely as possible
85+
2. Use `phpize`, `./configure`, and `make` to build a shared extension
86+
3. Export a PHP function such as `bypass_exec($cmd)` that wraps native command execution
87+
4. Upload the compiled module into `extension_dir`
88+
5. Load it with `dl()` and call the exported function
7689

77-
- Utilizes the `dl` function, circumventing restrictions by using relative paths or a script to automate the process.
90+
The attack is old, but still relevant because PHP 8.x changelogs continue to include `dl()`-specific crash fixes. The primitive still exists; the hard part is finding a deployment where it is reachable and where you can land a matching module.
7891

79-
3. **Script Execution:**
80-
- The attacker uploads `bypass.so` and a PHP script to the victim's server.
81-
- The script uses `dl_local` function to dynamically load `bypass.so` and then calls `bypass_exec` with a command passed via the `cmd` query parameter.
92+
## Minimal workflow
8293

83-
### Command Execution:
94+
### On the attacker box
8495

85-
- The attacker can now execute commands by accessing: `http://www.example.com/script.php?cmd=<command>`
96+
```bash
97+
mkdir bypass && cd bypass
98+
phpize
99+
./configure
100+
make
101+
```
86102

87-
This detailed walkthrough outlines the process of creating and deploying a PHP extension to execute system commands, exploiting the `dl` function, which should ideally be disabled to prevent such security breaches.
103+
The resulting shared object will usually be under `modules/`.
88104

89-
{{#include ../../../../banners/hacktricks-training.md}}
105+
If you are building on a different environment than the target, treat the produced file as a draft until you verify that the ABI matches the victim.
106+
107+
## Loading and using the extension
108+
109+
If the target really supports `dl()` and the module is inside `extension_dir`, the runtime side is simple:
110+
111+
```php
112+
<?php
113+
if (!extension_loaded('bypass')) {
114+
dl('bypass.so'); // use the correct filename for the target platform
115+
}
116+
echo bypass_exec($_GET['cmd']);
117+
?>
118+
```
90119

120+
On Windows the filename will typically be a `.dll`, while on Unix-like targets it will usually be a `.so`.
91121

122+
## Attacker notes
92123

124+
- Do not assume this works remotely just because `function_exists("dl")` returns true in some documentation or old writeup; validate the live SAPI
125+
- A failed `dl()` attempt may kill the PHP worker if the module is incompatible
126+
- From PHP 8 onward, disabled functions are removed from the function table, so userland enumeration may differ from older posts
127+
- If you cannot write to `extension_dir`, this technique is usually less practical than FPM/FastCGI, `LD_PRELOAD`, or module-specific bypasses already covered in this section
128+
129+
## References
130+
131+
- [PHP manual: dl](https://www.php.net/manual/en/function.dl.php)
132+
- [Tarlogic: A deep dive into disable_functions bypass and PHP exploitation](https://www.tarlogic.com/blog/disable_functions-bypasses-php-exploitation/)
133+
134+
{{#include ../../../../banners/hacktricks-training.md}}

0 commit comments

Comments
 (0)