Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2026-18963-Exploit — استغلال لثغرة Keycloak CVE-2026-18963 يتيح الاستيلاء غير المصادق عليه على الحسابات عبر تجاوز إعادة تعيين بيانات الاعتماد. يتضمن كشفًا آمنًا، وإثباتًا غير مدمر، واستيلاءً كاملًا، وتعداد أسماء المستخدمين، ومختبرًا بإصدارات قابلة للاستغلال ومصححة. | Kitploit
أدوات/GitHubGitHub/snizi/cve-2026-18963-exploit
المصادقة والترخيصتحليل الثغرات الأمنيةالاستغلالاستغلال تطبيقات الويباختبار الاختراق
GitHubsnizi/cve-2026-18963-exploit

CVE-2026-18963-Exploit

استغلال لثغرة Keycloak CVE-2026-18963 يتيح الاستيلاء غير المصادق عليه على الحسابات عبر تجاوز إعادة تعيين بيانات الاعتماد. يتضمن كشفًا آمنًا، وإثباتًا غير مدمر، واستيلاءً كاملًا، وتعداد أسماء المستخدمين، ومختبرًا بإصدارات قابلة للاستغلال ومصححة.

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة
عرض المستودع
71منذ 4 أياملم تتم المراجعة بعد

CVE-2026-18963 — تجاوز إعادة تعيين بيانات الاعتماد في Keycloak → استيلاء غير مصادق عليه على الحساب

CVE Affected Python Dependencies

بمعرفة اسم مستخدم أو عنوان بريد إلكتروني فقط، يمكن لمهاجم غير مصادق عليه تعيين كلمة مرور عشوائية على أي حساب في Keycloak. يتم تسليم بريد إعادة تعيين كلمة المرور إلى الضحية الحقيقية ولا تكون هناك حاجة إليه أبدًا — لا يقرأ المهاجم صندوق بريد، ولا ينقر على رابط، ولا يمتلك أي بيانات اعتماد أو جلسة سابقة.

المتأثر: Keycloak 26.0.0 – 26.7.1. تم الإصلاح في الإصدار 26.7.2.


هل أنا معرّض للخطر؟

أمر واحد. لا يتطلب اسم مستخدم صالحًا، ولا آثار جانبية — لا يرسل بريدًا إلكترونيًا، ولا يكتب على أي حساب، ويتوقف قبل الخطوة القابلة للاستغلال.```bash git clone https://github.com/Snizi/CVE-2026-18963-Exploit cd CVE-2026-18963-Exploit

python3 cve_2026_18963_poc.py
--base https://sso.example.com --realm YOUR_REALM
--client-id account
--redirect-uri https://sso.example.com/realms/YOUR_REALM/account/
--safe-check

root@kitploit:~
Python 3.9+، المكتبة القياسية فقط. لا شيء لتثبيته.

| الخروج | الحكم | المعنى |
|:---:|---|---|
| `0` | 🔴 **قابل للاستغلال** | تم تقديم بوابة البريد الإلكتروني المتوقفة — العيب نفسه |
| `2` | 🟢 **مُصحَّح** | انحرف التدفق إلى تسجيل الدخول وبقي هناك (الإصلاح #51844 موجود) |
| `2` | 🟡 **مُخفَّف** | إعادة تعيين بيانات الاعتماد غير قابلة للوصول — *نسيت كلمة المرور* معطّلة. **ليس تصحيحًا.** |
| `3` | ⚪ **غير حاسم** | استجابة غير معترف بها — **لا تقرأ هذا على أنه نجاح** |

شغّله لكل نطاق (realm) — *نسيت كلمة المرور* إعداد خاص بكل نطاق، و`master` يُحتسب.
التفاصيل، ولماذا لا يحتاج الفحص إلى مستخدم ولا يلمس شيئًا، في
[§4a](#4a-safe-detection---safe-check--start-here).

**هل تعرف بالفعل أنك مكشوف؟** انتقل إلى [المعالجة](#8-remediation) و
[الكشف / اصطياد التهديدات](#9-detection).

### جرّبه بدون هدف

يأتي المستودع مع مختبر يُشغّل إصدارًا قابلًا للاستغلال **26.7.1** وإصدارًا مُصحَّحًا **26.7.2**
جنبًا إلى جنب ضد نطاق متطابق، بالإضافة إلى صندوق بريد لمراقبة وصول بريد إعادة التعيين
وبقائه غير مقروء أثناء الاستيلاء على الحساب:```bash
cd lab && docker compose up -d

