
مختبر عشوائي قائم على التغطية لتطبيقات بايثون
pip3 install frelatage
مُختبر بايثون الطَفيلي الذي يستحقه العالم
التنصيب | كيف يعمل | المُميزات | استخدام Frelatage | الإعدادات
Frelatage هي مكتبة اختبار طفيلي (fuzzing) لبايثون تعتمد على التغطية، ويمكن استخدامها لاختبار كود بايثون. استُلهم تطوير Frelatage من مُختبرين آخرين، من بينهم AFL/AFL++، وAtheris وPythonFuzz. الهدف الرئيسي للمشروع هو الاستفادة من أفضل مميزات هؤلاء المُختبرين وجمعها معاً في أداة جديدة من أجل اختبار تطبيقات بايثون بكفاءة.
تنويه: هذا المشروع في مرحلة ألفا، وقد يتسبب في سلوكيات غير متوقعة. لا ينبغي استخدام Frelatage في بيئة إنتاج في الوقت الحالي.
pip3 install frelatage
مُوصى به للمطورين. يقوم تلقائياً باستنساخ الفرع الرئيسي من مستودع frelatage، والتنصيب من المصدر.
# استنساخ مستودع Frelatage تلقائياً وتثبيت Frelatage من المصدر
bash <(wget -q https://raw.githubusercontent.com/Rog3rSm1th/Frelatage/main/scripts/autoinstall.sh -O -)
الفكرة وراء تصميم Frelatage هي استخدام خوارزمية جينية لتوليد طفرات تغطي أكبر قدر ممكن من الكود. يمكن تلخيص آلية دورة الاختبار الطفيلي تقريباً في هذا المخطط:
graph TB
m1(طفرة 1) --> |إدخال| function(الدالة المُختبرة)
m2(طفرة 2) --> |إدخال| function(الدالة المُختبرة)
mplus(طفرة ...) --> |إدخال| function(الدالة المُختبرة)
mn(طفرة n) --> |إدخال| function(الدالة المُختبرة)
function --> generate_reports(إنشاء التقارير)
generate_reports --> rank_reports(ترتيب التقارير)
rank_reports --> select(اختيار أفضل n تقرير)
select --> |طفرة| nm1(طفرة 1) & nm2(طفرة 2) & nmplus(طفرة ...) & nmn(طفرة n)
subgraph طفرات الدورة
direction LR
m1
m2
mplus
mn
end
subgraph طفرات الدورة التالية
direction LR
nm1
nm2
nmplus
nmn
end
style function fill:#5388e8,stroke:white,stroke-width:4pxيتيح Frelatage اختبار دالة عن طريق تمرير ملف كإدخال.
import frelatage
import my_vulnerable_library
def MyFunctionFuzz(data):
my_vulnerable_library.parse(data)
input = frelatage.Input(value="initial_value")
f = frelatage.Fuzzer(MyFunctionFuzz, [[input]])
f.fuzz()
يمنحك Frelatage إمكانية اختبار وسائط إدخال من نوع ملف. لتهيئة قيمة هذه الملفات، يجب إنشاء ملفات في مجلد الإدخال (./in بشكل افتراضي).
إذا أردنا تهيئة قيمة ملف يُستخدم في الاختبار، يمكننا القيام بذلك كالتالي:
echo "initial value" > ./in/input.txt
ثم تشغيل المُختبر:
import frelatage
import my_vulnerable_library
def MyFunctionFuzz(data):
my_vulnerable_library.load_file(data)
input = frelatage.Input(file=True, value="input.txt")
f = frelatage.Fuzzer(MyFunctionFuzz, [[input]])
f.fuzz()
import frelatage
import my_vulnerable_library
input = frelatage.Input(file=True, value="input.txt")
@frelatage.instrument([[input]])
def MyFunctionFuzz_1(data):
my_vulnerable_library.load_file(data)
@frelatage.instrument([[input]])
def MyFunctionFuzz_1(data):
my_vulnerable_library.load_file_but_different(data)
# وهكذا دواليك
# سيختبر الطرق المزينة واحدة تلو الأخرى
frelatage.Fuzzer.fuzz_all()
إذا كنت بحاجة إلى تحميل عدة ملفات في مجموعة أساسية دفعة واحدة (مفيد إذا كنت تستخدم مجموعة أساسية كبيرة)، يمكنك استخدام الدالة المدمجة في Frelatage load_corpus. تُعيد هذه الدالة قائمة من الإدخالات.
load_corpus(directory: str, file_extensions: list) -> list[Input]
./، ./images["jpeg", "gif"]، ["pdf"]import frelatage
import my_vulnerable_library
def MyFunctionFuzz(data):
my_vulnerable_library.load_file(data)
my_vulnerable_library.load_file(data2)
# تحميل كل ملف في المجلد ./in
corpus_1 = frelatage.load_corpus(directory="./")
# تحميل كل ملف .gif/.jpeg في المجلد الفرعي ./in/images
corpus_2 = frelatage.load_corpus(directory="./images", file_extension=["gif", "jpeg"])
f = frelatage.Fuzzer(MyFunctionFuzz, [corpus_1, corpus_2])
f.fuzz()
يمكنك نسخ قاموس أو أكثر من هنا إلى المجلد المُخصص للقواميس (./dict بشكل افتراضي).
الاختبار الطفيلي التفاضلي هو تقنية اختبار برمجيات شائعة تحاول اكتشاف الأخطاء من خلال توفير نفس الإدخال لمكتبات/برامج متعددة وملاحظة الاختلافات في سلوكياتها. ستجد مثالاً هنا على استخدام الاختبار التفاضلي مع Frelatage باستخدام مكتبتي json وujson.
يمكنك العثور على المزيد من الأمثلة للمُختبرات والمجموعات الأساسية في دليل الأمثلة.
يتم حفظ كل تقرير عطل في مجلد الإخراج (./out بشكل افتراضي)، في مجلد يُسمى: id:<معرف العطل>,err:<نوع الخطأ>,err_pos:<موضع الخطأ>,err_file:<ملف الخطأ>.
يكون هيكل مجلد التقرير كالتالي:
├── out
│ ├── id:<معرف العطل>,err:<نوع الخطأ>,err_file:<ملف الخطأ>,err_pos:<موضع الخطأ>
│ ├── message
│ ├── input
│ ├── 0
│ ├── <ملف الإدخال 1>
│ ├── ...
│ ├── ...
يحتوي كل تقرير عطل على ملف message يحتوي على رسالة الخطأ.
يتم حفظ كل تقرير زيادة تغطية في مجلد التغطية (./cov بشكل افتراضي)، في مجلد يُسمى: coverage_<المعرف>، ويكون هيكل مجلد التقرير مشابهاً لهيكل مجلدات تقارير الأعطال.
يتم تسلسل الإدخالات الممررة إلى دالة باستخدام وحدة pickle قبل حفظها في ملف <report_folder>/input. لذلك من الضروري إلغاء التسلسل لتتمكن من قراءة محتويات الملف. يمكن القيام بهذه العملية باستخدام الأمر frelatage-report.
$ frelatage-report input
هناك طريقتان لإعداد Frelatage:
مثال على إعداد:
export FRELATAGE_DICTIONARY_ENABLE=1 &&
export FRELATAGE_SAVE_NEW_COVERAGE=1 &&
export FRELATAGE_TIMEOUT_DELAY=2 &&
export FRELATAGE_INPUT_FILE_TMP_DIR="/tmp/frelatage" &&
export FRELATAGE_INPUT_MAX_LEN=4096 &&
export FRELATAGE_MAX_THREADS=8 &&
export FRELATAGE_MAX_STAGES=1000000 &&
export FRELATAGE_MAX_CYCLES_WITHOUT_NEW_PATHS=5000 &&
export FRELATAGE_INPUT_DIR="./in" &&
export FRELATAGE_DICTIONARY_DIR="./dict" &&
python3 fuzzer.py
import frelatage
def myfunction(input1_string, input2_int):
pass
input1 = frelatage.Input(value="initial_value")
input2 = frelatage.Input(value=2)
f = frelatage.Fuzzer(
# الدالة التي تريد اختبارها
method=myfunction,
# المجموعة الأساسية
corpus=[[input1], [input2]],
# عدد الخيوط
threads_count=8,
# الاستثناءات التي سيتم أخذها في الاعتبار
exceptions_whitelist=(OSError),
# الاستثناءات التي لن تؤخذ في الاعتبار
exceptions_blacklist=(),
# المجلد الذي ستُخزن فيه تقارير الأخطاء
output_directory="./out",
# المجلد الذي ستُخزن فيه تقارير زيادة التغطية
coverage_directory="./cov",
# تفعيل أو تعطيل الوضع الصامت
silent=False,
# تفعيل أو تعطيل الاختبار اللانهائي
infinite_fuzz=False
)
f.fuzz()
يُرجى وضع في الاعتبار أنه، على غرار العديد من المهام كثيفة الاستخدام للحوسبة، قد يُسبب الاختبار الطفيلي ضغطاً على أجهزتك ونظام التشغيل. على وجه الخصوص:
سترتفع حرارة وحدة المعالجة المركزية (CPU) وستحتاج إلى تبريد مناسب. في معظم الحالات، إذا كان التبريد غير كافٍ أو توقف عن العمل بشكل صحيح، فسيتم تخفيض سرعات وحدة المعالجة المركزية تلقائياً. ومع ذلك، خاصة عند الاختبار على أجهزة أقل ملاءمة (أجهزة كمبيوتر محمولة، هواتف ذكية، إلخ)، فليس من المستحيل تماماً حدوث عطل.
قد تنتهي البرامج المستهدفة إلى استهلاك غيغابايت من الذاكرة بشكل غير منتظم أو ملء مساحة القرص بملفات عديمة الفائدة. يحاول Frelatage فرض حدود أساسية للذاكرة، لكنه لا يمكنه منع كل حادث محتمل. الخلاصة هي أنه لا ينبغي لك الاختبار على أنظمة لا يُعد احتمال فقدان البيانات فيها خطراً مقبولاً.
يتضمن الاختبار مليارات عمليات القراءة والكتابة على نظام الملفات. على الأنظمة الحديثة، سيتم تخزين هذا مؤقتاً بشكل كبير، مما يؤدي إلى إدخال/إخراج "فعلي" متواضع إلى حد ما - ولكن هناك العديد من العوامل التي قد تغير هذه المعادلة. تقع على عاتقك مسؤولية مراقبة المشاكل المحتملة؛ مع إدخال/إخراج ثقيل جداً، قد ينخفض عمر العديد من الأقراص الصلبة (HDD) وأقراص الحالة الصلبة (SSD).
طريقة جيدة لمراقبة إدخال/إخراج القرص على لينكس هي أمر 'iostat':
$ iostat -d 3 -x -k [...معرف القرص الاختياري...]
أنا Rog3rSm1th، عمري 21 سنة، وأنا شغوف بالكمبيوتر والأمن السيبراني من فرنسا. أحب تطوير الأدوات (OSINT، الاختبار الطفيلي...) ولعب CTFs/Wargames. لمعرفة المزيد عني وعن مشاريعي، فقط اضغط هنا.
➜ إذا كنت ترغب في توظيفي لأحد مشاريعك (برمجة، أمن سيبراني...)، فقط تواصل معي على [email protected] وسنقوم بتقييم احتياجاتك معاً.
لأي ملاحظة أو اقتراح أو تقرير عن خطأ، أو إذا وجدت خطأ باستخدام Frelatage، يمكنك التواصل معي على [email protected] أو على تويتر @Rog3rSm1th
| متغير البيئة | الوصف | القيم الممكنة | القيمة الافتراضية |
|---|
| FRELATAGE_DICTIONARY_ENABLE | تفعيل استخدام الطفرات بناءً على عناصر القاموس | 1 للتفعيل، 0 للإلغاء | 1 |
| FRELATAGE_SAVE_NEW_COVERAGE | حفظ إدخالات التغطية الجديدة لإعادة استخدامها لاحقاً | 1 للتفعيل، 0 للإلغاء | 1 |
| FRELATAGE_TIMEOUT_DELAY | التأخير بالثواني وبعده ستُرجع الدالة خطأ مهلة (TimeoutError) | 1 - لا نهائي | 2 |
| FRELATAGE_INPUT_FILE_TMP_DIR | المجلد المؤقت حيث تُخزن ملفات الإدخال | مسار مطلق لمجلد، مثلاً /tmp/custom_dir | /tmp/frelatage |
| FRELATAGE_INPUT_MAX_LEN | الحجم الأقصى لمتغير الإدخال بالبايت | 4 - لا نهائي | 4094 |
| FRELATAGE_MAX_THREADS | الحد الأقصى لعدد الخيوط المتزامنة | 8 - لا نهائي | 8 |
| FRELATAGE_MAX_STAGES | الحد الأقصى لعدد المراحل لدالة مُختبرة | 1 - لا نهائي | 1000000 |
| FRELATAGE_MAX_CYCLES_WITHOUT_NEW_PATHS | عدد الدورات دون العثور على مسارات جديدة وبعدها ننتقل إلى المرحلة التالية | 10 - لا نهائي | 5000 |
| FRELATAGE_INPUT_DIR | المجلد الذي يحتوي على ملفات الإدخال الأولية. يجب أن يكون مساراً نسبياً (بالنسبة لمسار ملف الاختبار) | مسار نسبي لمجلد، مثلاً ./in | ./in |
| FRELATAGE_DICTIONARY_DIR | المجلد الافتراضي للقواميس. يجب أن يكون مساراً نسبياً (بالنسبة لمسار ملف الاختبار) | مسار نسبي لمجلد، مثلاً ./dict | ./dict |
| FRELATAGE_DEBUG_MODE | تفعيل وضع التصحيح (عرض الخطأ عند تعطل Frelatage) | 1 للتفعيل، 0 للإلغاء | 1 |