
محرك تشويب (fuzzing) موزّع موجّه بالتغطية، متوافق مع أهداف libFuzzer؛ يتوسّع ليدعم آلاف المهام المتزامنة، ويستخدم sanitizers وتقطير corpus لاكتشاف الأخطاء.
لمَ لا؟ نحن نحاول حاليًا تخريب (fuzzing) أهداف كبيرة جدًا وبطيئة جدًا، وهي أهداف لا تتوسّع معها الأدوات مثل libFuzzer وAFL وما شابهها بالضرورة بشكل جيد. لأحد الأمثلة التي دفعتنا لهذا العمل، انظر SiliFuzz. أثناء العمل على Centipede، نخطط لتجربة مقاربات جديدة للتخريب التفاضلي على نطاق واسع، وهو ما لا تحاول محركات التخريب الحالية القيام به.
ميزات بارزة:
دعم جاهز للاستخدام لأهداف التخريب القائمة على libFuzzer. من أجل استخدام دالتك المفضلة LLVMFuzzerTestOneInput() تحتاج فقط إلى بناء هدفك باستخدام خيارات المترجم والرابط الخاصة بـ Centipede.
قيد التطوير. نحن نختبر Centipede ضمن فريق صغير على عدد قليل من الأهداف. ما لم تكن جزءًا من مشروع Centipede، أو ترغب في مساعدتنا، فغالبًا لا ترغب في مواصلة القراءة في الوقت الحالي.
قابلية التوسّع. الهدف هو القدرة على تشغيل أي عدد من المهام (jobs) بالتزامن، مع حمل اتصالات ضئيل جدًا. نختبر حاليًا مع 100 مهمة محلية ومع 10 آلاف مهمة على عنقود (cluster).
خارج العملية. يعمل الهدف في عملية منفصلة. أي انهيارات تحدث فيه لن تؤثر على أداة التخريب. يمكن أيضًا استخدام Centipede داخل العملية (in-process)، لكن هذا الوضع ليس الهدف الرئيسي. إذا كان هدفك صغيرًا وسريعًا فغالبًا لا تزال بحاجة إلى libFuzzer.
يتم تحقيق التكامل مع المُعقِّمات (sanitizers) عبر عمليات بناء منفصلة. إذا أردت أثناء التخريب العثور على أخطاء باستخدام ASAN أو MSAN أو TSAN، فستحتاج إلى توفير ملفات تنفيذية منفصلة لكل مُعقِّم، بالإضافة إلى ملف تنفيذي رئيسي واحد لـ Centipede نفسه. يجب ألا يستخدم الملف التنفيذي الرئيسي أيًا من المُعقِّمات.
لا يوجد جزء من الواجهة الداخلية مستقر. أي شيء قد يتغير في هذه المرحلة.
برنامج يُنتج تدفقًا لا نهائيًا من المدخلات لهدف ما وينسّق عملية التنفيذ.
ملف تنفيذي، أو مكتبة، أو واجهة برمجة تطبيقات (API)، أو بالأحرى أي شيء يمكنه استهلاك البايتات كمدخلات وإنتاج نوع من بيانات التغطية كمخرجات. هدف libFuzzer يمكن أن يكون هدفًا لـ Centipede. اقرأ المزيد هنا.
سلسلة من البايتات يمكن إدخالها إلى هدف. يمكن أن تكون المدخلات مجموعة عشوائية من البايتات، أو بعض البيانات المنظمة، مثل proto مُسلسَل (serialized proto).
رقم يمثل سلوكًا فريدًا للهدف. على سبيل المثال، قد تمثل الميزة 1234567 حقيقة أن الكتلة الأساسية رقم 987 في الهدف قد نُفِّذت 7 مرات. عند تنفيذ مدخلات مع الهدف، تجمع أداة التخريب الميزات التي لوحظت أثناء التنفيذ.
مجموعة من الميزات المرتبطة بمدخلات محددة واحدة.
بعض المعلومات حول سلوك الهدف عندما ينفّذ مدخلات معينة. عادةً ما يتم تمثيل التغطية كمجموعة ميزات أثارتها المدخلات في الهدف.
دالة تأخذ بايتات كمدخلات وتُخرج طفرة عشوائية صغيرة من المدخلات. انظر أيضًا: التخريب المراعي للبنية.
دالة تعرف كيفية إدخال مدخلات إلى هدف والحصول على تغطية في المقابل (أي تنفيذ).
محرك تخريب قابل للتخصيص يسمح للمستخدم باستبدال المُحوِّر والمُنفِّذ.
مكتبة تنفّذ واجهة المُنفِّذ التي يتوقعها مخرب Centipede. يعرف المشغّل كيفية تشغيل هدف مُجهَّز بأدوات sancov، وجمع التغطية الناتجة، وإعادتها إلى Centipede. يمكن ربط أهداف التخريب المحتملة لـ Centipede بهذه المكتبة لجعلها قابلة للتشغيل بواسطة Centipede.
مجموعة من المدخلات.
عملية اختيار مجموعة فرعية من مجموعة أكبر، بحيث تمتلك المجموعة الفرعية نفس ميزات التغطية التي تمتلكها المجموعة الأصلية.
ملف يمثل مجموعة فرعية من المجموعة (corpus)، وملف آخر يمثل مجموعات الميزات لتلك المجموعة الفرعية نفسها من المجموعة.
دمج الشارد B في الشارد A يعني: لكل مدخلات في الشارد B تمتلك ميزات مفقودة في الشارد A، أضف تلك المدخلات إلى A.
عملية تخريب واحدة. تكتب المهمة الواحدة إلى شارد واحد فقط، لكنها قد تقرأ شاردات متعددة.
دليل محلي أو بعيد يحتوي على بيانات تنتجها أو تستهلكها أداة تخريب.
git clone https://github.com/google/centipede.git
cd centipede
CENTIPEDE_SRC=`pwd`
BIN_DIR=$CENTIPEDE_SRC/bazel-bin
bazel build -c opt :all
ما ستحتاج إليه للخطوات التالية:
$BIN_DIR/centipede - الملف التنفيذي للمحرك (أداة التخريب).$BIN_DIR/libcentipede_runner.pic.a - المكتبة التي تحتاج إلى ربطها بهدف التخريب الخاص بك (المشغّل).$CENTIPEDE_SRC/clang-flags.txt - أعلام (flags) ترجمة clang الموصى بها للهدف.يمكنك إبقاء هذه الملفات في أماكنها أو نسخها إلى مكان آخر.
نقدّم مثالين لبناء الهدف: هدف صغير جدًا في ملف واحد، وlibpng. بمجرد بناء هدفك، انتقل إلى خطوة تشغيل هدف التخريب.
يستخدم هذا المثال واحدًا من أهداف التخريب البسيطة، المعروفة أيضًا باسم الألغاز (puzzles)، المضمّنة في مستودع Centipede.
ملاحظة: تستخدم الأوامر أدناه الأعلام من $CENTIPEDE_SRC/clang-flags.txt. يمكنك اختيار استخدام مجموعة أخرى من أعلام الأدوات (instrumentation flags): clang-flags.txt يوفر فقط خيارًا افتراضيًا بسيطًا.
FUZZ_TARGET=byte_cmp_4 # or any other source under $CENTIPEDE_SRC/puzzles
clang++ @$CENTIPEDE_SRC/clang-flags.txt -c $CENTIPEDE_SRC/puzzles/$FUZZ_TARGET.cc -o $BIN_DIR/$FUZZ_TARGET.o
تربط هذه الخطوة هدف التخريب الذي تم بناؤه للتو مع libcentipede_runner.pic.a والمكتبات الأخرى المطلوبة.
clang++ $BIN_DIR/$FUZZ_TARGET.o $BIN_DIR/libcentipede_runner.pic.a \
-ldl -lrt -lpthread -o $BIN_DIR/$FUZZ_TARGET
انتقل إلى خطوة التشغيل.
LIBPNG_BRANCH=v1.6.37 # You can experiment with other branches if you'd like
git clone --branch $LIBPNG_BRANCH --single-branch https://github.com/glennrp/libpng.git
cd libpng
CC=clang CFLAGS=@$CENTIPEDE_SRC/clang-flags.txt ./configure --disable-shared
make -j
FUZZ_TARGET=libpng_read_fuzzer
clang++ -include cstdlib \
./contrib/oss-fuzz/$FUZZ_TARGET.cc \
./.libs/libpng16.a \
$BIN_DIR/libcentipede_runner.pic.a \
-ldl -lrt -lpthread -lz \
-o $BIN_DIR/$FUZZ_TARGET
لن يعطي التشغيل المحلي النطاق الكامل، لكنه قد يكون مفيدًا أثناء مرحلة تطوير أداة التخريب. نوصي بنسخ كل من أداة التخريب والهدف إلى دليل محلي قبل التشغيل لتجنب إجهاد نظام ملفات الشبكة.
WD=$HOME/centipede_run
mkdir -p $WD
ملاحظة: قد تحتاج إلى إضافة llvm-symbolizer إلى $PATH الخاص بك لكي تعمل بعض وظائف Centipede. يمكن تثبيت المُرمِّز (symbolizer) كجزء من توزيعة LLVM:
sudo apt install llvm
which llvm-symbolizer # normally /usr/bin/llvm-symbolizer
rm -rf $WD/*
$BIN_DIR/centipede --binary=$BIN_DIR/$FUZZ_TARGET --workdir=$WD --num_runs=100
شاهد ما يوجد في دليل العمل
tree $WD
...
├── <fuzz target name>-d9d90139ee2ccc687f7c9d5821bcc04b8a847df5
│ └── features.0
└── corpus.0
تحذير: لا تتجاوز عدد أنوية جهازك بالنسبة لعلامة --j.
rm -rf $WD/*
$BIN_DIR/centipede --binary=$BIN_DIR/$FUZZ_TARGET --workdir=$WD --num_runs=100 --j=5
شاهد ما يوجد في دليل العمل:
tree $WD
...
├── <fuzz target name>-d9d90139ee2ccc687f7c9d5821bcc04b8a847df5
│ ├── features.0
│ ├── features.1
│ ├── features.2
│ ├── features.3
│ └── features.4
├── corpus.0
├── corpus.1
├── corpus.2
├── corpus.3
└── corpus.4
عادةً لا يغطي كل شارد في Centipede جميع الميزات التي تغطيها المجموعة بأكملها. من أجل تقطير المجموعة، ستحتاج عملية Centipede إلى قراءة جميع الشاردات. حاليًا، يعمل التقطير على النحو التالي:
--distill_shards=N. سيؤدي هذا إلى جعل أول N مهام تُنتج N ملفات مجموعات مقطّرة مستقلة (ملف واحد لكل مهمة). يجب أن تمتلك كل مجموعة مقطّرة نفس ميزات المجموعة الكاملة، لكن المدخلات قد تكون مختلفة جدًا بين هذه المجموعات المقطّرة.إذا كنت بحاجة أيضًا إلى تصدير المجموعة المقطّرة إلى دليل بنمط libFuzzer (دليل محلي بملف واحد لكل مدخلات)، أضف --corpus_dir=DIR.
ينشئ Centipede تقرير تغطية بسيطًا في شكل ملف نصي. يُنشئ الشارد 123 ملفًا باسم workdir/coverage-report-BINARY.000123.txt قبل بدء التخريب الفعلي، أي أن التقرير يعكس التغطية التي لاحظها الشارد 123 بعد تحميل المجموعة.
يعرض التقرير الدوال المغطاة بالكامل (تُلاحظ جميع حواف تدفق التحكم مرة واحدة على الأقل)، وغير المغطاة، أو المغطاة جزئيًا. بالنسبة للدوال المغطاة جزئيًا، يحتوي التقرير على معلومات رمزية لجميع الحواف المغطاة وغير المغطاة.
سيبدو التقرير مشابهًا لما يلي:
FULL: FUNCTION_A a.cc:1:0
NONE: FUNCTION_BB bb.cc:1:0
PARTIAL: FUNCTION_CCC ccc.cc:1:0
+ FUNCTION_CCC ccc.cc:1:0
- FUNCTION_CCC ccc.cc:2:0
- FUNCTION_CCC ccc.cc:3:0
يُحدَّد لاحقًا (TBD)