
اعثر على الثغرة التي لم تُكتب اختباراتك أبداً لاصطيادها. نموذج توضيحي لـ ReGrade يحاكي CVE-2023-5968: اصطد تسرب تجزئة كلمة المرور بمقارنة تطبيق بنفسه.
عرض عملي لاكتشاف ثغرات اليوم الصفري باستخدام ReGrade. ستوجّه مجموعة اختبارات عادية نحو ReGrade، وتسجّلها مقابل خدمة صغيرة، ثم تعيد تشغيلها مقابل نسخة ثانية من نفس الخدمة، وتستخدم Claude Code وأدوات MCP من ReGrade للكشف عن تسريب تجزئة كلمة المرور — وهي ثغرة لم تكتب أي اختبار لاكتشافها.
يحاكي هذا ثغرة حقيقية: CVE-2023-5968، حيث أعاد نقطة نهاية تحديث اسم المستخدم في منصة تعاون كامل كائن المستخدم بما في ذلك تجزئة bcrypt لكلمة المرور. تم نشرها في 2017 ونجت من 7 سنوات من الاختبارات والمراجعات والفحوصات. (تدوينة Curtail.)
المفاجأة: لا يوجد إصدار v2. أنت تقارن التطبيق بنفسه. تقوم كل نسخة جديدة بتجزئة
كلمات المرور باستخدام أملاح bcrypt مختلفة، لذا فإن قيم التجزئة المسربة تختلف بين النسختين
— وهذه العشوائية هي ما يكتشفه ReGrade. الثغرة كامنة في الكود الذي شحنته بالفعل؛ لا حاجة
لتغيير الإصدار لاكتشافها.
regrade من https://app.regrade.curtail.com/downloads، ثم عيّن REGRADE_API_KEY
(أو ~/.regrade/key).claude plugin marketplace add https://app.regrade.curtail.com/downloads/latest/marketplace.json
ثم claude plugin install regrade@regrade --scope user، واربطه مرة واحدة (/mcp,
مسجّل الدخول إلى نفس الحساب كمفتاحك).واجهة برمجة تطبيقات صغيرة لـ"دردشة فريق" (app/store.py) تحتوي على مستخدمين وقنوات. يقوم Docker Compose
بتشغيل نسختين متطابقتين — نفس الصورة، نفس الكود، لا علامة إصدار:
http://localhost:8001 — تسجل مقابل هذه.http://localhost:8002 — تعيد التشغيل مقابل هذه.تحتوي نقطة نهاية واحدة على عيب مزروع:
| نقطة النهاية | السلوك |
|---|---|
GET /users/<id> | مُطهّرة — لا تعيد كلمة المرور أبدًا. خط الأساس النظيف. |
يتم تجزئة كلمات المرور باستخدام bcrypt عند بدء التشغيل، لذا تحتفظ instance-a و instance-b بتجزئات مختلفة لنفس المستخدم.
git clone https://github.com/Curtail-Inc/hello-ReGrade-security
cd hello-ReGrade-security
docker compose up -d --build
traffic/test_api.py هي مجموعة اختبارات وظيفية عادية: تسجيل الدخول، قراءة مستخدم، إعادة تسمية مستخدم،
سرد القنوات. تؤكد سلوك CRUD ولا تقدم أي تأكيدات أمنية — لا تتحقق أبدًا مما إذا كان password
يتسرب. (لماذا تفعل؟ لم يكن أحد يعلم بوجود الثغرة.)
ابدأ وكيل المستشعر أمام instance-a:
regrade proxy --target http://localhost:8001 --port 19870
في نافذة طرفية أخرى، قم بتشغيل نفس مجموعة الاختبارات — فقط وجّه BASE_URL نحو الوكيل.
هذا هو التغيير الوحيد:
BASE_URL=http://localhost:19870 python -m pytest traffic/test_api.py
جميع الاختبارات تنجح، كما هي. أوقف الوكيل (Ctrl-C)؛ يتم رفع التسجيل ويطبع
Recording ID: <uuid> — دوّنه.
regrade replay --rec-id <RECORDING_ID> --target http://localhost:8002
نفس الكود على كلا الجانبين — الاختلافات الوحيدة هي القيم التي تولدها النسخة الجديدة.
افتح هذا المستودع في Claude Code واطلب منه أن يرشدك خلال إعادة التشغيل. بفضل
CLAUDE.md في هذا المستودع، سيقوم بما يلي:
summarize_deltas → العديد من الدلتات: رمز الدخول token، طوابع created_at للقنوات،
ومن بينها بهدوء — $.password.create_id_mapping لـ $.token للجلسة (معرّف ديناميكي، وليس ضوضاء للتجاهل)،create_filter_rule DROP لـ $.channels[*].created_at (طابع زمني لكل بدء تشغيل)،apply_profile_to_replay، ثم query_deltas(unlabeled_only=true) — كرر حتى يتبقى دلتا واحدة فقط.$.password — تجزئة bcrypt في نص استجابة.
تسريب على مستوى CVE، تم اكتشافه بدون معرفة مسبقة وبدون أي تأكيدات أمنية.رمز الجلسة وتجزئة كلمة المرور كلاهما يختلفان بين التشغيلتين — كلاهما سلاسل عالية العشوائية تتغير في كل مرة. أحدهما ضوضاء شرعية تقوم بتعيينها؛ والآخر هو اختراق. لا يمكنك التمييز بينهما بمجرد "لقد تغير" — يجب أن تنظر إلى ما تغير. قم بتصفية كل حقل عالي العشوائية بعيدًا كضوضاء وكنت ستخفي الثغرة.
هذا هو الدرس: ReGrade لا يتحقق من التوقعات، بل يقارن السلوك — لذا يمكنه اكتشاف الثغرات التي لم يفكر أحد في كتابة اختبار لها.
app/store.py هو ملف واحد. مسارات GET تستدعي sanitize()؛ مسار PATCH ينساها.
انظر بعد أن تكتشفها باستخدام ReGrade — المغزى هو أن ReGrade التقطها من الحركة وحدها،
بمقارنة التطبيق بنفسه.
Apache-2.0.
PATCH /users/<id> (إعادة التسمية) | العيب — تعيد كائن المستخدم الكامل بما في ذلك تجزئة password من bcrypt. استدعاء sanitize() مفقود واحد، تمامًا مثل CVE-2023-5968. |