☰
【八个月网安课程】第十二周·周四:文件操作漏洞审计——unlink、fopen、fwrite 等
2026/10/26 1:53:57 网站建设 项目流程

今天将聚焦于 PHP 中另一类高危操作——文件相关函数。当file_get_contents、unlink等函数的参数被攻击者控制时,轻则泄露任意文件内容,重则删除关键系统文件。学会如何从代码层面审计这些漏洞,并掌握防御方法。


第十二周·周四:文件操作漏洞审计——unlink、fopen、fwrite 等

🎯 今日学习目标(达成效果)

  • 能识别PHP 代码中常见的危险文件操作函数:file_get_contents、fopen、file_put_contents、fwrite、unlink、copy、rename、include(复习);
  • 能审计文件操作函数中的路径参数是否来源于用户输入,并判断是否经过充分的白名单或路径限制;
  • 能独立发现任意文件读取、任意文件写入、任意文件删除漏洞,并构造 payload 验证漏洞(如读取../../etc/passwd);
  • 能利用文件写入漏洞写入一句话木马,并理解其危害不亚于文件上传漏洞;
  • 能提出防御方案:路径白名单、禁用../、固定扩展名、使用安全 API(如basename提取纯文件名)、最小权限原则。

📘 一、文件操作漏洞的本质

当 Web 应用使用用户输入来构造文件路径,并进行读、写、删、复制等操作时,如果没有对路径进行严格限制,攻击者就可以通过路径穿越(../)跳出预定目录,访问或修改服务器上的任意文件。

三类典型漏洞:

  1. 任意文件读取:读取配置文件、密码文件、源码等。
  2. 任意文件写入:写入木马、修改系统配置、篡改日志等。
  3. 任意文件删除:破坏应用数据、删除系统关键文件(可能配合条件竞争)。

共同根源:路径参数可控 + 缺乏严格的路径校验。


📘 二、危险函数详解与审计要点

1.file_get_contents—— 读取文件内容

