最有效修复方式是直接禁用setup.php反序列化入口:生产环境应删除或重命名该文件,或在文件开头强制exit拦截;同时移除__wakeup中eval调用,升级PHP并启用unserialize($data, ['allowed_classes' => false]),禁用allow_url_fopen及危险流封装器。
直接禁用 setup.php 的反序列化入口
phpmyadmin 2.10–4.8.x 多个版本中,/scripts/setup.php 对 $_post['configuration'] 的反序列化是漏洞根源。只要 $action 不等于 clear,就会无条件调用 unserialize() —— 这个逻辑本身就不该存在。修复最有效的方式不是加固过滤,而是移除该功能入口:
- 生产环境应直接删除或重命名
/scripts/setup.php(官方早在 4.9.0+ 版本中已废弃此脚本) - 若必须保留(如旧版定制部署),在文件开头强制 exit:
if (isset($_POST['configuration']) && !empty($_POST['configuration'])) { http_response_code(403); die('Configuration import disabled.');} - Apache/Nginx 层可加规则拦截:
location ~ ^/scripts/setup\.php$ { return 403; }
替换 __wakeup 中的 eval 调用
漏洞链关键一环是 PMA_Config::__wakeup() 会触发 load(),而后者用 eval() 加载外部配置。即使你控制了 $source,只要去掉 eval 就断掉 RCE 链:
- 定位到
libraries/classes/Config.php(老版本为libraries/config.class.php)中的load()方法 - 删掉所有
eval('?>'. trim(...))相关分支,改用安全方式加载:只允许读取本地config.inc.php,且必须是绝对路径、白名单校验、禁止协议(如ftp://、php://) - 若需动态配置,改用
json_decode(file_get_contents(...), true)+ 严格 schema 校验,彻底规避代码执行风险
升级并验证 unserialize 使用点
除了 setup.php,整个 phpMyAdmin 代码库中还有其他 unserialize() 调用(如 session 反序列化、导入导出模块)。不能只修一个点:
- 搜索全部
unserialize(出现位置:grep -r "unserialize(" --include="*.php" . - 对每个调用点检查:参数是否完全可控?是否在反序列化前做了类型/类名白名单限制?是否启用了
allowed_classes参数(PHP 7.0+)? - 例如,
import.php中处理压缩包元数据时曾有类似问题,需确保传入unserialize()的字符串来自可信 ZIP 内部字段,而非用户直传 - PHP 7.4+ 建议统一使用
unserialize($data, ['allowed_classes' => false]),彻底禁用对象反序列化
别忽略 PHP 配置层的兜底防护
即使代码层修复了,底层 PHP 配置不当仍可能让攻击绕过:
- 确认
allow_url_fopen = Off—— 否则攻击者仍可通过http://协议从外网加载恶意配置 - 禁用危险流封装器:
stream_wrapper_unregister('ftp'); stream_wrapper_unregister('phar');(放在auto_prepend_file中) - Web 服务器限制 POST 数据大小和结构:
php_admin_value max_input_vars 1000、php_admin_value suhosin.post.max_name_length 64(suhosin 已弃用,但类似思路可用) - 最关键的是:永远不要信任任何来自
$_GET、$_POST、$_COOKIE的序列化字符串 —— 它们本质上就是未签名的二进制 payload
setup.php 后仍被攻破,就是因为某个冷门导入功能悄悄复用了同一套不安全的反序列化逻辑。



















