

ctf.show web5 完整解题步骤与原理
一、题目代码逻辑拆解
先梳理页面中 PHP 代码的核心规则:
- 必须通过 GET 请求传入两个参数
v1和v2 - 严格的参数校验:
v1必须是纯英文字母 (ctype_alpha()校验,仅支持 a-z/A-Z,不能包含数字、符号)v2必须是纯数字 (is_numeric()校验,仅支持 0-9 的数字)
- 核心判断:当
md5($v1) == md5($v2)成立时,直接输出 Flag。
二、核心漏洞原理:PHP 弱类型比较 + MD5 0e 绕过
这道题的考点是PHP 弱类型比较(==) 的经典漏洞,也是 CTF Web 入门的必考知识点:
- PHP 中,使用
==做相等判断时,会自动做类型转换。如果两个字符串的格式是0e+纯数字,会被解析为科学计数法的浮点数。 - 科学计数法中,
0e开头的数值本质是0 × 10的n次方,结果永远等于 0。 - 因此,只要两个字符串的 MD5 值都是
0e开头,它们的弱类型比较结果就会是true,哪怕原始字符串完全不同。
三、符合要求的 Payload 与解题步骤
1. 筛选符合条件的参数
我们需要找到两组满足校验规则、且 MD5 值为 0e 开头的字符串:
表格
| 参数 | 符合要求的取值 | 校验规则匹配 | 对应 MD5 值 |
|---|---|---|---|
| v1(纯字母) | QNKCDZO |
全英文字母,通过 ctype_alpha() 校验 |
0e83040045199378878257216928734 |
| v2(纯数字) | 240610708 |
纯数字,通过 is_numeric() 校验 |
0e46209743190650901956298873685 |
补充:你截图里的
v1=byGcY&v2=240610708同样有效,byGcY的 MD5 值也是 0e 开头,符合要求。
2. 完整解题操作
-
打开题目地址,在浏览器地址栏的 URL 末尾,拼接 GET 参数,最终访问的 URL 为: plaintext
https://b40a656a-e437-4bb4-b077-f2117008f78b.challenge.ctf.show/?v1=QNKCDZO&v2=240610708 -
访问拼接后的 URL,页面会直接输出 Flag。
-
复制完整的 Flag,提交到 ctf.show 平台即可完成题目。
四、补充知识点与备用方案
-
**为什么不用 MD5 强碰撞?**MD5 强碰撞是指两个不同内容生成完全一致的 MD5 值,实现难度高、成本大。这道题用弱类型的 0e 绕过即可,是入门题的标准考点。
-
其他可用的备用 Payload
表格
纯字母 v1 纯数字 v2 aabg7XSs 0e215962017 aabC9RqS 314282422 s878926199a 342302858 -
关键注意点
- 必须用 GET 方式传参,直接在 URL 拼接即可,无需其他工具
- 这里的判断是弱比较
==,如果是强比较===,就需要真正的 MD5 碰撞,这道题不涉及 ctype_alpha()不区分大小写,大小写字母均可通过校验;is_numeric()不能包含小数点、正负号,必须是纯数字。
这两个值是通过暴力枚举(Brute Force) 生成并筛选出来的,是 PHP 弱类型比较漏洞中公认的「经典 Payload」。
一、生成原理与过程
- 核心目标 :找到满足以下条件的两组字符串:
- 第一组(
v1):纯字母,MD5 值以0e开头,后面全是数字 - 第二组(
v2):纯数字,MD5 值以0e开头,后面全是数字
- 第一组(
- 实现方法:写一个脚本,循环生成随机字符串,计算 MD5,逐一检查是否符合规则。
简单的 Python 枚举示例(理解原理用)
python
运行
import hashlib
import random
import string
def find_md5_0e(prefix='', charset=string.ascii_letters, max_len=10):
"""暴力枚举寻找MD5以0e开头的字符串"""
while True:
# 随机生成指定字符集的字符串
length = random.randint(1, max_len)
s = prefix + ''.join(random.choice(charset) for _ in range(length))
# 计算MD5
md5_hash = hashlib.md5(s.encode()).hexdigest()
# 检查是否符合 0e+纯数字 的格式
if md5_hash.startswith('0e') and md5_hash[2:].isdigit():
print(f"找到匹配值:{s}")
print(f"MD5:{md5_hash}")
return s
# 示例:找纯字母的(对应v1)
print("正在寻找纯字母的0e开头MD5...")
find_md5_0e(charset=string.ascii_letters)
二、为什么这两个值最常用?
- 「经典」的定义 :它们是最早被批量枚举出来、且在 CTF 社区中流传最广的两组值,类似「数学公式」一样被大家记住。
- 流传过程 :
- 早期安全研究者在研究 PHP 弱类型比较漏洞时,通过脚本跑出来了一批这样的字符串
- 这些 Payload 被整理成「CTF 常用 Payload 清单」,在各类 Writeup(题解)中反复出现
- 久而久之,
QNKCDZO和240610708就成了这一考点的「标准答案」
三、更多同类常用 Payload(可直接替换)
除了题目中的这两个,以下是同样常用的备选值,遇到同类题可直接用:
表格
| 纯字母(v1) | 对应 MD5 | 纯数字(v2) | 对应 MD5 |
|---|---|---|---|
QNKCDZO |
0e83040045199378878257216928734 |
240610708 |
0e46209743190650901956298873685 |
aabg7XSs |
0e087386482136013740957780965295 |
0e215962017 |
0e291242474110885585856788203760 |
aabC9RqS |
0e041022512123460537604701758222 |
314282422 |
0e631149826846557425283880885155 |