$content=file_get_contents($path);
  • 审计关键:$path是否包含外部变量?是否允许../或绝对路径?
  • 攻击示例:?file=../../../etc/passwd或?file=php://filter/...(结合伪协议)。
  • 搜索正则:file_get_contents\s*\(\s*\$(?!this|_)查找可能由变量控制的调用。

2.fopen/fwrite—— 打开文件并写入

$fp=fopen($filename,'w');fwrite($fp,$data);fclose($fp);
  • 审计关键:$filename是否可控?$data是否可控(如果都能控,可写 shell)。
  • 注意:fopen模式为'w'或'a'时可能创建新文件。

3.file_put_contents—— 直接写入文件

file_put_contents($filename,$data);
  • 这是写入漏洞最常见的形式,审计方式与fopen类似。

4.unlink—— 删除文件

unlink($file);
  • 审计关键:$file是否可控?路径是否允许穿越?
  • 攻击演示:?file=../../config.php可能导致配置文件被删除。

5.copy/rename—— 复制与重命名

copy($source,$dest);rename($old,$new);
  • 如果源和目的均可控,攻击者可复制敏感文件到可访问目录,或移动文件实现权限绕过。

6.include/require—— 文件包含(复习)

  • 同样属于文件操作,审计时重点关注变量是否进入包含路径,可导致 LFI/RFI。

审计原则:任何用于文件路径的外部参数,如果没有被严格限定在固定目录内或使用白名单,就应标记为潜在漏洞。


✍️ 三、动手实践:审计一个存在文件读取漏洞的功能

实践 1:搭建任意文件读取靶场

创建file_read.php:

<?php$template=$_GET['template']??'default.html';$path='templates/'.$template;if(file_exists($path)){echofile_get_contents($path);}else{echo'Template not found.';}?>

审计分析:

  • 输入源:$_GET['template']完全可控。
  • 路径构造:使用字符串拼接,未过滤../。
  • 危险函数:file_get_contents,且file_exists仅检查存在性,不防穿越。
  • 利用:?template=../../etc/passwd可直接读取系统文件。

防御方案:

  • 移除路径中的../和..\(但需注意编码绕过)。
  • 使用basename()仅提取文件名,忽略目录部分:
    $template=basename($_GET['template']);
  • 结合白名单,仅允许特定文件名。

实践 2:搭建任意文件写入靶场

创建save_config.php:

<?php$filename=$_POST['config_name']??'app.conf';$content=$_POST['config_data']??'';$path='configs/'.$filename;file_put_contents($path,$content);echo'Config saved.';?>

漏洞:攻击者可上传config_name=../../var/www/html/shell.php,内容为<?php @eval($_POST['cmd']);?>,写入 Webshell。

防御:

  • 强制扩展名:$filename = uniqid() . '.conf';完全忽略用户提供的文件名。
  • 路径仅允许在指定目录内,并且去除../。

实践 3:审计一个简单的文件删除功能

创建delete_file.php:

<?php$file=$_GET['file'];$path='uploads/'.$file;if(file_exists($path)){unlink($path);echo'Deleted.';}?>

利用:?file=../../config.php删除配置文件。如果删除的是.htaccess,还可能引发后续攻击。

防御:

  • 检查文件名是否包含..。
  • 使用realpath并验证解析后的路径是否仍位于允许目录内。
  • 删除前记录日志,并限制可删除的文件类型。

✍️ 四、动手实践四:在 DVWA 或自建项目中审计文件操作

1. DVWA File Inclusion 源码审计

  • 打开dvwa/vulnerabilities/fi/source/low.php:
    $file=$_GET['page'];include($file);
  • 审计:直接包含用户输入,典型的文件包含漏洞,也可用于读取文件(通过php://filter)。

2. 审计一个简单 CMS 的文件操作点

使用 Seay 搜索unlink、file_get_contents、file_put_contents等关键字,查看是否有参数直接来自$_GET、$_POST且未过滤。例如 BlueCMS 中的/admin/tpl_manage.php可能存在删除模板文件的功能,若$tpl_name来自 GET 且未过滤,则可删除任意文件。

记录:在审计报告中写明路径、参数、危险函数及利用思路。


📝 五、课后测试题与解析

测试题 1:以下代码是否存在漏洞?如果存在,是什么类型?如何利用?

$file=$_GET['file'];$content=file_get_contents('data/'.$file);echo$content;

参考答案:
存在任意文件读取漏洞。$file参数直接来自用户,攻击者可通过../穿越目录,如?file=../../etc/passwd,读取系统文件。另外可能利用 PHP 伪协议(如php://filter)读取 PHP 文件源码。修复:使用basename()过滤,或白名单限制文件名。

测试题 2:在审计文件删除功能时,即使使用realpath验证路径,还需要注意什么?

参考答案:

  • 竞争条件:在realpath检查与unlink执行之间存在时间窗口,攻击者可能通过并发请求替换文件链接,删除意料之外的文件。需要配合文件锁或原子操作。
  • 权限检查:realpath只解析路径,不检查文件所有权或权限,仍需确保 Web 进程不删除系统关键文件,应限制可删除的目录范围。
  • 逻辑绕过:即使路径合法,如果删除操作可被滥用删除用户资产(如删除其他用户的头像),也属于越权漏洞,需要验证当前用户对文件的所有权。

✅ 今日学习效果自检清单

  • 我能列出至少 5 个 PHP 文件操作函数,并说出它们在什么条件下会产生漏洞
  • 我独立编写了包含任意文件读取、写入、删除漏洞的代码,并成功利用
  • 我使用 Seay 在靶场代码中搜索了文件操作函数,并人工确认了潜在漏洞
  • 我掌握了路径穿越的防御手段(basename、realpath、白名单、禁用../)
  • 我理解了文件写入漏洞与文件上传漏洞的相似性,以及各自的防御重点

⚠️ 阶段避坑重点

  • 不要只过滤../:攻击者可以使用....//、..\/、URL 编码%2e%2e%2f等绕过简单替换。必须使用basename()或realpath()等可靠方法。
  • file_exists不是安全函数:它只检查文件是否存在,不防御路径穿越,审计时不能因为前面有file_exists就认为安全。
  • 写入权限控制:即使路径被限制,如果 Web 目录可写,写入的木马依然可执行。文件存储目录应在 Web 根目录之外,或禁止脚本执行。
  • 注意文件包含与文件读取的区别:包含函数(include)会执行代码,读取函数(file_get_contents)仅获取内容,但防御思路相似。
  • 审计时要关注框架封装:现代框架通常使用Storage或Filesystem抽象,直接搜索函数可能遗漏,需了解框架的文件操作 API 并进行追踪。

今天你完成了文件操作漏洞的审计训练,这是代码审计中与 SQL 注入同样常见的漏洞类型。明天我们将进入更危险的命令/代码执行点审计,追踪eval、system等函数的参数,理解它们如何在代码中留下后门。请保留今天的审计环境,命令执行审计常用相似的方法论。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询