1 |
|
大量过滤,无法使用空格、分号、单引号和括号。
所以利用文件包含漏洞, payload 如下:
1 | ?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php |
include
:文件包含标识符。
在 PHP 中常用的文件包含语句有以下四种:
include()
找不到被包含的文件时只会产生警告,脚本将继续运行。
include_once()
与
include()
类似,唯一区别是如果该文件中的代码已经被包含,则不会再次包含。require()
找不到被包含的文件时会产生致命错误,并停止脚本运行。
require_once()
与
require()
类似,唯一区别是如果该文件中的代码已经被包含,则不会再次包含。
当以上 四种函数 参数可控的情况下,我们需要知道以下两点特性,
- 若文件内容符合 PHP 语法规范(例如有
<?php
),包含时不管扩展名是什么都会被 PHP 解析。 - 若文件内容不符合 PHP 语法规范则可能暴漏其源码。
为什么是
include$_GET[1]
?include()
不是函数,可以用括号也可以加空格:1
2include($_GET[1])
include $_GET[1]另外, PHP 解释器会把
include$_GET[1]
中的$_GET[1]
解析为变量而不会因为无空格就报错。?>
是干什么的?因为分号被过滤了,语句不完整会报错无法执行,所以要找分号的代替品。
?>
前的代码会被解析为 PHP 的最后一句代码,不用加分号也不会报错。此外,eval()
函数不是把 PHP 语句“插入”到代码中再执行,所以这里不会出现?>
把后面的代码全部截断的问题。为什么不直接写文件路径
./flag.php
而是写一长串php://
开头的东西?文件包含时:
- 若文件内容包含有效的PHP标签(如
<?php
开头),PHP会解析并执行标签内的代码,标签外的文本会直接输出。- PHP 解析器从
<?php
开始解析代码,直到遇到?>
或文件末尾。 - 如果文件末尾没有
?>
,解析器会直接执行到文件结束,不会报错,且所有代码均有效。
- PHP 解析器从
- 若文件内容没有PHP标签,PHP会将其视为纯文本或HTML,直接输出内容到前端。
剧透一下
flag.php
文件,这个文件有<?php
开头,语法也正确,所以会被解析为 PHP 文件。但文件内只有一个变量的定义,没有任何输出,所以直接访问该文件不会回显内容。- 若文件内容包含有效的PHP标签(如
那
php://filter/convert.base64-encode/resource=flag.php
是什么?PHP 的
php://
伪协议(PHP Wrapper Protocols)是一种用于访问输入/输出流(I/O Streams)和内存资源的特殊协议。它允许开发者通过文件系统函数(如file_get_contents()
、file_put_contents()
、fopen()
等)直接操作数据流,而无需真实的物理文件。像这样的文件包含协议还有
file://
、data://
等等。后面会详细展开。简单说说
php://
伪协议的几种用法(此处不细讲):php://input
:读取原始的 HTTP 请求体数据(如 POST 数据)。php://output
:直接向输出缓冲区写入数据。php://stdin
、php://stdout
、php://stderr
:访问标准输入、输出和错误流(常用于命令行脚本)。php://filter
:对数据流应用过滤器(如编码转换、压缩)。(本题所用方法)php://temp
、php://memory
:临时存储数据到内存或临时文件。php://fd
:直接访问文件描述符(File Descriptor)。
本题所用的
php://filter
与其说是“过滤器”,不如说是“转换器”,即读/写时转换目标资源内容的编码、格式等。先看看基本语法:1
php://filter/[操作]/[过滤器链]/resource=[目标资源]
- 操作:
read
(读取时过滤)、write
(写入时过滤)或空(默认同时应用读写)。 - 过滤器链:多个过滤器用
|
分隔,按顺序执行(无过滤器会输出原始文件)。 - 目标资源:可以是文件路径、URL 或其他伪协议(如
php://input
)。
关于过滤器链,PHP 内置了多种过滤器,分为几类:
字符串处理
string.rot13
:对字符串进行 ROT13 编码(把英文字母按字母表往后推 13 位,因为字母表有 26 位,所以加解密可以用同一套算法)。string.toupper / string.tolower
:转大写/小写。string.strip_tags
:去除 HTML/PHP 标签。
编码转换
convert.base64-encode / convert.base64-decode
:Base64 编解码。convert.quoted-printable-encode / convert.quoted-printable-decode
:QP 编解码。convert.iconv.*
:字符集转换(需安装 iconv 扩展)。
压缩处理
zlib.deflate / zlib.inflate
:使用 Zlib 压缩/解压。bzip2.compress / bzip2.decompress
:Bzip2 压缩/解压。
加密
mcrypt.* / mdecrypt.*
:加密解密(已废弃,不建议使用)。
所以本题是通过
convert.base64-encode
把会被解析成 PHP 文件、无回显的flag.php
转换为 Base64 编码然后输出,绕过了 PHP 文件的解析。Base64 解码得(已删去注释):
1
2
$flag="ctfshow{ead582c0-8247-4aa9-a2fa-abc1bb47c29d}";有
<?php
开头且语法正确,但作用仅仅是定义了一个变量$flag
,所以直接包含该文件会解析为 PHP 文件且无回显。转换为 Base64 编码,则被识别为文本文件,直接回显内容。