disable_functions 是 PHP 安全配置中用于禁止危险函数执行的核心机制,但很多开发者只配置了常见的 exec、system 等函数,却忽略了其他可以间接执行系统命令的函数,导致攻击者仍然能够绕过限制。本文从绕过案例出发,分析配置不当的常见原因,并给出完整的防护策略。
攻击者绕过 disable_functions 的核心思路是寻找 PHP 中未被禁用的、能够执行系统命令或操作外部程序的函数。以下列出几种主流绕过手法:
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 系列,导致防线形同虚设。
正确做法不是禁用所有可能危险的函数(这会破坏应用功能),而是采用“最小权限原则”,只允许应用必需的函数。具体步骤:
exec, system, passthru, shell_exec, proc_open, popen, pcntl_exec, dl, putenv, mail, error_log。file_put_contents、fopen 等如果配合远程文件包含也可能造成危害,但需要根据业务判断。以下是一个较为安全的 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 是 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)扫描代码中使用的函数,并对照安全清单进行配置审查。
评论
加载中...