engineering

الاختبار العشوائي الموجَّه بواسطة LLM: تغطية أكبر، أعطال غبية أقل

كان هناك ثلاثاء فقدنا فيه سبع ساعات نطارد SIGSEGV بدا جميلاً. كنا نختبر محلل JWT خاص بأحد العملاء، وقد بصق AFL++ تقريبًا على الفور عطلًا قابلًا للتكرار. كانت رائحته كاكتشاف حقيقي. عزلناه، وقلَّصناه بـ afl-tmin، وكتبنا الـ PoC، وأرسلناه إلى القناة الداخلية بثقة شخص يقوم بالفعل بصياغة CVE في عقله.

كان العطل عبارة عن strncpy على مخزن مؤقت فارغ لأن رأس JWT كان حرفيًا السلسلة { متبوعة ببايتات ثنائية. المحلل، قبل الوصول إلى أي شيء مثير للاهتمام، كان يشغل base64url_decode ويتعطل في مسار لن يتم الوصول إليه أبدًا في الإنتاج: الموازن الأمامي يتطلب Content-Type ونقطة واحدة على الأقل في الرمز. كانت ثغرة، نعم. وكانت عديمة الفائدة أيضًا.

في تلك الظهيرة قررنا أن هذا يكفي.

لماذا تختنق أدوات الاختبار العشوائي الرمادية بالقواعد النحوية

استراتيجيات الطفرة الخاصة بـ AFL (قلب البتات، الترقيع، الحساب) عمياء عن القواعد: معظم المدخلات المطفرة لا تتجاوز المحلل. أشار Wang et al. (Superion، ICSE 2019) بالفعل إلى أن "AFL يقضي قدرًا كبيرًا من الوقت في التعامل مع الصحة النحوية ويعثر فقط على أخطاء التحليل".

بالنسبة إلى JSON أو ASN.1 أو أي تنسيق ذي حالة، تتوقف التغطية الفعالة. تظهر العديد من الأعطال، لكن معظمها من المقدمة: المدققات، فاكات تشفير base64، فحوص الطول. الأشياء المثيرة للاهتمام (منطق التحقق من التوقيع، معالجة kid، هجمات من نوع algorithm confusion) تجلس خلف جدار لا تعبره قلبات البتات.

LLM كمحرك طفرة دلالي

يضع Xia et al. في Fuzz4All (ICSE 2024)[1] ذلك في صيغة رسمية: LLM "يتعلم ضمنيًا النحو والدلالات وقيود API الصالحة". إنه مولد احتمالي للمدخلات المعقولة.

أوضح Deng et al. مع TitanFuzz (ISSTA 2023)[2] تغطية أكبر بنسبة 30-50% على TensorFlow/PyTorch. نقل Meng et al. مع ChatAFL (NDSS 2024)[3] المبدأ إلى بروتوكولات الشبكة: انتقالات حالة أكثر بنسبة 47.6%، و9 CVEs جديدة. أضاف Yang et al. مع WhiteFox (OOPSLA 2024)[4] وكيلًا يقرأ الكود المصدري لاستنتاج متطلبات الإدخال.

النمط المشترك: LLM لا يقوم بالاختبار العشوائي، بل يقترح مرشحين. تظل أداة الاختبار العشوائي التقليدية المحرك الموجه بالتغطية.

بنيتنا: AFL++ مع إضافة دلالية

يغلف Gwaihir CLI AFL++ ويضيف شيئين: مطفِّر مخصص يفوض نسبة من الطفرات إلى مزود دلالي، و Beorn الذي يجلب قواعد نحوية معروفة، وعينات تاريخية، وCVEs سابقة لنفس التنسيق.

  1. يحلل Gwaihir الهدف. إذا تعرف Beorn على التنسيق، فإنه يحقن قواعد نحوية أولية.
  2. يبدأ AFL++ بمجموعة أولية مولدة بواسطة LLM من المواصفات.
  3. يرتبط المطفِّر المخصص بـ afl_custom_fuzz. كل N تكرارات يطلب من LLM طفرة دلالية.
  4. تُعاد حقن المدخلات التي تزيد التغطية إلى المجموعة.
// gwaihir_mutator.c — جزء توضيحي
size_t afl_custom_fuzz(void *data, uint8_t *buf, size_t buf_size,
                       uint8_t **out_buf, uint8_t *add_buf,
                       size_t add_buf_size, size_t max_size) {
  gwaihir_ctx_t *ctx = (gwaihir_ctx_t *)data;
  ctx->counter++;
  if (ctx->counter % ctx->llm_every == 0) {
    return gwaihir_llm_mutate(ctx, buf, buf_size, out_buf, max_size);
  }
  return afl_havoc_mutate(ctx, buf, buf_size, out_buf, max_size);
}

حالة محلل JWT

حملة 24 ساعة على نفس البرنامج الثنائي:

  • AFL++ عادي: تغطية 12.3%، 47 عطلًا فريدًا، 2 قابل للاستغلال.
  • AFL++ + رموز القاموس: تغطية 19.8%، 31 عطلًا، 3 قابلة للاستغلال.
  • AFL++ + Gwaihir/Beorn: تغطية 41.7%، 18 عطلًا فريدًا، 7 قابلة للاستغلال.

أعطال أقل إجمالاً، ولكنها أكثر كثافة. ثلاثة من السبعة انتهى بها الأمر إلى أن تكون ثغرات حقيقية في منطق kid وفي معالجة الخوارزميات غير المتماثلة.

Fuzz4All مقابل نهجنا

Fuzz4All أكثر طموحًا: LLM هو حلقة التوليد. تنوع هائل (98 ثغرة في GCC/Clang/Z3/OpenJDK) ولكن قيدين: بدون تغذية راجعة للتغطية يتحسس بشكل أعمى، وكل طفرة هي استدعاء للنموذج. نهجنا أكثر تواضعًا وأرخص: AFL++ يقوم بـ 98% بسرعة قلب البتات، و LLM يتدخل فقط لدفع التغطية إلى ما بعد المحلل.

المقايضات

تكلفة الرموز. 24 ساعة × استدعاء واحد/50 تنفيذًا × 5,000 تنفيذ/ث = ~8.6 مليون استدعاء. غير عملي بدون التخزين المؤقت العدواني والنماذج المحلية. نستخدم نموذجًا مُكمَّمًا على GPU محلي بنسبة 95% ونحتفظ بنموذج كبير للطفرات الإنقاذية.

الكمون. استدعاء واحد لـ LLM، حتى المحلي، يستغرق عشرات/مئات الميلي ثانية. AFL++ متزامن. طابور غير متزامن حيث يملأ LLM مجموعة من الطفرات المُولَّدة مسبقًا.

نقص التحديد. موجه غامض ← LLM يهلوس مدخلات مملة. موجه ضيق ← يكرر الحالات المعروفة. نكرر العمل على الموجهات تقريبًا بقدر ما نكرر العمل على الكود.

ما نأخذه معنا

الاختبار العشوائي الموجَّه بـ LLM ليس بديلاً عن AFL++. إنه مكمل يرفع سقف التغطية عندما يكون لدى الهدف تحليل معقد. لا يطارد مزيدًا من الأعطال؛ بل يطارد أعطالًا أفضل. بالنسبة للفريق، عنى ذلك تخصيص ساعات أقل لعمليات الفرز العديمة الفائدة ومزيد من الساعات للثغرات التي تهم العميل.

المراجع