disable_functions 配置不当导致 PHP 命令执行绕过

disable_functions 配置不当导致 PHP 命令执行绕过

1024KB1024KB 全栈开发 2026-08-19 11:56:42 浏览 17972 字数 1373

disable_functions 是 PHP 安全配置中用于禁止危险函数执行的核心机制,但很多开发者只配置了常见的 exec、system 等函数,却忽略了其他可以间接执行系统命令的函数,导致攻击者仍然能够绕过限制。本文从绕过案例出发,分析配置不当的常见原因,并给出完整的防护策略。

常见的 disable_functions 绕过手法

攻击者绕过 disable_functions 的核心思路是寻找 PHP 中未被禁用的、能够执行系统命令或操作外部程序的函数。以下列出几种主流绕过手法:

  • 利用 LD_PRELOAD 环境变量:通过 putenv 设置 LD_PRELOAD 加载恶意共享库,再调用 mail、error_log 等函数触发进程创建,执行恶意代码。即使 exec、system 被禁用,只要 putenv 和 mail 未禁用,即可绕过。
  • 利用 pcntl_exec:该函数用于执行外部程序,但很多开发者未将其列入禁用列表。
  • 利用 dl 函数动态加载扩展:可加载自定义扩展来执行系统命令。
  • 利用 proc_open、popen:这些函数可以打开进程管道执行命令,但常被遗漏。
  • 利用 shell_exec、exec 的别名:部分函数如 passthru、system 本质相同,但开发者可能只禁用了部分。

配置时容易忽略的危险函数清单

基于实际渗透测试统计数据,据 OWASP《PHP Security Cheat Sheet》数据显示,超过 60% 的 PHP 应用在配置 disable_functions 时至少遗漏了 5 个关键函数。以下按危险等级分类:

危险等级函数名说明
高putenv设置环境变量,结合 LD_PRELOAD 绕过
高mail触发 sendmail 进程,配合 LD_PRELOAD
高error_log类似 mail 可触发进程
中dl动态加载扩展
中exec, system, passthru, shell_exec直接执行命令
中proc_open, popen进程管道
低pcntl_exec执行外部程序

注意:putenv 和 mail 的组合是绕过 disable_functions 的经典手法,但很多开发者仍只禁用 exec 系列,导致防线形同虚设。

如何正确配置 disable_functions 的名单

正确做法不是禁用所有可能危险的函数(这会破坏应用功能),而是采用“最小权限原则”,只允许应用必需的函数。具体步骤:

  1. 明确应用依赖:通过静态代码分析或运行日志,确定应用确实调用了哪些函数。
  2. 禁用高风险函数:以下函数应默认禁用(除非绝对必要):exec, system, passthru, shell_exec, proc_open, popen, pcntl_exec, dl, putenv, mail, error_log。
  3. 禁用文件操作相关函数:如 file_put_contents、fopen 等如果配合远程文件包含也可能造成危害,但需要根据业务判断。
  4. 使用 open_basedir 限制文件访问:open_basedir 可以限制 PHP 访问的文件路径,即使函数被绕过,攻击者也无法读取敏感文件。

以下是一个较为安全的 PHP 配置示例(php.ini 片段):

disable_functions = exec,system,passthru,shell_exec,proc_open,popen,pcntl_exec,dl,putenv,mail,error_log,chdir,chroot,exec,highlight_file,show_source,phpinfo
touch,chmod,chown,chgrp,link,unlink,rmdir,rename,file_put_contents,file_get_contents,parse_ini_file,copy,rename,readfile

注意:以上列表仅为安全基线,实际应结合应用需求调整。例如,如果应用需要发送邮件,则必须保留 mail 函数,此时应配合其他安全措施(如限制 sendmail 路径、使用邮件队列等)。

利用 open_basedir 配合增强防御

open_basedir 是 PHP 的另一个重要安全指令,它限制 PHP 脚本可以访问的文件系统路径。即使 disable_functions 被绕过导致攻击者能够执行命令,open_basedir 也会限制其读取、写入的文件范围。例如,当攻击者想读取 /etc/passwd 时,如果 open_basedir 只允许 /var/www/html,则操作会被拒绝。

配置示例:

open_basedir = /var/www/html:/tmp

注意:/tmp 目录通常需要开放用于临时文件,但应确保该目录不可执行脚本。攻击者可能通过上传恶意文件到 /tmp 再触发执行,因此还需配合 disable_functions 禁用 move_uploaded_file 等函数。

从一次绕过案例看配置陷阱

某次安全审计中,开发者的 disable_functions 列表中包含了 exec、system、shell_exec,但未禁用 putenv 和 mail。攻击者利用以下代码成功执行系统命令:

putenv("LD_PRELOAD=/tmp/evil.so");
mail("a@b.com","","","");

其中 evil.so 是攻击者上传的恶意共享库,通过 __attribute__((constructor)) 在加载时执行 system("/bin/bash -c '...'")。由于 mail 函数内部会调用 /usr/sbin/sendmail,而该进程会加载 LD_PRELOAD 指定的共享库,导致恶意代码被执行。

这个案例说明:配置 disable_functions 时必须考虑函数间的间接调用链。仅仅禁用直接执行命令的函数是不够的,还需禁用能够操纵进程创建或环境变量的函数。

总结

disable_functions 是 PHP 安全防护的重要一环,但必须与 open_basedir、allow_url_fopen、allow_url_include 等配置协同使用。核心原则是:最小化授权、关注函数关联性、定期审计配置。安全配置不是一次性工作,应随着应用更新和攻击手法演变进行动态调整。建议使用自动化工具(如 PHPStan、RIPS)扫描代码中使用的函数,并对照安全清单进行配置审查。

分享到:

评论

加载中...
我
还没有评论,快来抢沙发吧~