1. 什么是文件漏洞
程序开发人员一般会把重复使用的函数写到单个文件中,需要使用某个函数时直接调用此文件,而无需再次编写,这种文件调用的过程一般被称为文件包含。程序开发人员一般希望代码更灵活,所以将被包含的文件设置为变量,用来进行动态调用,但正是由于这种灵活性,从而导致客户端可以调用恶意文件,造成文件包含漏洞。
文件包含的对象,如果是合法的php代码则会执行代码,否则会直接输出内容。
PHP中的四种文件函数:require()、include()、require_once()、include_once()。include出错了顶多报个警,脚本继续跑;require出错了直接崩溃退出 。做CTF的时候,这点细微差别有时候能帮你判断后端的逻辑。
下面介绍一些常见的敏感信息路经,这些文件往往包含很多有用的信息:
Windows系统:
c:\boot.ini //查看系统版本
C:\Windows\System32\drivers\etc\hosts - 主机名解析文件
C:\Windows\win.ini - 系统初始化配置
C:\Windows\system.ini - 系统硬件配置
c:\windows\repair\sam //存储windows系统初次安装的密码
c:\programFiles\mysql\my.ini //MySQL配置文件,内置root密码
c:\windows\php.ini //PHP配置文件
c:\windows\system32\inetsrc\MetaBase.xml //IIS配置文件
Linux/Unix系统
/etc/passwd - 用户账户信息(经典测试文件)
/etc/shadow - 用户密码哈希(需要root权限)
/etc/group - 用户组信息
/etc/hosts - 主机名解析配置
/etc/resolv.conf - DNS配置
/etc/issue - 系统登录提示信息
/etc/os-release - 系统版本信息
/usr/local/app/apache2/conf/httpd.conf //Apache2默认配置文件
/usr/local/app/apache2/conf/extra/httpd-vhost.conf //虚拟网站配置
/usr/local/app/php5/lib/php.ini //PHP配置文件
/etc/httpd/conf/httpd.conf //Apache配置文件
/etc/my.cnf //MySQL配置文件
2. DVWA靶场练习
2.1 Low级别
源码如下:
php
<?php
// The page we wish to display
$file = $_GET[ 'page' ];
//include "config/common/"+$file;
?>
源码中没有任何的过滤,直接接收page参数 。那我们就可以直接怼相对路径。
使用 ?page=../../../../../../etc/passwd,可以多写几个../保证返回到跟路径。

2.2 Medium级别
源码:
php
<?php
// The page we wish to display
$file = $_GET[ 'page' ];
// Input validation
$file = str_replace( array( "http://", "https://" ), "", $file );
$file = str_replace( array( "../", "..\"" ), "", $file );
?>
源码用了 str_replace 把 ../ 和 http:// 替换成了空字符。程序员以为删一次就完了?我们双写!?page=..././..././..././..././..././etc/passwd中间的../被删掉后,两边剩下的字符刚好又拼成了一个新的../,这就叫"双写绕过" 。
2.3 High级别
源码:
php
<?php
// The page we wish to display
$file = $_GET[ 'page' ];
// Input validation
if( !fnmatch( "file*", $file ) && $file != "include.php" ) {
// This isn't the page we want!
echo "ERROR: File not found!";
exit;
}
?>
源码用了 fnmatch("file*", $file),强制要求文件名必须以 file 开头。谁说只能传文件名?我们用协议!file:// 协议也是以 file 开头的,完美符合白名单逻辑。使用 ?page=file:///etc/passwd
file:///etc/passwd -> 意思是"去找本地文件系统里的 /etc/passwd。
为什么是三个斜杠 ///?标准格式其实是:file://<主机名>/<路径>但在本地访问时,主机名通常是空的(默认为 localhost),所以变成了 file://(协议)+ /(根目录),这就连成了 file:///。

2.4 Impossible 级别
源码:
php
<?php
// The page we wish to display
$file = $_GET[ 'page' ];
// Only allow include.php or file{1..3}.php
if( $file != "include.php" && $file != "file1.php" && $file != "file2.php" && $file != "file3.php" ) {
// This isn't the page we want!
echo "ERROR: File not found!";
exit;
}
?>
白名单方式,文件名写死了,其他文件都不可能包含
3. 从读取到GetShell
只会读/etc/passwd还不够,拿到Shell才是我们的目标。
3.1 php://filter
有时候文件包含不执行PHP代码,而是直接输出了,这时候我们想看目标网站的源码怎么办? 直接包含会被服务器解析执行,我们看不见源码。
用 php://filter 伪协议,把文件内容用 Base64 编码后再读取 。
使用 ?page=php://filter/read=convert.base64-encode/resource=file1.php拿到的一串乱码(Base64)解码一下,源码就是你的了 。

3.2 日志毒化 (Log Poisoning)
如果你找不到上传点,也没有RFI,本地文件也没法利用,怎么办?务器总会记录日志吧?比如Apache的 access.log 。
操作:
- 用Burp抓包,在
User-Agent里写入PHP一句话木马:<?php phpinfo(); ?>。 - 利用LFI漏洞包含这个日志文件:
?page=/var/log/apache2/access.log。 - 服务器解析日志时,就会把你刚才注入的PHP代码给执行了 。
3.3 文件上传 + 文件包含
这是最稳的GetShell方式。
操作:
-
做一个"图片马"(表面是jpg,里面藏着php代码) 。
-
上传这个图片,记下路径 。
-
利用文件包含漏洞去包含这个图片路径。
-
蚁剑连接:在蚁剑里填好URL,配合Cookie(如果需要登录),直接连上Webshell
4. 文件包含 vs 文件下载
|------|--------------------|-------------------------|
| 特性 | 文件包含 (Inclusion) | 文件读取/下载 (Read/Download) |
| 核心区别 | 会执行代码 | 只输出内容,不执行 |
| 主要语言 | PHP特产 (include机制) | Java, Python, PHP都有 |
| 危害 | 极高 (RCE, GetShell) | 中高 (敏感信息泄露) |
5. 防御总结
-
白名单:最狠的招,写死只能包含哪几个文件(如DVWA Impossible级别) 。
-
权限控制:设置目录权限,不让你访问敏感目录 。
-
关配置 :把
allow_url_include关掉 。