type-juggling
PHP type juggling and weak comparison (`==`) bypass. Use when authentication, HMAC/signature checks, or token validation uses loose equality, numeric coercion, or hash comparisons without strict types — common in legacy PHP and CTF-style code paths.
By yaklang · 2,948 installs
npx skills add yaklang/hack-skills --skill type-juggling
Source repository · Upstream listing
SKILL: PHP Type Juggling — Weak Comparison & Magic Hash Bypass
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. Routing note: when you encounter PHP login/signature logic or code like md5($ GET['x'])==md5($ GET['y']) , start with this skill; if hash equals / === is already used, this path usually does not apply.
0. QUICK START
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)
Minimal PHP probes (local or php r in lab)
Routing hints
Clue Next step
Source code uses == to compare passwords, tokens, or HMAC values Go to Sections 1 3
md5($a) == md5($b) or loose sha1 comparison Section 2 magic hashes
hash hmac(...) != '0' or compared with "0" Section 3
strcmp 、 json decode(..., true) 、 intval Section 5
1. LOOSE COMPARISON ( == ) — TRUTH TABLE & VERSIONS
PHP compares operands with type juggling unless you use === or hash equals() for secrets.
1.1 Core examples (strings vs numbers)
Expression Result Mechanism (short)
'0010e2' == '1e3' 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”)
'0e462097431906509019562988736854' == '0e830400451993494058024219903391' true Both parse as 0.0 in scientific notation
'123a' == 123 true String 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 true Empty string → 0
'' == false true both “falsy” in loose rules
false == NULL true loose equality
0 == false true loose equality
'' == 0 == false == NULL true (chain) Each adjacent pair is true under == ( ''==0 , 0==false , false==NULL ) — classic “falsy” chain
'0' == false true String '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
1.2 PHP 5 vs 7 vs 8 (high signal deltas)
Topic PHP 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.
1.3 Safe alternative (defense / verification)
2. MAGIC HASHES ( 0e… + digits only)
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.
2.1 Reference table (MD5 / SHA 1 and longer algos)
Algorithm Example input Digest (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.0 → true .
2.2 Exploit pattern in code
2.3 Payload sketch (pair hunting)
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.
3. HMAC BYPASS (LOOSE COMPARE VS "0" OR 0 )
If logic uses loose inequality against a constant:
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 == .
Example (MD5 style 0e digest for a numeric message)
Concept Example
Message type Unix timestamp, incrementing id, millisecond clock
Timestamp brute force pattern Tutorials 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.
Goal Find $data such that hash hmac('md5', $data, $key) matches ^0e[0 9]+$
Note Without knowing $key , you may still brute $data if algorithm/output are visible in a oracle; CTFs often leak or fix key
Mitigation : hash equals($mac, $expected) + fixed length hex/binary encoding; never compare HMAC to bare "0" .
4. NULL JUGGLING (ARRAYS & TYPE ERRORS)
Invalid types can yield NULL on the compared side; loose equality to another NULL or coerced value may pass.
Call Typical PHP 7/8 behavior
md5([]) PHP 8: TypeError ; older: warnings / not reliable across versions
sha1([]) Same
Idea If error handler or custom wrapper converts failures to NULL , then NULL == NULL or NULL == sha1("x") if other side is also NULL
Real audits : look for @ , custom try/catch that sets hash to null , or user input passed where a string is required.
5. CTF PATTERNS
5.1 strcmp / strcasecmp with arrays
Payload:
5.2 intval bypass
5.3 json decode + true for associative array auth
5.4 is numeric + loose compare
5.5 Deserialization + magic properties
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.
6. DECISION TREE
Tool references
Tool Use
Local php CLI Reproduce == behavior for target major version
Static code review Grep == , != on crypto outputs; find missing hash equals
CTF frameworks Payload 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.