), // 现有 rce-guard 只认官方利用链,拦不住这种形态,故按 PHP 代码特征识别。 $__tpl_bad = '/(\?\>\s*$|<\?php|<\?=|\{\$|\{:|";\s*(eval|assert|system|exec|passthru|shell_exec|file_put_contents|base64_decode|fwrite|copy|curl_exec)\s*\(|eval\s*\(\s*\$_|assert\s*\(\s*\$_)/i'; $__tpl_hit = false; $__tpl_uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; $__tpl_qs = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : ''; foreach (array($__tpl_uri, rawurldecode($__tpl_uri), $__tpl_qs, rawurldecode($__tpl_qs)) as $__tpl_s) { if ($__tpl_s !== '' && @preg_match($__tpl_bad, $__tpl_s)) { $__tpl_hit = true; break; } } if (!$__tpl_hit) { foreach (array($_GET, $_POST, $_COOKIE) as $__tpl_arr) { if (!is_array($__tpl_arr)) { continue; } foreach ($__tpl_arr as $__tpl_k => $__tpl_v) { $__tpl_pool = array((string)$__tpl_k); if (is_string($__tpl_v)) { $__tpl_pool[] = $__tpl_v; $__tpl_pool[] = rawurldecode($__tpl_v); } foreach ($__tpl_pool as $__tpl_s) { if ($__tpl_s !== '' && @preg_match($__tpl_bad, $__tpl_s)) { $__tpl_hit = true; break 3; } } } } } if ($__tpl_hit) { if (!headers_sent()) { http_response_code(403); } header('Content-Type: text/plain; charset=utf-8'); echo 'Blocked by yunucms-tpl-rce-guard'; exit; } // === /yunucms-tpl-rce-guard === // 1) 从原始请求串/URI 直接拦截——避免 ThinkPHP 从 REQUEST_URI 重新解析出被剥离的参数 // 【关键】只拦截「明确的 RCE 特征」,绝不拦截普通业务参数。 // 历史上曾因拦截裸参数 register= / route= / data= / method=get 误杀了大量正常 URL // (包括后台登录 iframe 目标 index.php?s=/admin/index/login 所在文件),导致 // 前台/后台整片 403「无法访问」。本版本仅拦截数组形态 register[ / route[ / method[ / data[ // (真正的 RCE 注入载体),标量形态(?data=1 等)一律放行。 $raw_uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; $raw_qs = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : ''; $raw_path = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : ''; $check_str = strtolower($raw_uri . "\n" . $raw_qs . "\n" . $raw_path); $rce_patterns = array( '_method=__construct', 'invokefunction', 'filter[]=', 'filter%5b', 'filter[0]', 'filter%5b0%5d', 's=index/think', 's=%2findex%2fthink', 's=/index/think', 's=\\think', 's=%5cthink', 'phpmyadmin', '/pma/', // 仅拦截数组形态(真正的 RCE 注入载体);标量形态是正常业务参数,绝不拦截 'register[', 'route[', 'method[', 'data[', ); foreach ($rce_patterns as $pat) { if (strpos($check_str, strtolower($pat)) !== false) { http_response_code(403); header('Content-Type: text/plain; charset=utf-8'); echo 'Blocked by yunucms-rce-guard'; exit; } } // 2) 剥离已进入 PHP 超全局变量的 RCE 键 // - 「绝不可能是正常业务参数」的键:_method / filter / _override / capture —— 无条件剥离 // - register / route / method / data:仅在「数组形态」时剥离(RCE 注入载体); // 其标量形态(如表单字段 method/data)是正常业务字段,绝不剥离,否则会破坏后台表单提交。 $always_strip = array('_method', 'filter', '_override', 'capture'); foreach ($always_strip as $k) { if (isset($_GET[$k])) unset($_GET[$k]); if (isset($_POST[$k])) unset($_POST[$k]); if (isset($_COOKIE[$k])) unset($_COOKIE[$k]); if (isset($_REQUEST[$k])) unset($_REQUEST[$k]); } $array_only_strip = array('register', 'route', 'method', 'data'); foreach ($array_only_strip as $k) { if (isset($_GET[$k]) && is_array($_GET[$k])) unset($_GET[$k]); if (isset($_POST[$k]) && is_array($_POST[$k])) unset($_POST[$k]); if (isset($_REQUEST[$k]) && is_array($_REQUEST[$k])) unset($_REQUEST[$k]); if (isset($_COOKIE[$k]) && is_array($_COOKIE[$k])) unset($_COOKIE[$k]); } // 3) 【已移除泛化 s= 拦截,见下方说明】 // ⚠️ 历史版本在第 3 段用单词级正则 // /think|invokefunction|call_user_func|system|eval|assert/i 匹配 $arr['s'] // 会导致「只要 s= 路由里出现 system 就 unset 掉 s 参数」。 // 而 yunucms 后台有一个真实的同名控制器 System(s=admin/system/basic 等), // 于是所有后台 system/xxx 菜单被误杀 → TP5 拿不到 s → 默认渲染前台 index/index // → 症状就是「后台点菜单直接跳到网站首页」。所有站点无一例外必现。 // ✅ 真实 TP5 RCE 的 s= 载荷形态已被第 1 段 $rce_patterns 全覆盖: // s=index/think、s=%2findex%2fthink、s=/index/think、s=%5cthink、s=\think、invokefunction、 // _method=__construct、filter[]=、filter[0]、phpmyadmin 等。 // 第 3 段属冗余防御,且因 system/eval/assert/system 单词匹配造成大面积误杀, // 故整段删除。此修复不影响 RCE 防护强度。 // === RCE 防护结束 === // === 官方引导段(2.3.5+)=== if (version_compare('5.5', PHP_VERSION, '>')) {die('PHP 5.5 or greater is required!!!');} if (version_compare('7.5', PHP_VERSION, '<')) {die('PHP version should not exceed 7.5!!!');} if (PHP_VERSION_ID >= 70300) { ini_set('session.cookie_samesite', 'Lax'); } define('APP_PATH', __DIR__ . '/app/'); define('APP_AUTO_BUILD', TRUE); define('APP_DEBUG', FALSE); define('CONF_PATH', __DIR__.'/config/'); require './system/start.php'; ?>