Disable Functions Bypass - dl Function
Tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
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.
这是一个 真实 的原语,但在现代目标上,它远不像早期写作所暗示的那么常见。
Why this bypass is uncommon today
主要阻碍因素有:
dl()必须存在且不能被禁用enable_dl必须仍然允许动态加载- 目标 SAPI 必须支持
dl() - payload 必须是为 相同目标 ABI 编译的有效 PHP 扩展
- 扩展必须可以从配置的
extension_dir访问到
官方 PHP 手册是这里最重要的现实检查:dl() 仅对 CLI 和 embed SAPIs 可用,并且仅在从命令行运行时对 CGI SAPI 可用。这意味着该技术通常在普通 PHP-FPM/mod_php web 请求中不可用,所以在花时间构建 payload 之前请先检查 SAPI。
另请注意,enable_dl 是一个 INI_SYSTEM 设置,并且从 PHP 8.3.0 起,PHP 将其记录为 已弃用,因此你通常无法在运行时通过攻击者控制的 PHP 代码将其打开。
如果 dl() 不可行,回到更广泛的列表 module/version dependent bypasses。
Fast triage from a foothold
在构建任何东西之前,收集模块必须匹配的确切参数:
<?php
phpinfo();
echo "PHP_VERSION=" . PHP_VERSION . PHP_EOL;
echo "PHP_SAPI=" . php_sapi_name() . PHP_EOL;
echo "ZTS=" . (PHP_ZTS ? "yes" : "no") . PHP_EOL;
echo "INT_BITS=" . (PHP_INT_SIZE * 8) . PHP_EOL;
echo "enable_dl=" . ini_get("enable_dl") . PHP_EOL;
echo "extension_dir=" . ini_get("extension_dir") . PHP_EOL;
echo "disabled=" . ini_get("disable_functions") . PHP_EOL;
?>
What you care about:
PHP_SAPI:如果这是fpm-fcgi或apache2handler,dl()通常在 web 利用场景中是死胡同extension_dir:payload 必须从这里加载PHP Version、CPU 架构、debug/non-debug,以及 ZTS/non-ZTS:你的模块必须与之匹配disable_functions:确认dl的缺失是因为被禁用,还是因为 SAPI 不支持它
实际利用限制
1. 通常你需要对 extension_dir 有写权限
这是最大的瓶颈。
dl() 接收 extension filename,PHP 从 extension_dir 加载它。实际上,这意味着普通的任意文件上传到 /var/www/html/uploads 不足够。你仍然需要一个可将 .so/.dll 放到 PHP 实际从中加载扩展的路径。
实际可利用的场景示例:
- CTFs 或有意设置弱点的实验环境,
extension_dir可写 - 共享托管或容器配置错误,暴露了可写的 extension 路径
- 一个能写任意文件的独立原语,已经能写入
extension_dir - 在后利用场景中,已经提权到可以把文件放到那里
2. 模块必须与目标构建匹配
仅匹配 PHP_VERSION 不够。扩展还需要匹配:
- 操作系统和 CPU 架构
- libc/工具链的期望
ZEND_MODULE_API_NO- debug 与 non-debug 构建
- ZTS vs NTS
如果这些不匹配,dl() 会失败或导致进程崩溃。
3. open_basedir 并不是这里的主要防线
一旦你能把模块放到 extension_dir 并调用 dl(),扩展代码就会在 PHP 进程内执行。那时相关的障碍并不是 open_basedir,而是能否把一个有效的共享对象放到扩展加载路径中。
构建恶意扩展
经典流程仍然有效:
- 尽可能复现受害者的构建环境
- 使用
phpize、./configure和make构建共享扩展 - 导出一个 PHP 函数,例如
bypass_exec($cmd),用来封装原生命令执行 - 将编译好的模块上传到
extension_dir - 用
dl()加载它并调用导出的函数
这个攻击手法虽旧但仍相关,因为 PHP 8.x 的变更日志仍持续包含针对 dl() 的崩溃修复。该原语仍然存在;难点在于找到一个可达的部署环境,以及能投放匹配模块的位置。
最小化工作流程
在攻击者主机上
mkdir bypass && cd bypass
phpize
./configure
make
生成的共享对象通常位于 modules/ 下。
如果你在与目标不同的环境上构建,请将生成的文件视为草稿,直到你确认 ABI 与 victim 匹配。
加载并使用扩展
如果目标确实支持 dl() 且模块位于 extension_dir 中,运行时部分很简单:
<?php
if (!extension_loaded('bypass')) {
dl('bypass.so'); // use the correct filename for the target platform
}
echo bypass_exec($_GET['cmd']);
?>
在 Windows 上,文件名通常为 .dll,而在类 Unix 的目标上通常为 .so。
攻击者注意事项
- 不要仅因为某些文档或旧文章中
function_exists("dl")返回 true 就假设这在远程环境中可用;务必验证实时 SAPI - 如果模块不兼容,失败的
dl()调用可能会导致 PHP worker 崩溃 - 从 PHP 8 开始,被禁用的函数会从函数表中删除,因此用户态的枚举可能与较早的帖子不同
- 如果你无法写入
extension_dir,相比于本节已介绍的 FPM/FastCGI、LD_PRELOAD或模块特定的绕过方法,该技术通常不太实用
参考资料
Tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。


