
Type Juggling
Test and exploit PHP loose equality (`==`) in auth, HMAC, and token checks during authorized security review.
Overview
Type Juggling is an agent skill for the Ship phase that documents PHP type juggling and weak-comparison bypass techniques for authorized auth and token-validation testing.
Install
npx skills add https://github.com/yaklang/hack-skills --skill type-jugglingWhat is this skill?
- Routes sinks: loose `==` vs strict `hash_equals` / `===` before attempting bypass payloads
- Magic hash and numeric coercion probes (`0e…`, `240610708` / `QNKCDZO` style comparisons)
- First-pass payload set for password arrays, empty strings, booleans, and JSON shapes
- Coverage of strcmp, json_decode, and intval CTF-style tricks with PHP major-version awareness
- Explicit note to abandon path when strict comparison is already enforced
- First-pass payload list includes 10+ probe shapes (arrays, 0e strings, QNKCDZO-related md5 pairs)
Adoption & trust: 1.1k installs on skills.sh; 980 GitHub stars; 2/3 security scanners passed (skills.sh audits).
What problem does it solve?
Legacy PHP compares passwords or tokens with `==`, and you need a disciplined way to prove bypass via coercion instead of random guessing.
Who is it for?
Builders doing authorized reviews on PHP login, webhook signatures, or CTF challenges where loose equality is visible in source or behavior.
Skip if: Attacking systems without permission, or modern codebases already standardized on `===` and `hash_equals` throughout auth paths.
When should I use this skill?
Authentication, HMAC/signature checks, or token validation uses loose equality, numeric coercion, or hash comparisons without strict types.
What do I get? / Deliverables
You identify exploitable loose-comparison branches, validate behavior against target PHP version, and document bypass paths—or confirm strict checks block the attack class.
- Documented comparison sink and bypass proof
- Minimal reproducible payload set for the vulnerable branch
Recommended Skills
Journey fit
How it compares
Focused PHP weak-comparison methodology—not a general OWASP checklist or automated SAST product.
Common Questions / FAQ
Who is type-juggling for?
Solo developers and security reviewers using coding agents to analyze PHP authentication and signature logic under authorized scope.
When should I use type-juggling?
During Ship security review when you see `==` on passwords, tokens, or md5/sha comparisons—or lab CTF tasks with numeric string coercion.
Is type-juggling safe to install?
The skill describes offensive techniques; install only for legal testing contexts and consult the Security Audits panel on this Prism page before running probes against any environment.
SKILL.md
READMESKILL.md - Type Juggling
# 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) ```text password[]=x password= 0 0e12345 240610708 QNKCDZO true [] {"password":true} admin%00 ``` ### Minimal PHP probes (local or `php -r` in lab) ```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')); ``` ### 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) ```php 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** t