python3 ../cve_2026_18963_poc.py --base http://localhost:8080 \
  --realm poc --client-id poc-app --safe-check   # VULNERABLE
python3 ../cve_2026_18963_poc.py --base http://localhost:8100 \
  --realm poc --client-id poc-app --safe-check   # PATCHED

⚠️ الاختبار المصرّح به فقط

هذا المستودع مخصص للمدافعين ومستجيبي الحوادث ومختبري الاختراق المصرح لهم. قم بتشغيله ضد الأنظمة التي تملكها أو لديك إذن كتابي لاختبارها. كل شيء هنا يأتي مع مختبر ضعيف مكتفٍ ذاتيًا (lab/)، فلا حاجة للمس أي شيء خارجي لتتعلم كيف يعمل الثغرة. توجيهه نحو بنية تحتية تابعة لطرف ثالث دون تصريح يعد غير قانوني في معظم الدول وليس شيئًا يدعمه هذا المشروع.

المراجع: keycloak#51833 · GHSA-4gv3-mc9p-5wqc · الإصلاح keycloak#51844


المحتويات

  • 1. السبب الجذري
  • 2. الإصدارات المتأثرة (بما في ذلك الخطوط القديمة)
  • 3. المختبر
  • 4. الاستخدام
    • 4أ. الكشف الآمن (--safe-check) — ابدأ من هنا
    • 4ب. الإثبات غير المدمر (--check)
    • 4ج. الاستيلاء الكامل
    • 4د. تعداد أسماء المستخدمين (--enum)
  • 5. سمات تسجيل الدخول المخصصة
  • 6. الفجوة المعروفة — PKCE
  • 7. التحقق الذي تم إجراؤه
  • 8. المعالجة
  • 9. الكشف
  • المؤلف

1. السبب الجذري

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

العيب 1 — علامة غير محددة النطاق ولاصقة

services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java

processAction() — أي طلب POST يحمل مفتاح النموذج tryAnotherWay:```java processor.getAuthenticationSession().setAuthNote( AuthenticationProcessor.AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED, "true"); return createSelectAuthenticatorsScreen(model);

root@kitploit:~
الملاحظة هي **قيمة منطقية بسيطة بدون أي سجل لأي مجموعة تنفيذ تنتمي إليها**. يتم مسحها فقط في الفرع الذي يعالج معامل `authenticationExecution` المُرسل. احذف هذا المعامل — كما يفعل هذا الإثبات المفاهيمي (PoC) طوال الوقت — وستبقى العلامة مضبوطة طوال عمر جلسة المصادقة.

`processFlow()` — بينما تكون العلامة صحيحة (truthy)، يتم تخطي تقييم التدفق الطبيعي:```java
if (Boolean.parseBoolean(authSession.getAuthNote(AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED))) {
    String lastExecutionId = authSession.getAuthNote(CURRENT_AUTHENTICATION_EXECUTION);
    if (lastExecutionId != null) {
        AuthenticationExecutionModel executionModel =
            realm.getAuthenticationExecutionById(lastExecutionId);
        if (executionModel != null)
            return createSelectAuthenticatorsScreen(executionModel);   // <-- attacker-usable form
    }
}

يقدّم نموذجًا قابلًا للإرسال يستهدف أي تنفيذ متوقف حاليًا، بدلًا من إبقاء الجلسة مثبّتة على "انتظار البريد الإلكتروني".

