这是一道典型的 PHP 代码审计与绕过题(通常出现在 CTF 比赛中)。核心目标是利用 highlight_file() 函数来读取服务器上的敏感文件(比如 flag.php 或 /flag),但代码中做了一些非常严格的限制。

- 代码核心逻辑分析
首先,我们把图片中的关键代码拆解一下:
php
function filter($file){
if(preg_match('/\. \. \/|http|https|data|input|rot13|base64|string/i', $file)){
die("hacker!");
}else{
return $file;
}
}
$file = $_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
码的执行流程和限制如下:
输入源:通过 GET 请求接收参数 ?file=...。
第一道关卡 (is_file):必须满足 !is_file($file)。也就是说,你传入的字符串绝对不能被 PHP 直接识别为一个"客观存在的本地普通文件"。如果满足,则直接拦截并输出 "hacker!"。
第二道关卡 (filter):不能包含以下敏感关键字(忽略大小写 /i):
.../(即 .../ 目录遍历,注意图片中正则匹配了带空格或转义的路径,这里阻止了常规的相对路径跳转)
协议伪协议:http, https, data, input
常用过滤器:rot13, base64, string
- 核心矛盾点(破局关键)
矛盾 1:我们需要读取一个文件(比如 flag.php),但 is_file('flag.php') 会返回 true,导致直接被 else { echo "hacker!"; } 拦截。
矛盾 2:为了绕过 is_file(),我们通常会使用 PHP 伪协议(Wrapper)。但是最常用的 php://filter/read=convert.base64-encode/resource=flag.php 中包含的 string、base64 等关键字全部被 filter() 函数拉黑了。
总结:我们需要找到一个既能让 is_file() 返回 false,又能让 highlight_file() 正常解析并读取到目标文件的路径或伪协议,且不包含黑名单关键字。
- 解题思路与绕过技巧
思路一:利用其他未被过滤的 PHP 过滤器
虽然 base64、rot13、string 被过滤了,但 PHP 支持的过滤器远不止这些。
我们可以利用 php://filter 配合其他的编码转换过滤器。例如:
Iconv 编码转换:convert.iconv.*
解压/压缩过滤器(如果适用):zlib.deflate / zlib.inflate
构造 Payload 示例:
php
?file=php://filter/read=convert.iconv.UCS-2.UCS-4/resource=flag.php

为什么能绕过 is_file? 因为 php://filter/... 是一个伪协议流,is_file() 无法将其直接识别为本地物理文件,返回 false,顺利通过第一关。
为什么能绕过 filter()?
因为里面没有包含 base64、string 等黑名单词汇。
还有其他payload可以读取flag
- php://filter/resource=flag.php
为什么能绕过正则:它的字符串里只有 php、filter、resource、flag.php。对照题目黑名单(http|https|data|input|rot13|base64|string),没有任何一个词被命中。
读取原理:php://filter 是 PHP 专门用来处理流的通道。当你没有指定任何 read(过滤器)时,它默认就是直接读取原文件。
结果:直接在页面上高亮显示 flag.php 的源码。
- php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php
为什么能绕过正则:使用了 convert.iconv(编码转换),避开了被过滤的 convert.base64 和 convert.string。
读取原理:它在读取 flag.php 时,强行把文件的编码从 UCS-2LE 转换成 UCS-2BE(一种字节序转换)。
结果:由于字节序被颠倒了,页面上会输出一堆表面上看起来是乱码、但实际上包含了 flag 信息的文本。你在浏览器查看网页源码,把它逆向转换回来就能看到明文。
- php://filter/read=convert.quoted-printable-encode/resource=flag.php
为什么能绕过正则:虽然包含了 read=,但后面使用的是 convert.quoted-printable-encode(可打印字符引用编码,常用于邮件传输),这个过滤器同样不在黑名单里。
读取原理:它在读取文件时,会将文件内容进行 Quoted-printable 编码。
结果:高亮输出编码后的文本(通常表现为很多 =3D、=0D 之类的字符),代码中的特殊字符会被保护起来,同样能借此拿到数据。
- compress.zlib://flag.php
为什么能绕过正则:compress.zlib:// 是 PHP 的压缩流包装器,完全不在黑名单的任何一个字眼里。
读取原理:这个伪协议本意是用来打开并解压 .gz 压缩文件的。但是如果后面跟的是一个普通的未压缩文本文件(如 flag.php),zlib 流在读取时发现它不是压缩包,会直接降级作为普通文件流读取输出。
结果:直接原汁原味地高亮输出 flag.php 的源码