CTF Web 入门:BUUCTF Ez_bypass 1 的 3 步完整解题与漏洞复现
CTF Web 入门:BUUCTF Ez_bypass 1 的深度解析与实战复现
初识 PHP 代码审计:从 Ez_bypass 开始
当你第一次接触 CTF Web 题目时,PHP 代码审计往往是必经之路。BUUCTF 的 Ez_bypass 1 作为一道经典的入门题,完美展现了 PHP 弱类型比较和数组绕过等核心概念。这道题看似简单,却蕴含着 Web 安全中几个关键知识点:
- MD5 强比较绕过:理解
===与==的区别 - PHP 数组特性:利用数组绕过特定函数检查
- 弱类型比较:掌握 PHP 类型转换的"怪癖"
- HTTP 请求方法:GET 与 POST 传参的配合使用
让我们先搭建一个简单的测试环境来验证这些概念。你可以使用以下 Docker 配置快速启动一个 PHP 环境:
FROM php:7.4-apache RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli COPY src/ /var/www/html/代码审计:逐层剖析漏洞点
第一步:GET 参数的条件检查
题目源码中第一个关键检查点:
if(isset($_GET['gg'])&&isset($_GET['id'])) { $id=$_GET['id']; $gg=$_GET['gg']; if (md5($id) === md5($gg) && $id !== $gg) { echo 'You got the first step'; // 后续代码... } }这里需要同时满足两个看似矛盾的条件:
md5($id) === md5($gg)- 两个值的 MD5 必须严格相等$id !== $gg- 两个原始值不能相同
绕过技巧:利用 PHP 处理数组时的特性。当md5()函数接收到数组参数时,会返回 NULL 并产生警告,但比较时 NULL === NULL 成立。
| 参数类型 | md5() 返回值 | 比较结果 |
|---|---|---|
| 字符串 | 32位哈希值 | 正常比较 |
| 数组 | NULL | NULL === NULL |
构造 Payload:
?id[]=1&gg[]=2第二步:POST 参数的巧妙绕过
通过第一关后,代码进入第二个检查点:
if(isset($_POST['passwd'])) { $passwd=$_POST['passwd']; if (!is_numeric($passwd)) { if($passwd==1234567) { // 输出flag } } }这里需要满足:
!is_numeric($passwd)- 不是纯数字$passwd==1234567- 弱类型比较等于 1234567
PHP 弱类型比较特性:
==会进行类型转换后再比较- 字符串 "1234567a" 转换为数字时会截取前面数字部分
is_numeric()对 "1234567a" 返回 false
构造 Payload:
passwd=1234567a实战演示:两种工具链解决方案
方案一:HackBar 快速验证
HackBar 是 Firefox 的一款插件,适合快速测试:
- 在 URL 后附加 GET 参数:
?id[]=1&gg[]=2 - 在 POST 数据区域填写:
passwd=1234567a - 点击"Execute"执行请求
方案二:Burp Suite 专业抓包
对于更复杂场景,Burp Suite 是更专业的选择:
- 拦截浏览器请求
- 修改 GET 参数:
GET /challenge.php?id[]=1&gg[]=2 HTTP/1.1 - 添加 POST 数据:
passwd=1234567a - 转发请求查看响应
漏洞复现:搭建本地测试环境
为了深入理解,建议在本地复现漏洞。以下是完整步骤:
创建
flag.php:<?php $flag = 'flag{test_flag}'; ?>创建题目源码文件
index.php:<?php include 'flag.php'; if(isset($_GET['gg'])&&isset($_GET['id'])) { $id=$_GET['id']; $gg=$_GET['gg']; if (md5($id) === md5($gg) && $id !== $gg) { echo 'You got the first step'; if(isset($_POST['passwd'])) { $passwd=$_POST['passwd']; if (!is_numeric($passwd)) { if($passwd==1234567) { echo 'Good Job!'; highlight_file('flag.php'); } } } } } ?>使用 PHP 内置服务器启动:
php -S localhost:8000测试 Payload:
curl "http://localhost:8000/?id[]=1&gg[]=2" -d "passwd=1234567a"
知识延伸:PHP 类型比较的陷阱
这道题的核心在于 PHP 的类型系统。下表总结了常见的比较陷阱:
| 比较表达式 | 结果 | 原因分析 |
|---|---|---|
| "123" == 123 | true | 字符串转数字 |
| "123abc" == 123 | true | 字符串截取数字部分 |
| "0e123" == "0e456" | true | 科学计数法解释为0 |
| [] === [] | true | 数组比较 |
| md5([]) === md5([]) | true | 都返回NULL |
防御建议:
- 始终使用
===进行严格比较 - 对用户输入进行严格类型检查
- 使用
ctype_digit()替代is_numeric()检查纯数字
CTF 解题思维训练
通过这道题,我们可以总结出 CTF Web 题目的通用解题思路:
- 代码审计:定位关键条件判断
- 参数分析:识别所有用户可控输入点
- 函数研究:了解每个函数的行为和限制
- 特性利用:寻找语言特性或函数缺陷
- Payload构造:组合利用多个漏洞点
- 工具验证:使用工具发送精心构造的请求
对于新手来说,建议建立自己的漏洞知识库,记录每种漏洞类型的:
- 触发条件
- 利用方法
- 防御措施
- 典型题目
进阶挑战:变种题目设想
理解了基本原理后,可以尝试解决以下变种题目:
修改后的版本:
if (hash('sha256', $id) === hash('sha256', $gg) && $id != $gg)提示:不同哈希函数对数组的处理可能不同
加强版检查:
if (is_string($passwd) && $passwd===strval(1234567))提示:需要完全匹配类型和值
多重验证:
if ($passwd==1234567 && strlen($passwd)==7)提示:考虑PHP类型转换与字符串长度关系