الرابط هو processResult() case FORK: — عندما يتم تشغيل إرسال بريد إعادة التعيين، فإنه يختم CURRENT_AUTHENTICATION_EXECUTION = <reset-credential-email execution id> ويُفرّع المتصفح إلى صفحة تسجيل الدخول. التنفيذ المتوقف هو تحديدًا بوابة البريد الإلكتروني.

العيب 2 — بوابة البريد الإلكتروني لا تتحقق أبدًا من رمز الإجراء

`services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialEmail.java````java @Override public void action(AuthenticationFlowContext context) { context.getUser().setEmailVerified(true); context.success(); }

root@kitploit:~
Unconditional. لا شيء يتحقق من أن التدفق استُؤنف بواسطة رمز إجراء صالح،
لذا فإن *الوصول* إلى `action()` يُعتبر مكافئًا لإثبات السيطرة على صندوق البريد.

### السلسلة```
tryAnotherWay POST            → sticky AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED="true"
submit victim identifier      → mail sent to victim, e-mail execution parked (FORK)
re-enter reset-credentials    → sticky flag serves a form targeting the parked e-mail execution
POST that form                → ResetCredentialEmail.action() → success() → gate bypassed
                              → flow advances to UPDATE_PASSWORD → attacker sets the password

ستة طلبات HTTP، بدون مصادقة، وبدون معامل authenticationExecution في أي نقطة.

