التعلم المعزز لتوليد الثغرات
قبل بضعة أشهر، خلال نهائي CTF، علق فريقنا لمدة ثلاث ساعات على ثنائي pwn. Stack canary، NX، PIE جزئي، وفيض من 72 بايت بالضبط في دالة غير واضحة. جربنا كل شيء. لا شيء. في الساعة 03:14 صباحًا، أطلق أحدنا تقريبًا على سبيل المزاح برنامجًا نصيًا من خمسة أسطر يقوم بتعديل المدخلات باستخدام bitflips على غرار AFL وإعادة إرسالها دون تفكير. بعد عشرين دقيقة، فاز البرنامج النصي بالـ flag بحظ إحصائي محض.
تلك الليلة عدنا إلى المنزل بسؤال يدور في رؤوسنا: ماذا لو كان لدينا، بدلاً من عملية غبية، عملية تتعلم من كل محاولة فاشلة؟
لماذا يتناسب RL مع اختطاف تدفق التحكم
مساحة البحث لثغرة ما قاسية لكنها غير منتظمة. تنتج معظم المدخلات نفس السلوك، ومنطقة ضيقة فقط تقود إلى حالة الآلة التي تريدها: سجل التعليمات يشير إلى ذاكرة تتحكم بها. ما يميز ذلك الفضاء الفرعي هو تدرج المعلومات: مدخل يقترب من إفساد RIP يظهر آثارًا جانبية يمكن ملاحظتها — سجلات مكتوبة جزئيًا، قيم stack تطابق بايتات من المدخلات، قفزات إلى عناوين غير صالحة لكنها قريبة من بايتات متحكم بها.
هذا بالضبط ما يمكن لدوال المكافأة المصممة جيدًا أن تلتقطه.
أحدث ما توصل إليه الفن
نشر Avgerinos و Brumley في 2011 أول نظام شامل مع AEG (NDSS 2011) باستخدام التنفيذ الرمزي. وسّع Mayhem (Cha et al., S&P 2012) التحليل. كلاهما شبه حتمي لكنهما يدفعان الثمن الكلاسيكي للتنفيذ الرمزي: انفجار المسارات وحاسبات SMT المختنقة.
أضفى Böttinger الطابع الرسمي على fuzzing كـ MDP باستخدام Deep Reinforcement Fuzzing (2018) باستخدام DQN. تشير PwnGPT (Shao et al., ACL 2025) إلى رفع معدل توليد الثغرات من 26.3% إلى 57.9% مع o1-preview. وقد نضج PPO (Schulman et al., 2017) + RLHF (Ouyang et al., 2022) بما يكفي لجعل تدريب وكيل على بيئة ثنائية محادثة جدية.
إعدادنا
بيئة على نمط Gym فوق ptrace ومركبة جانبية eBPF. الملاحظة هي tensor كثيف يحتوي على:
- حالة السجلات العامة الـ 16 (x86_64)، مُعيّرة.
- نافذة stack حول RSP (256 بايت) كبايتات خام.
- أعلام التحكم:
NX،canary_present،RIP_corrupted،register_controlled[]. - تجزئة آخر أثر تغطية مأخوذ من eBPF (uprobe على كل كتلة أساسية).
مساحة الإجراء منفصلة على بايتات المدخلات. تجمع المكافأة بين التغطية الجديدة (إيجابية، متناقصة)، والاستدلال حول قابلية الاستغلال وعقوبة الطول.
import gym
from gwaihir.tools.rl_env import BinaryPwnEnv
class CTFPwnEnv(BinaryPwnEnv):
def step(self, action):
mutated = self.apply_action(self.current_input, action)
trace = self.run_with_ebpf(mutated)
new_bbs = len(trace.basic_blocks - self.coverage_seen)
cov_reward = np.log1p(new_bbs) * 0.3
self.coverage_seen |= trace.basic_blocks
expl = 0.0
if trace.rip_controlled_bytes > 0:
expl += 5.0 + 0.1 * trace.rip_controlled_bytes
if trace.canary_leaked: expl += 2.5
if trace.tainted_syscall_args: expl += 1.5
reward = cov_reward + expl - 0.001 * len(mutated)
done = trace.rip_fully_controlled or self.steps > 4096
return self.observe(trace), reward, done, {"trace": trace}
PPO مقابل DQN
بدأنا بـ DQN. بمجرد أن تنمو مساحة الإجراء، أصبح DQN هشًا: كانت شبكة الهدف تتباعد في كل مرة تطلق فيها مكافأة قابلية الاستغلال قيمة ضخمة ومعزولة. PPO، بتقطيعه لنسبة السياسة، يمتص تلك القمم دون أن ينهار. أعطانا PPO مع GAE (λ=0.95)، معامل إنتروبيا 0.01 ودفعات 4096 تدريبات مستقرة على مدار 24 ساعة على RTX 4090.
تفصيل نادرًا ما يُروى: لا يُنفق معظم الوقت على التدرجات، بل يُنفق على تشغيل الثنائي. تتطلب كل خطوة fork، ptrace، جمع eBPF، parse. وازينا 32 بيئة لكل GPU باستخدام SubprocVecEnv ومع ذلك كان عنق الزجاجة هو I/O.
النتائج على 10 ثنائيات CTF
الدفعة: اثنان من babys، ثلاثة تتطلب تسريب canary، اثنان مع format string، اثنان مع سلسلة ROP الحد الأدنى وواحد مع heap (UAF). حلّ الوكيل، المدرّب من الصفر لكل ثنائي، 7 من 10. من بين الثلاثة التي فشل فيها: ثنائي heap (متوقع)، واحد من ROP يتطلب gadget غير موجود، و format string حيث علقت السياسة في حد أدنى محلي تسرب فيه العناوين إلى الأبد.
بالمقارنة مع angr الخالص: فاز RL بـ 5، تعادل في 2، خسر في 3 (حلّ angr heap و format-write أسرع). إنها ليست أداة متفوقة، إنها مكمّلة.
القيود القاسية
- سياسة واحدة لكل ثنائي. نحن لا ننقل المعرفة بين التحديات.
- أوقات تدريب بالساعات لتحديات يحلها إنسان متمرس في دقائق.
- تشكيل مكافآت هش. المخصصات المخصصة أو إجراءات التخفيف غير القياسية تكسر الاستدلالات.
- لا يزال التنفيذ الرمزي يفوز عندما يكون المسار قابلاً للاشتقاق بشكل حتمي.
المقايضة الحقيقية: تغطية واسعة رخيصة (RL) مقابل استدلال عميق مكلف (رمزي). نشغلها بالتوازي داخل Gwaihir.
ما القادم
ثلاث جبهات: (أ) التدريب المسبق على exploit-db وكتابات CTF، (ب) تعلم المكافآت على نمط RLHF، (ج) دمج الوكيل كأداة فرعية لـ LLM منسق.
في الوقت الحالي نواصل التدريب. وفي المرة القادمة التي يعلق فيها فريق ثلاث ساعات في الساعة الثالثة صباحًا، نريد أن يكون الوكيل هو من يجد الـ flag — لا الحظ.
المراجع
- Avgerinos, T. et al. (2011). AEG: Automatic Exploit Generation. NDSS.
- Cha, S.K. et al. (2012). Unleashing Mayhem. IEEE S&P.
- Böttinger, K. et al. (2018). Deep Reinforcement Fuzzing. IEEE SPW. arXiv:1801.04589.
- Schulman, J. et al. (2017). PPO. arXiv:1707.06347.
- Ouyang, L. et al. (2022). InstructGPT/RLHF. NeurIPS.
- Shao, Y. et al. (2025). PwnGPT. ACL.