الإصلاح (PR #51844)

  • الملاحظة الآن تخزّن model.getId()، وprocessFlow() تحترمها فقط عندما تساوي CURRENT_AUTHENTICATION_EXECUTION، وإلا تزيلها. في الهجوم يختلف الاثنان (معرّف اختيار المستخدم مقابل معرّف بوابة البريد الإلكتروني) — وهذا بالضبط ما يكتشفه التصحيح، وبالضبط الإشارة التي يعتمد عليها --safe-check.
  • ResetCredentialEmail.action() يتطلب الآن context.getUser().getId().equals(authNote(ACTION_TOKEN_USER_ID)) وإلا يفشل مع INVALID_USER.

2. الإصدارات المتأثرة (بما في ذلك الخطوط القديمة)

ماذا يعني "القديم" لهذا CVE

  • الإصدارات القديمة ليست آمنة تلقائيًا — إنها آمنة لسبب محدد. الملاحظة المنطقية الثابتة أُدخلت في 26.0.0 عبر الالتزام 6a9e60bb، الذي أضاف شاشة محدد المصادقة "جرّب طريقة أخرى" إلى تدفق إعادة التعيين. أي شيء أقدم ببساطة يفتقر إلى مسار الكود هذا. ويشمل ذلك التوزيعات القديمة القائمة على WildFly وRH-SSO 7.x، وهي غير متأثرة بهذا الخطأ بينما تبقى خارج نطاق الدعم وعرضة للكثير من الثغرات الأخرى. البقاء على بناء قديم ليس علاجًا.
  • خطوط 26.x القديمة هي المشكلة الحقيقية. 26.7.2 هو الإصدار المُصلَح الوحيد المنشور لمسار المجتمع. إذا كان النشر يعتمد على 26.0 – 26.6، فلا يوجد إصدار تصحيحي على ذلك الخط — الإصلاح يتطلب ترقية إصدار ثانوي، وليس إصدارًا نقطيًا. وسما 26.4.15 / 26.6.6 هما نقل خلفي من المورّد وليسا قابلين للتبادل مع صور المجتمع.
  • نظرًا لأن العديد من النشرات طويلة العمر مثبتة على إصدار 26.x أقدم لأسباب توافقية، فإن "نحن محدّثون بالكامل على خطنا" افتراض شائع وغير صحيح هنا. تحقق من البناء قيد التشغيل، وليس من سياسة التحديث.

الشروط المسبقة: النطاق مفعّل فيه نسيت كلمة المرور و تدفق إعادة تعيين الاعتمادات المرتبط به يستخدم مصادق reset-credential-email المدمج.


3. المختبر

يوفّر المستودع كلاً من Keycloak القابل للاستغلال والمُصلَح، مع استيراد نفس النطاق، بالإضافة إلى Mailpit لالتقاط بريد إعادة التعيين — حتى تتمكن من مشاهدته يصل ويبقى غير مقروء بينما يتم الاستيلاء على الحساب.```bash cd lab docker compose up -d

root@kitploit:~
| الخدمة | الرابط | الإصدار |
|---|---|---|
| `kc-vuln` | http://localhost:8080 | 26.7.1 — **قابل للاستغلال** |
| `kc-patched` | http://localhost:8100 | 26.7.2 — **مجموعة تحكم مُصححة** |
| `kc-mailpit` | http://localhost:8025 | صندوق بريد الضحية |

المجال `poc`، العميل العام `poc-app`، المستخدم `victim` / `OriginalPassw0rd!`، مسؤول
Keycloak `admin` / `admin`.

ثبّت إصدارات مختلفة باستخدام `KC_VULN_VERSION` / `KC_PATCHED_VERSION`:```bash
KC_VULN_VERSION=26.5.7 docker compose up -d keycloak-vuln

بين عمليات التشغيل، يقوم lab/reset-victim.sh باستعادة كلمة مرور الضحية (KC=http://localhost:8100 lab/reset-victim.sh يستهدف النسخة المصدّاة).

يقوم lab/legit_reset.py بإجراء إعادة تعيين حقيقية عن طريق سحب رابط رمز الإجراء من Mailpit والنقر عليه. وهو العيّنة الضابطة لأعمال الكشف في §9 — قم بتشغيله والاستغلال ضد نفس النطاق، ثم قارن التتبعات.

التفكيك: docker compose down -v.


4. الاستخدام

Python 3.9+، المكتبة القياسية فقط — لا تبعيات، يعمل على أي خادم قفز.``` --base Keycloak base URL (e.g. https://sso.example.com) --realm realm name --client-id any enabled public client with the standard flow --redirect-uri a URI permitted by that client (default http://localhost:9999/callback) --insecure skip TLS verification --verbose log every HTTP request --dump FILE write the response body of a failing step to FILE

root@kitploit:~
`--client-id` يمكن أن يكون أي عميل عام مفعّل مع التدفق القياسي. العميل المدمج
`account` موجود في كل realm وهو الخيار الموثوق، لكنه يقيّد
redirect URIs، لذا يجب أن يكون `--redirect-uri` **إذن**
`<base>/realms/<realm>/account/` — الافتراضي مرفوض وتفشل الخطوة 1.

### 4أ. الكشف الآمن (`--safe-check`) — ابدأ من هنا

لا يتطلب **اسم مستخدم صالحًا** وليس له **آثار جانبية**. هذا هو الفحص الذي يجب استخدامه
عندما يجب ألا تزعج الهدف.```bash
python3 cve_2026_18963_poc.py \
  --base https://sso.example.com --realm corp \
  --client-id account \
  --redirect-uri https://sso.example.com/realms/corp/account/ \
  --safe-check

لماذا لا يحتاج إلى مستخدم، ولا يرسل بريدًا. ResetCredentialEmail.authenticate() يتفرع أيضًا لمستخدم غير معروف:```java if (user == null) { context.forkWithSuccessMessage(EMAIL_SENT); return; }

root@kitploit:~
`processResult()` `case FORK:` لذلك يوقف `CURRENT_AUTHENTICATION_EXECUTION`
على تنفيذ البريد الإلكتروني **حتى لو لم يتم العثور على أحد** — ولا يتم إرسال أي بريد،
لأنه لا يوجد أحد لإرسال البريد إليه. يتوقف الفحص عند أداة التمييز ولا يرسل أبدًا
إلى البوابة، لذا لا يتم تشغيل `action()` أبدًا: لا NPE على الهدف، ولا كتابة `emailVerified`،
ولا بريد، ولا يتم لمس أي حساب.

**إنه يؤكد على الإشارة الإيجابية فقط.** VULNERABLE ⟺ الخطوة 5 تُرجع نموذجًا
لا يزال داخل `login-actions/reset-credentials` يختلف `execution` الخاص به عن
تنفيذ اختيار المستخدم. هذا *هو* الخطأ: ملاحظة
`AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED` القديمة التي تخدم بوابة البريد الإلكتروني المتوقفة.
كلا الجزأين مهمان — المسار يثبت أننا ما زلنا في تدفق إعادة التعيين، ومعرّف التنفيذ
المختلف يثبت أنها بوابة البريد الإلكتروني وليست إعادة عرض.

أي شيء آخر **ليس** صامتًا يُعتبر مُصححًا. PATCHED يتطلب دليله الخاص
(متفرع إلى `login-actions/authenticate` *و* وجود حقل إدخال كلمة مرور)؛ كل ما تبقى
هو INCONCLUSIVE ويحتاج إلى إنسان. تصميم سابق اعتبر "ليس نموذج البوابة"
مُصححًا، وهو ما يحوّل بصمت كل سمة مخصصة، وصفحة خطأ، وكتلة WAF
وشاشة وسيطة إلى شهادة سلامة خاطئة.

### 4b. إثبات غير مدمر (`--check`)

يقود السلسلة الكاملة لكنه يتوقف عند نموذج تحديث كلمة المرور. الوصول إلى ذلك النموذج
بدون رمز إجراء هو أمر قاطع.```bash
python3 cve_2026_18963_poc.py \
  --base https://sso.example.com --realm corp \
  --client-id account \
  --redirect-uri https://sso.example.com/realms/corp/account/ \
  --victim [email protected] --check

هناك تأثيران جانبيان لا مفر منهما، لأنهما يحدثان في مرحلة سابقة لنموذج كلمة المرور — اذكرها في نطاق الاختبار:

  • يتم إرسال بريد إلكتروني لإعادة تعيين كلمة المرور إلى الضحية الحقيقية (الخطوة 4 هي طلب إعادة تعيين حقيقي)، و
  • تقوم الدالة الضعيفة action() بتعيين emailVerified = true على الحساب.

لا يتم تعديل أي بيانات اعتماد. يُفضَّل استخدام حساب اختبار مخصص.

4c. الاستيلاء الكامل

للمختبر أو للعرض التوضيحي المصرَّح به صراحةً فقط.```bash python3 cve_2026_18963_poc.py
--base http://localhost:8080 --realm poc --client-id poc-app
--victim victim --new-password 'PoCPassw0rd!1'

root@kitploit:~
الخروج برمز `0` يعني `قابل للاستغلال` · `2` يعني `غير قابل للاستغلال` · `1` يعني `تم تغيير كلمة المرور لكن فشل منح التأكيد`
(وجّه `--verify-client-id` إلى عميل لديه منح وصول مباشر).

إكمال التدفق يعيد أيضًا **رمز تفويض OIDC للضحية**، لذا
يكون الاستيلاء فوريًا — لا حاجة لتسجيل دخول ثانٍ بكلمة المرور الجديدة.

### 4د. تعداد أسماء المستخدمين (`--enum`)

نفس الثغرة تُعد مؤشرًا لأسماء المستخدمين، وهي أقوى مما تسمح به Keycloak عادةً.
`ResetCredentialEmail.authenticate()` يعيد عمدًا رسالة متطابقة
*"يجب أن تتلقى بريدًا إلكترونيًا قريبًا"* للمستخدمين الحقيقيين وغير المعروفين، لذا
لا يمكن استخدام نموذج إعادة التعيين نفسه للتعداد — هذا الدفاع لا يزال قائمًا في الخطوة 4. لكنه
ينهار عند **الخطوة 6**، حيث `action()` يصل إلى المستخدم دون قيد أو شرط
(`context.getUser().setEmailVerified(true)`).

| المعرف | الخطوة 6 | الحكم |
|---|---|---|
| مستخدم حقيقي | `200`، يصل إلى نموذج تحديث كلمة المرور | صالح |
| مستخدم غير معروف | `400` (صفحة خطأ NPE) | غير صالح |```bash
python3 cve_2026_18963_poc.py \
  --base http://localhost:8080 --realm poc --client-id poc-app \
  --enum candidates.example.txt

لا يغيّر كلمة المرور أبدًا. يخرج بالرمز 0 إذا تم حلّ أي معرّف، وبالرمز 2 بخلاف ذلك.

التكلفة لكل فحص — اقرأ قبل التشغيل. الوصول إلى الأوراكل يتطلب إكمال الخطوة 4، لذا كل فحص ضد حساب حقيقي يرسل إلى ذلك الشخص رسالة إعادة تعيين كلمة مرور حقيقية ويضبط emailVerified = true على سجله. إنه ليس فحصًا صامتًا: إنه مرئي لصاحب الحساب ويغيّر بياناته. قائمة أسماء من 5000 اسم تعني 5000 رسالة بريد إلكتروني لأشخاص حقيقيين و5000 حساب معدّل.

استخدمه لإثبات وجود الأوراكل على عدد قليل من المعرّفات من أجل التقرير — وليس لجمع دليل كامل. الحراسات متحفظة عمدًا:

  • --enum-max N يرفض القوائم الأطول من N (الافتراضي 25)
  • --enum-delay SEC يتوقف بين الفحوصات (الافتراضي 2.0)

رفع أي منهما يجب أن يكون قرارًا واعيًا يُسجَّل في ملاحظات المشاركة.

زاوية الإبلاغ: هذا يهزم ضابط مكافحة التعداد الذي نفّذه Keycloak عن قصد. يستحق الكتابة عنه كنتيجة مستقلة إلى جانب الاستيلاء، وهو يلغي "أسماء المستخدمين لدينا غير قابلة للتخمين" كعامل مخفِّف.


5. سمات تسجيل الدخول المخصصة

أي نشر جاد يشحن سمة تسجيل دخول مخصصة، والسمات المخصصة تعيد تسمية أو تحذف معرّفات العناصر القياسية (kc-form-login, kc-reset-password-form, kc-select-credential-form, kc-passwd-update-form). أداة تعتمد على تلك المعرّفات تبلّغ عن نتيجة سلبية خاطئة على النشرات الأكثر أهمية تحديدًا — هذه الأداة فعلت ذلك، قبل إعادة كتابتها. السمات التي شوهدت في الميدان تستخدم معرّفات مثل id="login-form" وتشحن رابط نسيت كلمة المرور بسمة href فارغة.

لذلك يعتمد هذا الإثبات المفاهيمي على لا شيء يتحكم به السمة:

  • عناوين URL لإجراءات النماذج فقط. كل قرار يُتَّخذ من action= للنماذج على الصفحة — login-actions/reset-credentials, login-actions/authenticate, login-actions/required-action — ومن معامل الاستعلام execution داخلها. تلك المسارات تُنتج بواسطة LoginActionsService الخاص بـ Keycloak نفسه، وليس بواسطة السمة.
  • لا معرّفات عناصر. ابحث في المصدر: لا يوجد معرّف kc-* واحد فيه.
  • لا نصوص رسائل. سلاسل الاستجابة مترجمة — عالم ألماني يجيب "Reset Credential nicht erlaubt"، والمطابقة على "You should receive an email" تنكسر على كل عالم غير إنجليزي.
  • لا يتبع أبدًا رابط "نسيت كلمة المرور" المخصص. قد يكون الرابط غائبًا، أو فارغًا، أو مدفوعًا بـ JavaScript، أو يشير إلى مكان خارج Keycloak تمامًا — لا شيء من ذلك يقول أي شيء عن إمكانية الوصول إلى نقطة النهاية. الأداة تبني /realms/<realm>/login-actions/reset-credentials?client_id=…&tab_id=… وتفحصه مباشرة، آخذةً tab_id من أي نموذج تعرضه صفحة تسجيل الدخول.

إذا كان الهدف لا يزال يُرجع INCONCLUSIVE، شغّل مع --verbose --dump out.html واقرأ الاستجابة — الأداة ترفض التخمين عمدًا.


6. فجوة معروفة — PKCE

عميل يفرض PKCE يرفض الخطوة 1 بـ Missing parameter: code_challenge_method. يُبلَّغ عن هذا كـ INCONCLUSIVE (خروج 3)، وليس أبدًا كنجاح. حتى يدعم PKCE، لا يمكن فحص عالم يكون فيه العميل العام الوحيد القابل للاستخدام يفرض PKCE بهذه الأداة — جرّب عميل account المدمج، الذي لا يفرضه عادةً.


7. التحقق المُنجز

كل تشغيل أدناه ضد المختبر في هذا المستودع، باستخدام الكود كما نُشر.

نتيجتان تستحقان الإشارة إليهما خارج نص الاستشارة:

  1. الحسابات التي لا تحتوي على عنوان بريد إلكتروني قابلة للاستغلال. ResetCredentialEmail.authenticate() يسلك مسار forkWithSuccessMessage عندما يكون user.getEmail() فارغًا، والذي لا يزال يوقف التنفيذ عبر case FORK:. الأمر نفسه ينطبق على فشل إرسال SMTP — خادم بريد معطّل أو غائب ليس تخفيفًا. هذا مهم مباشرةً للعوالم المندمجة مع AD/LDAP، حيث لا تحمل الحسابات غالبًا أي سمة بريد.
  2. إكمال التدفق يسجّل دخول المهاجم كضحية. إعادة التوجيه النهائية تحمل رمز تفويض OIDC صالحًا، لذا يكون الحساب مخترقًا في اللحظة التي يُرسَل فيها نموذج كلمة المرور.

المصادقة متعددة العوامل ليست تخفيفًا. تدفق إعادة تعيين البيانات الافتراضي لا يحتوي على خطوة OTP، وبمجرد المرور، يمكن للمهاجم إزالة عوامل الضحية المسجّلة.


8. المعالجة

الإصلاح: الترقية. 26.7.2 للإصدارات المجتمعية، أو علامة backport من البائع المطابقة لاشتراكك. كل ما يلي هو حل مؤقت.

تخفيفات مؤقتة، الأفضل أولًا:

  1. عطّل نسيت كلمة المرور لكل عالم (Realm settings → Login). مؤكد الفعالية — التدفق يُرجع HTTP 400 ولا يمكن الدخول إليه. تحقق من كل عالم، بما في ذلك master.
  2. عطّل تنفيذ إعادة تعيين كلمة المرور في تدفق إعادة تعيين البيانات المرتبط. يعمل، لكن صفحة تسجيل الدخول لا تزال تعرض الرابط، لذا تجربة المستخدم سيئة. مفيد حيث تتجاهل سمة مخصصة مفتاح العالم.
  3. أضف مصادقًا إلزاميًا (OTP/WebAuthn) بعد خطوة البريد الإلكتروني في تدفق إعادة التعيين. هذا لا يغلق الالتفاف — يحد فقط من الاستيلاء الكامل إلى الحسابات التي سجّلت ذلك العامل فعليًا.

العوالم التي يكون تدفق إعادة التعيين المرتبط بها مخصصًا بالكامل ولا يستدعي أبدًا reset-credential-email غير قابلة للاستغلال عبر هذا المسار.


9. الاكتشاف

Keycloak لا يصدر حدث "تخطي رمز الإجراء"، لذا الاكتشاف استدلالي. شغّل lab/legit_reset.py بجانب الاستغلال لتوليد كلا الأثرين والمقارنة.

  • سجلات الوكيل العكسي / الدخول — أقوى إشارة. إعادة تعيين مشروعة تُظهر GET /login-actions/action-token?... (الضحية ينقر على البريد) قبل تغيير كلمة المرور. الالتفاف لا يحتوي على مثل هذا GET. بدلاً من ذلك يُظهر POST إلى login-actions/reset-credentials يحتوي جسمه على tryAnotherWay، متبوعًا بـ POST ثانٍ إلى نفس المسار بجسم فارغ، ثم نموذج كلمة المرور. POST بـ tryAnotherWay داخل تدفق إعادة التعيين ليس شيئًا تنتجه واجهة المستخدم القياسية في الاستخدام العادي.
  • أحداث الإدارة: SEND_RESET_PASSWORD متبوعًا بـ UPDATE_PASSWORD يشاركان نفس code_id خلال ثوانٍ قليلة — أقل من ثانية في المختبر. مستخدم لديه البريد مفتوحًا بالفعل يمكنه أن يبدو سريعًا أيضًا، لذا قم بتأكيد ذلك مع سجلات الوكيل.
  • الحسابات التي انقلب emailVerified فيها إلى true دون حدث VERIFY_EMAIL مقابل هي مؤشر داعم مفيد، ومؤشر لا يمكن للمهاجم تجنّب تركه.

غياب الأحداث لا يثبت شيئًا إذا كان تسجيل الأحداث أو الاحتفاظ بها معطّلًا. تحقق من نافذة الاحتفاظ قبل استنتاج أن نشرًا لم يُستهدف.


المؤلف

Snizi — github.com/Snizi — [email protected]

صدر بموجب رخصة MIT. المشكلات وطلبات السحب مرحّب بها — خاصةً دعم PKCE وخصائص السمات الواقعية الإضافية.

تنزيل الأداة
الخطقابل للاستغلالإصلاح المجتمع
القديم (Keycloak القائم على WildFly، ≤ 17)غير متأثر—
Quarkus 17 – 25.xغير متأثر—
26.026.0.0 – 26.0.17لا يوجد
26.126.1.0 – 26.1.5لا يوجد
26.226.2.0 – 26.2.16لا يوجد
26.326.3.0 – 26.3.5لا يوجد
26.426.4.0 – 26.4.1426.4.15 (وسم النقل الخلفي من المورّد)
26.526.5.0 – 26.5.7لا يوجد
26.626.6.0 – 26.6.526.6.6 (وسم النقل الخلفي من المورّد)
26.726.7.0 – 26.7.126.7.2
الخروجالحكمالمعنى
0قابل للاستغلالتم تقديم بوابة البريد الإلكتروني المتوقفة — الخلل نفسه
2مُصحَّحانحرف التدفق إلى تسجيل الدخول وبقي هناك (الإصلاح #51844 موجود)
2مُخفَّفإعادة تعيين بيانات الاعتماد غير قابلة للوصول — نسيت كلمة المرور معطّلة. ليس تصحيحًا.
3غير حاسماستجابة غير معترف بها — لا تقرأ هذا على أنه نجاح
الاختبارالهدفالنتيجة
--safe-check26.7.1VULNERABLE، خروج 0 — بوابة البريد الإلكتروني خُدِمت (execution ≠ choose-user)
--safe-check26.7.2PATCHED، خروج 2 — تحوّل إلى تسجيل الدخول وبقي
--safe-check، نسيت كلمة المرور معطّل26.7.2MITIGATED، خروج 2 — HTTP 400، التدفق غير قابل للوصول
استيلاء كامل26.7.1خروج 0 — كلمة المرور ضُبطت، رمز OIDC صدر، منح كلمة المرور يؤكد
استيلاء كامل26.7.2خروج 2 — حُظر عند الخطوة 5، الحساب لم يُمس
--check26.7.1وصل إلى UPDATE_PASSWORD؛ كلمة المرور تحقّق أنها لم تتغير بعد ذلك
--enum26.7.1victim و [email protected] VALID، does-not-exist INVALID
حالة بيانات الاعتماد بعد الاستيلاء26.7.1كلمة مرور جديدة → 200، كلمة مرور قديمة → 400
حالة بيانات الاعتماد بعد تشغيل محظور26.7.2كلمة مرور قديمة → 200، كلمة مرور المهاجم → 400
صندوق بريد الضحيةMailpitرسائل إعادة التعيين سُلّمت وغير مقروءة؛ رابط رمز الإجراء لا يُجلب أبدًا