چکیده – فازینگ هدایت شده (Directed Fuzzing)، بر آزمایش خودکار بخشهای خاصی از کد تمرکز دارد و با بهرهگیری از اطلاعات اضافی مانند رهگیری پشته (Stack Trace) مربوط به باگ، بهصورت کامل یا جزئی، وصلهها (Patches) یا عملیات پرخطر این کار را انجام میدهد. از کاربردهای اصلی آن میتوان به بازتولید باگ (Bug Reproduction)، آزمون وصله (Patch Testing) و راستیآزمایی گزارشهای تحلیل ایستا (Static Analysis Report Verification) اشاره کرد. اگرچه فازینگ هدایت شده در سالهای اخیر توجه زیادی را به خود جلب کرده است، اما آسیبپذیریهایی که شناسایی آنها دشوار است، مانند استفاده مجدد پس از آزادسازی (Use-After-Free یا UAF)، همچنان بهخوبی مورد رسیدگی قرار نگرفتهاند؛ بهویژه در سطح باینری (Binary Level).
ما UAFUZZ را ارائه میکنیم؛ نخستین فازِر هدایت شده جعبهخاکستری (Directed Greybox Fuzzer) در سطح باینری که به طور اختصاصی برای باگهای UAF طراحی شده است. این تکنیک از یک موتور فازینگ متناسب با ویژگیهای UAF، ابزارگذاری سبک کد (Lightweight Code Instrumentation) و یک مرحله کارآمد تریاژ یا دستهبندی و ارزیابی باگ (Bug Triage) بهره میبرد. ارزیابی تجربی بازآفرینی باگ روی نمونههای واقعی نشان میدهد که UAFUZZ از نظر نرخ تشخیص خطا (Fault Detection Rate)، زمان تا آشکارسازی (Time to Exposure) و تریاژ باگ، عملکردی بهطور قابلتوجه بهتر از فازرهای هدایت شده پیشرفته موجود دارد. همچنین اثربخشی UAFUZZ در آزمون وصله به اثبات رسیده است؛ بهگونهای که این ابزار موفق به کشف ۳۰ باگ جدید، شامل ۷ مورد CVEدر برنامههایی مانند Perl، GPAC و GNU Patch شده است. در نهایت، یک معیار ارزیابی بزرگ برای فازینگ (Fuzzing Benchmark) و اختصاصی برای UAF در اختیار جامعه پژوهشی قرار میدهیم که بر پایه کدهای واقعی و باگهای واقعی ساخته شده است.
۱. مقدمه (Introduction)
زمینه (Context). یافتن باگها در مراحل اولیه، در فرایند مدیریت آسیبپذیری (Vulnerability Management) اهمیت بسیار زیادی دارد. رشد اخیر فازینگ (Fuzzing) [50, 53]، در هر دو حوزه دانشگاه و صنعت، مانند Springfield مایکروسافت [52] و OSS-FUZZ گوگل [14]، توانایی این روش را در یافتن انواع مختلف باگها در برنامههای کاربردی دنیای واقعی نشان میدهد. فازینگ جعبهخاکستری مبتنی بر پوشش (Coverage-based Greybox Fuzzing یا CGF)، مانند AFL [1] و LIBFUZZER [13]، از اطلاعات مربوط به پوشش کد (Code Coverage) برای هدایت تولید ورودی به سمت بخشهای جدیدی از برنامه تحت آزمون (Program Under Test یا PUT) استفاده میکند. هدف این است که تا حد امکان حالتهای برنامه بیشتری بررسی شوند، به این امید که در نهایت یک کرش (Crash) ایجاد گردد.
در مقابل، فازینگ جعبهخاکستری هدایت شده (Directed Greybox Fuzzing یاDGF) [25, 28] با هدف انجام آزمون فشار (Stress Testing) روی مکانهای هدفِ از پیش انتخاب شده و بالقوه آسیبپذیر طراحی شده است و در زمینههای مختلف امنیتی کاربرد دارد:
- بازتولید باگ(Bug Reproduction) ، [25, 28, 42, 61]
- آزمون وصله (Patch Testing)، [25, 51, 59]
- راستیآزمایی گزارش تحلیل ایستا [31, 49] (Static Analysis Report Verification)
مکانهای هدف بسته به نوع کاربرد میتوانند از رهگیری پشته باگ (Bug Stack Traces)، وصلهها (Patches) یا گزارشهای تحلیل ایستا (Static Analysis Reports) به دست آیند.
ما عمدتاً بر بازتولید باگ (Bug Reproduction) تمرکز داریم که رایجترین کاربرد عملی DGF است [25، 28، 42، 49، 74]. در این فرایند، با استفاده از اطلاعات موجود در گزارش باگ، ورودیهای اثبات مفهوم (Proof-of-Concept یا PoC) برای آسیبپذیریهای افشاشده تولید میشوند. این فرایند بسیار ضروری است، چرا که تنها ۵۴٫۹٪ از گزارشهای معمول باگ را میتوان بازآفرینی کرد؛ دلیل این امر، کمبود اطلاعات و نقض حریم خصوصی کاربران است [54].
حتی در صورتی که یک PoC در گزارش باگ ارائه شده باشد، توسعهدهندگان ممکن است همچنان لازم باشد تمام حالتهای مرزی (Corner Cases) باگ را در نظر بگیرند تا از بروز باگهای بازگشتی (Regression Bugs) یا وصلههای ناقص (Incomplete Fixes) جلوگیری کنند.
در چنین شرایطی، ارائه ورودیهای بیشتری که بتوانند باگ را فعال کنند، برای تسهیل و تسریع فرایند رفع باگ اهمیت پیدا میکند. ردپاهای پشته باگ (Bug Stack Traces)، یعنی توالی فراخوانی توابع در زمان فعال شدن یک باگ، بهطور گسترده برای هدایت فازرهای هدایتشده استفاده میشوند [25، 28، 42، 49]. اجرای یک برنامه با یک ورودی PoC، تحت ابزارهای پروفایلسازی (Profiling) مانند AddressSanitizer (ASan) [65] یا VALGRIND [55]، چنین رهگیری پشتهای از باگ را تولید میکند.
مسئله. با وجود پیشرفتهای چشمگیر در چند سال گذشته [5، 21، 23، 25، 28، 29، 39، 46، 47، 60، 62، 67، 73]، فازرهای جعبهخاکستری فعلی، چه هدایتشده و چه غیرهدایتشده، همچنان در یافتن آسیبپذیریهای پیچیدهای مانند استفاده پس از آزادسازی (Use-After-Free یا UAF)، باگهای عدم تداخل (Non-Interference) یا ناپایدار (Flaky Bugs) با مشکل مواجه هستند [24]؛ زیرا برای فعالسازی این باگها، مسیرهای اجراییِ منجر به فعالسازی باگ باید ویژگیهای بسیار خاصی را برآورده کنند.
برای مثال، OSS-FUZZ ، [14, 15] یا فازرهای جعبهخاکستری جدیدتر [25، 62، 73] تنها تعداد کمی آسیبپذیری UAF را پیدا کردهاند. در واقع، RODE0DAY [16]، که یک رقابت مستمر برای یافتن باگ است، تشخیص داده است که فازرها در آینده باید یافتن کلاسهای جدیدی از باگها، مانند UAFرا نیز هدف قرار دهند [37]؛ این امر به معنای فاصله گرفتن بیشتر از مجموعهدادههای باگ LAVA [36] است که بهطور گسترده مورد استفاده قرار میگیرند، اما تنها شامل سرریزهای بافری (Buffer Overflows) میباشند.
ما بر باگهای استفاده پس از آزادسازی (Use-After-Free یا UAF) تمرکز میکنیم. این باگها زمانی رخ میدهند که یک عنصر هیپ (Heap) پس از آزادسازی، مورد استفاده قرار گیرد. تعداد باگهای UAF در پایگاه داده ملی آسیبپذیریها (National Vulnerability Database یا NVD) [20] افزایش یافته است. در حال حاضر، UAFها به دلیل کمبود تکنیکهای مقابلهای (Mitigation Techniques) در مقایسه با سایر انواع باگها، بهعنوان یکی از بحرانیترین آسیبپذیریهای قابل بهرهبرداری شناخته میشوند و میتوانند پیامدهای جدیای مانند خرابی دادهها (Data Corruption)، نشت اطلاعات (Information Leaks) و حملات منع سرویس (Denial-of-Service Attacks) به دنبال داشته باشند.
هدف و چالشها (Goal and challenges). ما بر مسئله طراحی یک روش فازینگ هدایتشده کارآمد و متناسب با UAF تمرکز داریم. این تکنیک همچنین باید بتواند در سطح باینری کار کند؛ یعنی بدون نیاز به ابزارگذاری در سطح کد منبع (Source-Level Instrumentation)، زیرا کد منبع برنامههای حیاتی از نظر امنیتی همیشه در دسترس نیست یا ممکن است تا حدی به کتابخانههای شخص ثالث (Third-Party Libraries) وابسته باشد. با این حال، فازرهایی که شناسایی باگهای UAF را هدف قرار میدهند، با چالشهای زیر مواجه هستند:
چالش ۱. پیچیدگی (Complexity) – فعالسازی باگهای UAF مستلزم تولید ورودیهایی است که دنبالهای از سه رویداد تخصیص (Alloc)، آزادسازی (Free) و استفاده (Use) را روی یک مکان حافظه یکسان ایجاد کنند؛ این دنباله میتواند چندین تابع از برنامه تحت آزمون (PUT) را دربر بگیرد، در حالی که سرریزهای بافری (Buffer Overflows) تنها به یک دسترسی خارج از محدوده حافظه (Out-of-Bound Memory Access) نیاز دارند. برآورده ساختن همزمان این محدودیتهای زمانی (Temporal) و مکانی (Spatial) در عمل بسیار دشوار است.
چالش ۲. سکوت (Silence) – باگهای UAF اغلب هیچ اثر قابل مشاهدهای، مانند خطای بخشبندی (segmentation fault)، ایجاد نمیکنند. در چنین شرایطی، فازرهایی که صرفاً رفتارهای منجر به کرش را مشاهده میکنند، متوجه نمیشوند که یک مورد آزمون (Testcase) چنین باگ حافظهای را فعال کرده است. متأسفانه، ابزارهای محبوب پروفایلسازی (Profiling) مانند ASan یا VALGRIND نیز به دلیل سربار اجرایی (Runtime Overhead) بالای خود نمیتوانند در یک بستر فازینگ مورد استفاده قرار گیرند.
جدول ۱: خلاصهای از تکنیکهای موجود فازینگ جعبهخاکستری (Greybox Fuzzing)
در واقع، فازرهای هدایتشده پیشرفته موجود، یعنی AFLGO [25] و HAWKEYE [28]، قادر به برطرف کردن این چالشها نیستند. نخست، این فازرها بیش از حد کلی و عمومی میباشند و بنابراین با ویژگیهای خاص UAF، مانند بُعد زمانی (Temporality)، سازگاری ندارند؛ معیارهای هدایت آنها هیچ مفهومی از ترتیب رخدادها (Sequenceness) را در نظر نمیگیرند. دوم، این فازرها نسبت به باگهای UAF کاملاً نابینا هستند و برای بررسی آنها، لازم است تمام بذرهای (Seed) تولیدشده که تعدادشان نیز زیاد است، برای انجام یک بررسی اضافی و پرهزینه به یک ابزار پروفایلسازی ارسال شوند. در نهایت، پیادهسازیهای فعلی فازرهای فازینگ جعبهخاکستری هدایتشده مبتنی بر کد منبع (Source-Based Directed Greybox Fuzzers یا DGF) معمولاً از یک مرحله ابزارگذاری پرهزینه رنج میبرند [3]؛ برای مثال، AFLGO برای کامپایل و ابزارگذاری cxxfilt از مجموعه Binutils، نزدیک به ۲ ساعت زمان صرف کرده است.
پیشنهاد. ما UAFUZZ را ارائه میکنیم؛ نخستین فازر جعبهخاکستری هدایت شده در سطح باینری (Binary-Level Directed Greybox Fuzzer) که بهطور اختصاصی برای باگهای UAF طراحی شده است. یک مقایسه سریع میان UAFUZZ و فازرهای جعبهخاکستری موجود از نظر عملکرد در برابر UAF در جدول ۱ ارائه شده است. اگرچه ما عمدتاً از طرح کلی فازینگ هدایت شده پیروی میکنیم، چندین مؤلفه کلیدی آن را با دقت و متناسب با ویژگیهای UAF تنظیم کردهایم:
- معیار فاصله (Distance Metric)، زنجیرههای فراخوانی کوتاهتر را که به توابع هدف منتهی میشوند ترجیح میدهد؛ توابعی که احتمال بیشتری دارد هر دو تابع تخصیص (Allocation) و آزادسازی (Free) را در بر داشته باشند. این در حالی است که فازرهای هدایت شده پیشرفته موجود (State-of-the-Art یا SOTA) به یک فاصله عمومی مبتنی بر گراف جریان کنترل (Control-Flow Graph یا CFG) متکی هستند.
- انتخاب بذر (Seed) اکنون بر اساس یک معیار شباهت به هدف (Target Similarity Metric) مبتنی بر آگاهی از ترتیب رخدادها (Sequenceness-Aware) انجام میشود؛ در حالی که فازرهای هدایتشده SOTA، در بهترین حالت، به پوشش هدف (Target Coverage) متکی هستند.
- زمانبندی توان (Power Schedule) ما از این معیارهای جدید، بهعلاوه معیار دیگری با نام یالهای برش (Cut-Edges)، بهره میبرد. این معیار مسیرهای پیشوندی (Prefix Paths) را ترجیح میدهد که احتمال بیشتری دارد به کل هدف برسند.
در نهایت، مرحله تریاژ باگ (Bug Triaging) با استفاده مجدد از معیارهای قبلی ما، بذرها (Seed) را از پیش به عنوان باگهای محتمل یا غیرباگ شناسایی میکند و در نتیجه، تعداد بسیار زیادی از پرسوجوها (queries) به ابزار پروفایلسازی برای تأیید را حذف میکند؛ این ابزار در پیادهسازی ما، VALGRIND میباشد.
مشارکتها. مشارکتهای ما به شرح زیر است:
- ما نخستین تکنیک فازینگ جعبهخاکستری هدایت شده (Directed Greybox Fuzzing) را طراحی میکنیم که بهطور اختصاصی برای باگهای استفاده پس از آزادسازی (Use-After-Free یا UAF) متناسبسازی شده است (بخش ۴). ما به طور ویژه، سه مؤلفه اصلی فازینگ هدایت شده، یعنی ابتکار انتخاب (Selection Heuristic)، زمانبندی توان (Power Schedule) و معیارهای ورودی (Input Metrics)را بهصورت نظاممند بازبینی کرده و آنها را برای UAF تخصصیسازی میکنیم. نشان داده میشود که این بهبودها هم سودمند و هم مکمل یکدیگر هستند.
- ما یک زنجیرهابزار (Toolchain) [19] را بر پایه فازر جعبهخاکستری پیشرفته AFL [1] و پلتفرم تحلیل باینری BINSEC [4] توسعه میدهیم که UAFUZZ نام دارد. این ابزار، روش فوق را برای فازینگ هدایت شده UAF روی کدهای باینری پیادهسازی میکند (بخش ۵) و سربار (Overhead) اندکی دارد.
- ما بزرگترین معیار ارزیابی فازینگ (Fuzzing Benchmark) اختصاصیافته به آسیب پذیریهای UAF را ایجاد و بهصورت عمومی منتشر میکنیم [18]. این معیار شامل ۳۰ باگ واقعی از ۱۷ پروژه پرکاربرد است؛ از جمله تعداد معدودی از باگهای UAF پیشین که توسط فازرهای هدایت شده کشف شدهاند. هدف از این کار، تسهیل ارزیابیهای آینده در زمینه فازینگ UAF میباشد.
- ما تکنیک و ابزار خود را در سناریوی بازتولید باگ (Bug Reproduction) ارزیابی میکنیم (بخش ۶) و نشان میدهیم که UAFUZZ بسیار مؤثر است و بهمراتب عملکرد بهتری نسبت به رقبای پیشرفته موجود دارد. UAFUZZ به طور میانگین ۲ برابر سریعتر باگها را فعال میکند (در بهترین حالت تا ۴۳ برابر سریعتر است)، تعداد اجراهای آن بهطور میانگین بیش از ۳۴٪ است (در بهترین حالت تا بیش از ۳۰۰٪ میباشد) و فرایند تریاژ باگ را بهطور میانگین ۱۷ برابر سریعتر انجام میدهد (در بهترین حالت تا ۱۳۰ برابر سریعتر)، ضمن اینکه ۹۹٪ از بررسیهای موردنیاز برای تأیید باگها حذف میشود.
- در نهایت، اثربخشی UAFUZZ در آزمون وصله (Patch Testing) نیز اثبات شده میباشد (بخش ۶.۷) و این ابزار موفق به کشف ۳۰ باگ ناشناخته شده است که شامل ۱۱ باگ UAF و ۷ مورد CVE در پروژههایی مانند Perl، GPAC، MuPDF و GNU Patch (از جمله ۴ وصله دارای باگ) میشود. تاکنون، ۱۷ مورد از این باگها برطرف شدهاند.
UAFUZZ نخستین رویکرد فازینگ جعبهخاکستری هدایت شده (Directed Greybox Fuzzing) است که بهطور اختصاصی برای شناسایی آسیبپذیریهای استفاده پس از آزادسازی (Use-After-Free یا UAF) در سطح باینری طراحی شده و تنها با در اختیار داشتن رهگیری پشته باگ (Bug Stack Traces) کار میکند. UAFUZZ در بازآفرینی باگهای این دسته، عملکرد بهتری نسبت به فازرهای هدایت شده موجود دارد و نتایج امیدوارکنندهای نیز در زمینه آزمون وصله (Patch Testing) به دست آمده است. ما معتقدیم رویکرد ما میتواند در زمینههای مرتبط دیگری نیز کاربرد داشته باشد؛ برای مثال، در گزارشهای ناقص باگ حاصل از تحلیل ایستا (Static Analysis) یا در مواجهه با دستههای دیگری از آسیبپذیریها.
۲. پیشزمینه (Background)
ابتدا برخی از مفاهیمی را که در سراسر مقاله به کار میروند، روشن میکنیم.
۲.۱ استفاده پس از آزادسازی (Use-After-Free)
اجرا (Execution). یک اجرا، توالی کامل حالتهایی است که برنامه در پاسخ به یک ورودی طی میکند. هنگامی که یک ردپای اجرا (Execution Trace) با یک خطای قابل مشاهده به پایان برسد، آن اجرا منجر به کرش (Crash) میشود. هدف استاندارد فازرها، یافتن ورودیهایی است که به کرش منجر میشوند، زیرا کرشها نخستین گام در مسیر رسیدن به آسیبپذیریهای قابل بهرهبرداری (Exploitable Vulnerabilities) هستند.
باگهایUAF. باگهای استفاده پس از آزادسازی (Use-After-Free یا UAF) زمانی رخ میدهند که برنامه یک اشارهگر (Pointer) به یک شیء تخصیصیافته در هیپ (Heap-Allocated Object) را مورد ارجاعزدایی (Dereference) قرار دهد، در حالی که آن شیء دیگر معتبر نیست؛ یعنی اشارهگر در حالت سرگردان (Dangling Pointer) قرار دارد. توجه داشته باشید که آزادسازی مجدد (Double-Free یا DF) یک حالت خاص از این نوع باگ محسوب میشود.
شرایط فعالسازی UAF. به منظور فعالسازی یک باگ UAF، میبایست ورودی پیدا شود که اجرای آن، سه رویداد UAF را بهترتیب پوشش دهد: یک تخصیص (Alloc)، یک آزادسازی (Free) و یک استفاده (Use) که معمولاً بهصورت ارجاعزدایی (Dereference) انجام میشود و هر سه رویداد به یک شیء حافظه یکسان مربوط میباشند؛ همانطور که در فهرست ۱ نشان داده شده است.
لیست ۱- قطعهکدی که یک باگ استفاده پس از آزادسازی (UAF) را نشان میدهد:
1 char * buf = (char *) malloc ( BUF_SIZE );
2 free ( buf ); // pointer buf becomes dangling
3 ...
4 strncpy (buf , argv [1] , BUF_SIZE -1) ; // Use-After-Free
علاوه بر این، این استفاده نهایی معمولاً باعث نمیشود که اجرا بلافاصله با کرش (crash) مواجه شود؛ زیرا نقض حافظه (Memory Violation) تنها زمانی باعث کرش فرایند (Process) میشود که فرایند به یک نشانی خارج از فضای نشانی (Address Space) خود دسترسی پیدا کند؛ امری که در مورد یک اشارهگر سرگردان (Dangling Pointer) احتمال وقوع کمی دارد. بنابراین، باگهای استفاده پس از آزادسازی (UAF) اغلب شناسایی نمیشوند و میتوانند بردار مناسبی برای بهرهبرداری (Exploitation) باشند [45, 75].
۲.۲ رهگیری پشته و باگ (Stack Traces and Bug Traces)
با بررسی وضعیت یک فرایند (Process)، میتوان به یک رهگیری پشته (Stack Trace) دست یافت؛ یعنی فهرستی از فراخوانیهای تابعی که در آن وضعیت فعال هستند. هنگامی که یک فرایند دچار کرش میشود، بهدستآوردن رهگیری پشته از آن بهسادگی امکانپذیر است. از آنجا که رهگیریهای پشته اطلاعاتی (هرچند ناقص) درباره توالی مکانهای برنامه که به کرش منتهی شدهاند ارائه میکنند، برای بازآفرینی باگ (Bug Reproduction) بسیار ارزشمند هستند 25، 28، 42، 49]. با این حال، از آنجا که کرشهای ناشی از باگهای استفاده پس از آزادسازی (UAF) ممکن است مدت زیادی پس از وقوع خود UAF رخ دهند، رهگیریهای پشته استاندارد معمولاً برای بازتولید باگهای UAF مفید نیستند.
خوشبختانه، ابزارهای پروفایلسازی (Profiling) برای تشخیص پویای خرابیهای حافظه (Memory Corruptions)، مانند ASan [65] یا VALGRIND [55]، رهگیریهای پشته مربوط به تمام رویدادهای مرتبط با حافظه را ثبت میکنند. هنگامی که این ابزارها تشخیص میدهند که یک شیء پس از آزادسازی مورد استفاده قرار گرفته است، در واقع سه رهگیری پشته را گزارش میکنند: رهگیری مربوط به زمانی که شیء تخصیص داده شده است، رهگیری مربوط به زمانی که شیء آزاد شده است و رهگیری مربوط به زمانی که شیء پس از آزادسازی مورد استفاده قرار گرفته است. ما چنین توالی سهتایی از رهگیریهای پشته را یک رهگیری باگ (Bug Trace) مینامیم. هنگامی که از یک رهگیری باگ به عنوان ورودی برای تلاش جهت بازتولید باگ استفاده میکنیم، آن رهگیری باگ را یک هدف (Target) مینامیم.
۲.۳ فازینگ جعبهخاکستری هدایتشده (Directed Greybox Fuzzing)
فازینگ (Fuzzing) [50, 53] شامل آزمون تحت فشار یک کد از طریق تولید گسترده و انبوه ورودیها، با هدف یافتن باگها است. فازرهای جعبهخاکستری مبتنی بر پوشش (Coverage-Based Greybox Fuzzers یا CGF) جدید [1، 13] برای هدایت فرایند جستوجو به تحلیل سبک برنامه (Lightweight Program Analysis) متکی هستند که معمولاً از طریق بازخورد مبتنی بر پوشش (Coverage-Based Feedback) انجام میشود. بهطور کلی، یک بذر (Seed – ورودی) زمانی موفق گشته و ارجحیت خواهد داشت که به بخشهایی از کد دست یابد که کمتر مورد کاوش قرار گرفتهاند؛ سپس این بذرهای موفق و ارجح، جهش (Mutation) داده میشوند تا بذرهای جدیدی ایجاد شوند که برای اجرای کد مورد استفاده قرار میگیرند. فازینگ جعبهخاکستری مبتنی بر پوشش (CGF) با هدف پوشش گسترده کد طراحی شده است، با این امید که در نهایت آسیبپذیریهای ناشناخته شناسایی شوند.
از سوی دیگر، فازینگ جعبهخاکستری هدایت شده (Directed Greybox Fuzzing یا DGF) [25, 28] با هدف دستیابی هرچه سریعتر و با بیشترین دفعات ممکن به یک بخش بالقوه دارای باگ از کد که از پیش شناسایی شده است، از یک هدف (Target) مانند وصله (Patch) یا گزارش تحلیل ایستا (Static Analysis Report) استفاده میکند. فازرهای هدایت شده از اصول کلی و معماری مشابه فازرهای جعبهخاکستری مبتنی بر پوشش (CGF) پیروی میکنند، اما مؤلفههای کلیدی خود را با هدف موردنظرشان سازگار میکنند؛ بهگونهای که در اصل، بذرهایی را ترجیح میدهند که به هدف «نزدیکتر» میباشند.
در مجموع، فازرهای هدایتشده بر سه گام اصلی بنا شدهاند:
- ابزارگذاری (Instrumentation) – شامل پیشمحاسبه فاصله (Distance Pre-Computation)
- فازینگ (Fuzzing) – شامل انتخاب بذر (Seed)، زمانبندی توان (Power Schedule) و جهش بذر
- تریاژ (Triage)
الگوریتم هسته استاندارد DGF در الگوریتم ۱ ارائه شده است؛ بخشهایی که در UAFUZZ تغییر داده شدهاند، با رنگ خاکستری مشخص شدهاند. با داشتن یک برنامه P، مجموعهای از بذرهای اولیه S0 و یک هدف T، این الگوریتم مجموعهای از ورودیهای فعالساز باگ S′ را تولید میکند. صف فازینگ S با بذرهای اولیه موجود در S0 مقداردهی اولیه میشود (خط ۱).
۱. DGF ابتدا یک تحلیل ایستا (Static Analysis) انجام میدهد؛ برای مثال، فاصله تا هدف (Target Distance) را برای هر بلوک پایه (Basic Block) محاسبه میکند و ابزارگذاری (Instrumentation) لازم برای جمعآوری و ثبت اطلاعات پوشش (Coverage) یا فاصله به صورت پویا را در برنامه درج میکند (خط ۲).
۲. سپس فازر، بهطور مکرر ورودیهایی را که از صف فازینگS انتخاب شدهاند، جهش میدهد (خط ۴) تا هنگامی که زمان اجرای تعیینشده (Timeout) به پایان برسد. یک ورودی زمانی انتخاب میشود که مطلوب (Favored) باشد؛ یعنی تصور شود ورودی جالب یا ارزشمندی است، و یا با یک احتمال اندک α (آلفا) انتخاب گردد (خط ۵). پس از آن، DGF توان (Energy) را به بذر انتخاب شده s اختصاص میدهد؛ یعنی تعداد M جهشیافتههایی (Mutants) که باید از آن ایجاد شوند (خط ۶). سپس فازر با اعمال تصادفی تعدادی عملگر جهش (Mutation Operators) از پیش تعریف شده روی بذر s، تعداد M ورودی جدید تولید (خط ۸) و اجرای آنها را پایش میکند (خط ۹). چنانچه ورودی جهشیافته تولید شده s′ باعث کرش کردن (Crash) برنامه شود، به مجموعه S′ از ورودیهای منجر به کرش اضافه میشود (خط ۱۱). همچنین، جهشیافتههای جدید تولید شده به صف فازینگ اضافه میشوند (خط ۱۳).
۳. در نهایت، DGF مجموعه S′ را به عنوان مجموعه ورودیهای فعالساز باگ بازمیگرداند؛ در DGF استاندارد، مرحله تریاژ هیچ کاری انجام نمیدهد (خط ۱۴).
الگوریتم ۱: فازینگ جعبهخاکستری هدایت شده (Directed Greybox Fuzzing)
AFLGO [25] نخستین رویکردی بود که فاصله مبتنی بر گراف جریان کنترل (CFG-Based Distance) را برای ارزیابی میزان نزدیکی بین اجرای یک بذر (Seed) و چندین هدف پیشنهاد کرد و در کنار آن، یک زمانبندی توان مبتنی بر بازپخت شبیهسازیشده (Simulated Annealing-Based Power Schedule) نیز ارائه داد. HAWKEYE [28] رویکرد مبتنی بر CFG را حفظ میکند، اما دقت آن را بهبود میدهد، یک ابتکار انتخاب بذر (Seed Selection Heuristic) ارائه میدهد که تا حدی بر پوشش هدف (Target Coverage) که بهصورت مجموعهای از مکانها در نظر گرفته میشود مبتنی است و همچنین جهشهای تطبیقی (Adaptive Mutations) را پیشنهاد میکند.
۳. دلایل انجام پژوهش (Motivation)
مثال ساده موجود در فهرست ۲ شامل یک باگ استفاده پس از آزادسازی (Use-After-Free یا UAF) است که در اثر نبود فراخوانی exit() ایجاد شده است؛ این مورد یکی از علل ریشهای رایج در چنین باگهایی است (برای مثال، CVE-2014-9296 و CVE-2015-7199). این برنامه یک فایل را میخواند و محتوای آن را در یک بافر به نام buf کپی میکند. بهطور مشخص، یک قطعه حافظه (Memory Chunk) که p به آن اشاره میکند، تخصیص داده میشود (خط ۱۲)، سپس p_alias و p به یک ناحیه حافظه یکسان اشاره میکنند (خط ۱۵). حافظهای که هر دو اشارهگر به آن اشاره میکنند، در تابع bad_func آزاد میشود (خط ۱۰). باگ UAF زمانی رخ میدهد که حافظه آزاد شده دوباره از طریق p مورد ارجاعزدایی (Dereference) قرار گیرد (خط ۱۹).
شرایط فعالسازی باگ (Bug-triggering conditions). باگ UAF تنها در صورتی فعال میشود که سه بایت اول ورودی برابر با AFU باشند. برای شناسایی سریع این باگ، فازرها باید مسیر صحیح را از بخش if عبارتهای شرطی در خطوط ۱۳، ۵ و ۱۸ طی کنند تا سه رویداد UAF، یعنی تخصیص (Alloc)، آزادسازی (Free) و استفاده (Use)، بهترتیب پوشش داده شوند. شایان ذکر است که این باگ UAF باعث کرش برنامه نمیشود؛ بنابراین، فازرهای جعبهخاکستری موجود که فاقد سازوکارهای پاکسازی (Sanitization) هستند، این خطای حافظه را شناسایی نخواهند کرد.
فازینگ جعبهخاکستری مبتنی بر پوشش (Coverage-Based Greybox Fuzzing). با شروع از یک بذر (Seed) خالی، AFL به سرعت ۳ ورودی جدید، برای مثال AAAA، FFFF و UUUU، تولید میکند که هرکدام بهتنهایی یکی از ۳ رویداد UAF را فعال میکنند. با این حال، هیچیک از این بذرها، باگ را فعال نمیکنند. از آنجا که احتمال تولید ورودیای که با AFU شروع شود، در صورت شروع از یک بذر خالی، بسیار ناچیز است، سازوکار هدایت شده بر اساس پوشش (Coverage-Guided Mechanism) در اینجا نمیتواند در دنبالکردن یک توالی از رویدادهای UAF مؤثر باشد؛ این در حالی است که فعالسازی هر یک از این رویدادها بهصورت مجزا بهسادگی امکانپذیر است.
فازینگ جعبه خاکستری هدایت شده (Directed Greybox Fuzzing). با در اختیار داشتن یک رهگیری باگ (Bug Trace)، برای مثال (14 – alloc, 17, 6, 3 – free, 19 – use) که توسط ASan تولید شده است، DGF مانع از آن میشود که فازر مسیرهای نامطلوب را کاوش کند؛ برای مثال، بخش else در خط ۷ تابع func، به ویژه زمانی که شرط موجود در خط ۵ پیچیدهتر باشد. با این حال، فازرهای هدایت شده نیز نقاط کور خاص خود را دارند. برای مثال، سازوکارهای استاندارد انتخاب بذر در DGF، بذری را ترجیح میدهند که رهگیری اجرای آن مکانهای زیادی از اهداف را پوشش دهد، بهجای آنکه تلاش کنند این مکانها را به ترتیب مشخصی طی کنند. به عنوان مثال، در رابطه با هدف (A, F, U)، معیارهای فاصله استاندارد DGF [25, 28] ، بین ورودیs1 که مسیری به شکل A → F → U را پوشش میدهد و ورودیs2 که مسیر U → A → F را پوشش میدهد، تفاوتی قائل نمیشوند.
عدم وجود ترتیب در کاوش مکانهای هدف، شناسایی باگهای UAF را برای فازرهای هدایت شده موجود بسیار دشوار میکند. مثال دیگری نیز وجود دارد: تابع توان (Power Function) پیشنهادی HAWKEYE [28] ممکن است انرژی زیادی را به یک بذر اختصاص دهد که رهگیری اجرای آن به تابع هدف نمیرسد. این موضوع میتواند باعث شود فازر در مثال ساده ما، در بخش else در خط ۷ تابع func گرفتار گردد.
لیست ۲: مثال انگیزشی (Motivating Example)
1 int *p , * p_alias ;
2 char buf [10];
3 void bad_func (int *p) { free (p);} /* exit() is missing */
4 void func () {
5 if ( buf [1] == ’F ’)
6 bad_func (p);
7 else /* lots more code ... */
8 }
9 int main (int argc , char * argv []) {
10 int f = open ( argv [1] , O_RDONLY );
11 read (f , buf , 10) ;
12 p = malloc (sizeof(int));
13 if ( buf [0] == ’A ’){
14 p_alias = malloc (sizeof(int));
15 p = p_alias ;
16 }
17 func () ;
18 if ( buf [2] == ’U ’)
19 *p = 1;
20 return 0;
21 }
نگاهی اجمالی به UAFUZZ. ما بهطور ویژه بر اصلاح ابتکار انتخاب بذر با توجه به تعداد اهدافی که یک رهگیری اجرا پوشش میدهد (بخش ۴.۲) و افزودن معیارهای بذر (Seed) آگاه از ترتیب اهداف در DGF (بخش ۴.۳) تمرکز داریم. در یک مثال ساده،UAFUZZ ورودیهایی تولید میکند که فرایند را در جهت توالیهای هدف مورد انتظار پیش میبرند. برای مثال، در همان صف فازینگ که شامل ۴ ورودی است، نمونه جهشیافته AFAA که با جهش بذر AAAA تولید شده است، توسط AFL کنار گذاشته میشود؛ زیرا باعث افزایش پوشش کد نمیشود. با این حال، از آنجا که این نمونه جهشیافته در مقایسه با هر ۴ ورودی قبلی موجود در صف (که امتیازشان ۰ یا ۲ است)، بالاترین امتیاز را در معیار شباهت به هدف دارد (یعنی ۴ هدف شامل خطوط ۱۴، ۱۷، ۶ و ۳ را پوشش میدهد)، توسط UAFUZZ برای مراحل بعدی فازینگ انتخاب میشود. UAFUZZ با ادامه فازینگ روی AFAA، در نهایت ورودی ایجاد میکند که باگ را فعال میکند؛ برای مثال AFUA.
ارزیابی. AFLGO (در سطح کد منبع) نمیتواند باگ UAF را طی ۲ ساعت شناسایی کند، در حالی که UAFUZZ (در سطح باینری) قادر است آن را طی ۲۰ دقیقه فعال کند. همچنین، UAFUZZ برای تأیید تنها یک ورودی را به VALGRIND ارسال میکند؛ یعنی همان ورودی اثبات مفهوم (PoC) صحیح، در حالی که AFLGO ۱۲۰ ورودی را برای بررسی به VALGRIND ارسال میکند.
۴. رویکرد UAFUZZ (The UAFUZZ Approach)
UAFUZZ از چندین مؤلفه تشکیل شده است که شامل انتخاب بذر (بخش ۴.۲)، معیارهای ورودی (بخش ۴.۳)، زمانبندی توان (بخش ۴.۴) و تریاژ بذر (بخش ۴.۵) هستند. پیش از بررسی جزئیات این مؤلفهها، ابتدا مروری کلی بر رویکرد ارائه میکنیم.
هدف ما یافتن ورودی است که هم شرایط جریان کنترل (Control-Flow) یا زمانی (Temporal) و هم شرایط زمان اجرا (Runtime) یا مکانی (Spatial) لازم برای فعالسازی باگ UAF را برآورده سازد. ما این مسئله را با وارد کردن ویژگیهای UAF به DGF حل میکنیم تا ورودیهای بالقوه بیشتری تولید شوند که با توجه به ردپای باگ مورد انتظار UAF، اهداف را بهترتیب طی کنند. شکل ۲ نمای کلی این رویکرد را نشان میدهد. بهطور مشخص:
- ما سه معیار پویا (Dynamic Metrics) برای بذر پیشنهاد میکنیم که به طور اختصاصی برای شناسایی آسیبپذیریهای UAF طراحی شدهاند. معیار فاصله (Distance Metric) مشخص میکند که یک بذر (Seed) تا چه اندازه به تمام مکانهای هدف نزدیک است (بخش ۴.۳) و نیاز به این را در نظر میگیرد که ردپای اجرای بذر باید سه رویداد UAF را بهترتیب پوشش دهد. معیار پوشش یالهای برش (Cut-Edge Coverage Metric) (بخش ۴.۴.۱) توانایی یک بذر را در اتخاذ تصمیم صحیح در گرههای تصمیمگیری مهم اندازهگیری میکند. در نهایت، معیار شباهت به هدف (Target Similarity Metric) بهصورت مشخص ارزیابی میکند که ردپای اجرای یک بذر در زمان اجرا، چه تعداد از اهداف را پوشش میدهد (بخش ۴.۲.۲).
- راهبرد انتخاب بذر (بخش ۴.۲) ما، بذرهایی را ترجیح میدهد که در زمان اجرا، تعداد بیشتری از اهداف را پوشش میدهند. زمانبندی توان (Power Scheduler) که در طول فرایند فازینگ، میزان انرژی اختصاصیافته به هر بذر انتخابشده را بر اساس امتیازهای معیارهای آن تعیین میکند، در بخش۴.۴ بهتفصیل توضیح داده شده است.
- در نهایت، از معیارهای قبلی خود استفاده میکنیم تا ورودیهایی را که احتمال بیشتری دارند که باگ را فعال کنند (PoC)، از پیش شناسایی کنیم و آنها را برای تأیید باگ به یک ابزار پروفایلسازی (Profiling Tool) (در اینجا VALGRIND ) ارسال کنیم. به این ترتیب، از انجام تعداد زیادی بررسی غیرضروری جلوگیری میشود (بخش۴.۵).
۴.۱ تبدیل رهگیری باگ به ساختار خطی (Bug Trace Flattening)
یک رهگیری باگ (Bug Trace) (بخش ۲.۲) شامل توالی از رهگیریهای پشته (Stack Traces) است؛ بنابراین، ساختاری بزرگ و پیچیده است که برای ابزارگذاری سبک (Lightweight Instrumentation) موردنیاز در فازینگ جعبهخاکستری مناسب نیست. ارزشمندترین اطلاعاتی که باید از یک رهگیری باگ استخراج کنیم، توالی بلوکهای پایه (Basic Blocks) و توابعی است که طی اجرا پیموده شدهاند؛ ساختاری که کار با آن سادهتر است. ما این فرایند استخراج را خطیسازی رهگیری باگ (Bug Trace Flattening) یا تبدیل رهگیری باگ به ساختار خطی مینامیم.
این عملیات بهصورت زیر انجام میشود. ابتدا، هر یک از سه رهگیری پشته به عنوان یک مسیر در یک درخت فراخوانی (Call Tree) در نظر گرفته میشود؛ سپس همه رهگیریهای پشته را با یکدیگر ادغام میکنیم تا آن درخت را بازسازی کنیم. برخی از گرههای درخت دارای چند فرزند هستند؛ بنابراین، اطمینان حاصل میکنیم که فرزندان بر اساس ترتیب رویدادهای UAF مرتب شدهاند؛ یعنی فرزندی که از رویداد تخصیص (Alloc) به دست آمده است، پیش از فرزندی قرار گیرد که از رویداد آزادسازی (Free) به دست آمده است. شکل ۳ نمونهای از درخت مربوط به رهگیری باگی را که در شکل ۱ ارائه شده بود، نشان میدهد. در نهایت، با پیمایش پیشگام (Preorder Traversal) این درخت، توالی از مکانهای هدف (Target Locations) و توابع مرتبط با آنها به دست میآوریم که در الگوریتمهای بعدی از آن استفاده خواهیم کرد.
۴.۲ انتخاب بذر بر اساس شباهت به هدف (Seed Selection based on Target Similarity)
فازرها تعداد بسیار زیادی ورودی تولید میکنند؛ بنابراین، انتخاب هوشمندانه بذر (Seed) از میان صف فازینگ برای جهش در کارزار فازینگ بعدی، نقش مهمی در اثربخشی فازینگ دارد. الگوریتم انتخاب بذر ما بر دو بینش استوار است. نخست، باید بذرهایی را در اولویت قرار دهیم که بیشترین شباهت را به رهگیری باگ هدف (Target Bug Trace) دارند، زیرا هدف یک فازر هدایت شده، یافتن باگهایی است که رهگیری باگ هدف را پوشش میدهند. دوم، شباهت به هدف باید ترتیب رخدادها (که به آن Sequenceness نیز گفته میشود) را در نظر بگیرد؛ زیرا رهگیریهایی که تعدادی از مکانهای موجود در رهگیری باگ هدف را بهصورت متوالی و به ترتیب پوشش میدهند، نسبت به ردپاهایی که همان مکانها را با ترتیبی دلخواه پوشش میدهند، به هدف نزدیکتر هستند.
۴.۲.۱ انتخاب بذر (Seed Selection)
تعریف ۱. یک ورودی با بیشترین میزان پیشروی (Max-Reaching Input)، ورودی s است که ردپای اجرای آن، تا آن لحظه، بیشترین شباهت را به ردپای باگ هدف T دارد؛ منظور از بیشترین شباهت، بالاترین مقدار بر اساس معیار شباهت به هدف t(s,T) است.
الگوریتم ۲: IS_FAVORED (تشخیص بذر مطلوب)
ما در فرایند فازینگ، عمدتاً ورودیهایی با بیشترین میزان پیشروی (Max-Reaching Inputs) را انتخاب کرده و جهش (Mutate) میدهیم. بااینحال، از آنجا که بهبود پوشش کد (Code Coverage) نیز ضروری است، UAFUZZ با یک احتمال اندک α، ورودیهایی را نیز انتخاب میکند که مسیرهای جدیدی را پوشش میدهند (الگوریتم ۱). در آزمایشهای ما، احتمال انتخاب سایر ورودیهای موجود در صف فازینگ که در اولویت پایینتری قرار دارند، مانند AFL [1]، برابر با ۱٪ است.
۴.۲.۲ معیارهای شباهت به هدف (Target Similarity Metrics)
یک معیار شباهت به هدف t(s,T)، میزان شباهت بین اجرای یک بذر (Seed) به نام s و رهگیری باگ UAF هدف T را اندازهگیری میکند. ما ۴ معیار از این نوع تعریف میکنیم که بر اساس دو ویژگی از یکدیگر متمایز میشوند: اینکه آیا ترتیب اهداف پوششدادهشده در ردپای باگ را در نظر میگیریم (P) یا خیر (B) که P کوتاه شده Prefix (ترتیب) و B نیز کوتاه شده Bag (بدون ترتیب) است و اینکه آیا کل رهگیری را در نظر میگیریم یا تنها سه رویداد UAF را (3T):
- Target prefix tP(s, T ): یا پیشوند هدف که مکانهای موجود در T که با اجرای s بهترتیب و تا زمان نخستین انحراف (divergence)، پوشش داده میشوند؛
- UAF prefix t3T P(s, T ) : یا پیشوند UAF رویدادهای UAF موجود در T که با اجرای s، بهترتیب و تا زمان نخستین انحراف، پوشش داده میشوند؛
- Target bag tB(s, T ) : مکانهای موجود در T که با اجرای s پوشش داده میشوند؛
- UAF bag t3T B(s, T ) : رویدادهای UAF موجود در T که توسط s پوشش داده میشوند.
برای مثال، با استفاده از لیست ۲، مقادیر چهار معیار برای بذر (seed) به نام s = ‘ABUA’ نسبت به رهگیری باگ UAF یعنی T بهصورت زیر هستند:
tP (s, T ) = 2, t3PT (s, T ) = 1, tB (s, T ) = 3 and t3TB (s, T ) = 2
این چهار معیار، درجات متفاوتی از دقت را دارا میباشند. معیار t زمانی دقیقتر از معیار ′t در نظر گرفته میشود که برای هر دو بذر (seed) یعنی s1 و s2، رابطه زیر برقرار باشد:
t(s1, T ) ≥ t(s2, T ) ⇒ t′(s1, T ) ≥ t′(s2, T )
شکل ۴، چهار معیار ما را از نظر دقت نسبی (relative precision) با یکدیگر مقایسه میکند.
۴.۲.۳ ترکیب معیارهای شباهت هدف (Combining Target Similarity Metrics)
استفاده از یک معیار دقیق مانند P امکان ارزیابی بهتر میزان پیشرفت بهسوی هدف را فراهم میکند. بهطور خاص، P میتواند بذرهایی را که با رهگیری باگ هدف مطابقت دارند از بذرهایی که مطابقت ندارند، متمایز کند؛ درحالی که سایر معیارها قادر به انجام این کار نیستند. از سوی دیگر، یک معیار با دقت کمتر اطلاعاتی را ارائه میدهد که معیارهای دقیق فاقد آن هستند. برای مثال، P هیچ تفاوتی میان رهگیریهایی که پسوند آنها با ردپای خطای هدف مطابقت دارد، اما در نخستین مکانها از ردپای هدف منحرف میشوند، اندازهگیری نمیکند؛ مانند ‘UUU’ و ‘UFU’ در لیست ۲. در مقابل، B میتواند این تفاوت را تشخیص دهد. به منظور بهرهگیری همزمان از مزایای معیارهای دقیق و غیردقیق، آنها را با استفاده از ترتیب واژهنامهای (lexicographical order) با یکدیگر ترکیب میکنیم. بنابراین، معیار P-3TP-B به صورت زیر تعریف میشود:
این ترکیب، ابتدا بذرهایی (seed) را در اولویت قرار میدهد که بیشترین تعداد مکانها را در پیشوند پوشش (prefix) میدهند؛ سپس، در صورت برابر بودن، بذرهایی را ترجیح میدهد که به بیشترین تعداد رویدادهای UAF بهصورت متوالی دست پیدا میکنند؛ و در نهایت، در صورت برابر بودن، بذرهایی را ترجیح میدهد که بیشترین تعداد مکانها را در هدف پوشش میدهند. بر اساس بررسیهای اولیه، ما بهصورت پیشفرض از P-3TP-B برای انتخاب بذر در UAFUZZ استفاده میکنیم.
۴.۳ فاصله مبتنی بر UAF(UAF-based Distance)
یکی از مؤلفههای اصلی فازرهای جعبهسیاه هدایتشده (Directed Greybox Fuzzers)، محاسبه فاصله بذر (seed) است که ارزیابی از فاصله میان رهگیری اجرای بذر یعنی s و هدف ارائه میدهد. ایده اصلی در اینجا این است که اگر رهگیری اجرای s به هدف نزدیک باشد، آنگاه s نیز به ورودی که قادر به پوشش هدف خواهد بود نزدیک است؛ بنابراین، s یک بذر جالب و قابلتوجه محسوب میشود. در فازرهای جعبهسیاه هدایتشده موجود [2، 28]، فاصله بذر نسبت به هدفی محاسبه میشود که میتواند یک مکان منفرد یا مجموعهای از مکانها باشد. این روش برای بازتولید باگهای UAF مناسب نیست، زیرا برای بازتولید این خطاها باید ۳ مکان متفاوت را بهترتیب و بهصورت متوالی طی کرد. ازاینرو، پیشنهاد میکنیم محاسبه فاصله بذر را بهگونهای تغییر دهیم که نیاز به رسیدن به مکانها به ترتیب مشخص را نیز در نظر بگیرد.
۴.۳.۱ زوم: پیشزمینه درباره فواصل بذرها (Zoom: Background on Seed Distances)
فازرهای جعبهخاکستری هدایتشده (Directed Greybox Fuzzers) موجود [2، 28]، فاصله d(s,T) را از یک بذر s تا یک هدف T بهصورت زیر محاسبه میکنند.
فاصله بذر در AFLGO [2]. فاصله بذر d(s,T) به صورت میانگین حسابیِ فاصلههای بلوک پایه db(m,T) برای هر بلوک پایه m موجود در رهگیری اجرای (Execution Trace) بذر s تعریف میشود. فاصله بلوک پایه db(m,T) با استفاده از دو مؤلفه تعریف میشود: نخست، طول کوتاهترین مسیر درونرویهای (Intra-procedural Shortest Path) از m تا بلوک پایهای که شامل یک دستور «فراخوانی» (Call) میشود، این مسیر با استفاده از گراف جریان کنترل (Control-Flow Graph یا CFG) تابعی که m در آن قرار دارد، محاسبه میشود؛ و دوم، طول کوتاهترین مسیر بینرویهای (Inter-procedural Shortest Path) از تابعی که m در آن قرار دارد تا توابع هدف Tf. این مسیر نیز با استفاده از گراف فراخوانی (Call Graph) محاسبه میشود. در مورد ما، Tfهمان تابعی است که رویداد استفاده (Use Event) در آن رخ میدهد.
بهبود HAWKEYE [28]. عامل اصلی در محاسبه فاصله بذر، محاسبه فاصله میان توابع در گراف فراخوانی (Call Graph) است. برای محاسبه این فاصله، AFLGO از گراف فراخوانی اولیه استفاده میکند که در آن وزن هر یال برابر با ۱ در نظر گرفته شده است.HAWKEYE با ارائه فاصله افزوده میان توابع مجاور (Augmented Adjacent-Function Distance یا AAFD)، این محاسبه را بهبود میدهد. در این روش، وزن یال میان یک تابع فراخوان (Caller Function) faو یک تابع فراخوانده شده (Callee Function) fb از مقدار قبلی به wHawkeye( fa, fb) تغییر میکند. ایده اصلی این است که به یالهایی در گراف فراخوانی اولویت داده شود که تابع فراخواندهشده در موقعیتها یا شرایط متنوعی قابل فراخوانی باشد؛ یعنی در مکانهای مختلف چندین بار ظاهر شود.
۴.۳.۲ فاصله بذر پیشنهادی ما مبتنی بر UAF
فاصلههای بذر پیشین [2، 28] هیچگونه ترتیبی را میان مکانهای هدف در نظر نمیگیرند، در حالی که در مورد آسیبپذیری استفاده پس از آزادسازی (Use-After-Free یا UAF)، در نظر گرفتن این ترتیب ضروری است. ما این مسئله را با تغییر فاصله میان توابع در گراف فراخوانی برطرف میکنیم؛ بهگونهای که مسیرهایی که بهترتیب از سه رویداد UAF، یعنی تخصیص (alloc)، آزادسازی (free) و استفاده (use) در ردپای خطا عبور میکنند، در اولویت قرار گیرند. این کار با کاهش وزن یالهای گراف فراخوانی که احتمال میرود بین این رویدادها قرار داشته باشند و با استفاده از تحلیل ایستای سبکوزن (Lightweight Static Analysis) انجام میشود.
این تحلیل ابتدا مجموعههای Ralloc، R f ree, و Ruseرا محاسبه میکند؛ یعنی مجموعه توابعی که به ترتیب میتوانند تابع alloc، free یا use موجود در ردپای خطا را فراخوانی کنند؛ تابع use همان تابعی است که رویداد استفاده در آن رخ میدهد.
سپس، هر یال فراخوانی بین fa و fb را به عنوان یک جهت در نظر میگیریم: یا رو به پایین (fa) اجرا میشود و سپس fb را فراخوانی میکند و یا رو به بالا (fb) فراخوانی میشود و سپس fa اجرا میشود. با استفاده از این مفهوم، برای هر جهت محاسبه میکنیم که با عبور از یال در آن جهت، چه تعداد رویداد متوالی را میتوان پوشش داد. برای مثال، اگر fa ∈ Ralloc و fb ∈ R f ree ∩ Ruseباشد، عبور از یال فراخوانی fa → fb میتواند امکان پوشش هر سه رویداد UAF را بهترتیب فراهم کند.
برای شناسایی آزادسازی مضاعف (Double Free) نیز یالهای فراخوانی را در این محاسبه لحاظ میکنیم که امکان رسیدن متوالی به دو رویداد free را فراهم میکنند. سپس، یک یال فراخوانی از faبه fb را با کاهش وزن آن در اولویت قرار میدهیم؛ میزان این کاهش بر اساس تعداد رویدادهای متوالی تعیین میشود که آن یال امکان پوشش آنها را فراهم میکند. در آزمایشهای خود، از تابع ΘUAF ( fa, fb) زیر با مقدار β = 0.25 استفاده میکنیم:
شکل ۵ نمونهای از یک گراف فراخوانی را نشان میدهد که در آن، یالها با استفاده از تابع ΘUAF فوق، در اولویت قرار گرفتهاند. در نهایت، تغییر وزن یالهای پیشنهادی خود را با تغییر وزن ارائه شده توسط HAWKEYE ترکیب میکنیم:
همانند AFLGO، ما مسیر کوتاهتر منتهی به اهداف را در اولویت قرار میدهیم، زیرا احتمال بیشتری دارد که تنها شامل تعداد محدودی از محدودیتهای جریان کنترل (Control-Flow Constraints) باشد و در نتیجه، پوشش دادن آن از طریق فازینگ آسانتر باشد. بنابراین، روش مبتنی بر فاصله ما، هم روابط فراخوانی به طور کلی را از طریق wHawkeyew و هم روابط فراخوانی را که رویدادهای UAF را بهترتیب پوشش میدهند، از طریق ΘUAF در نظر میگیرد.
۴.۴ زمانبندی توان (Power Schedule)
فازرهای هدایت شده بر اساس پوشش (Coverage-Guided Fuzzers) از یک زمانبندی توان (Power Schedule) یا اختصاص انرژی (Energy Assignment) استفاده میکنند تا تعداد ورودیهای اضافی را که باید از یک ورودی انتخابشده تولید شوند، تعیین کنند. به این تعداد، انرژی بذر (Seed Energy) گفته میشود. این معیار مشخص میکند که چه مدت زمانی را باید صرف فازینگ یک بذر خاص کنیم.
در حالی که AFL [1] عمدتاً از ویژگیهای مربوط به ردپای اجرا (Execution Trace)، مانند اندازه رهگیری (Trace Size)، سرعت اجرای برنامه تحت آزمون (PUT) و مدتزمان سپری شده از زمان اضافه شدن بذر به صف فازینگ (Fuzzing Queue)، برای تخصیص انرژی به بذر استفاده میکند، پژوهشهای اخیر [26، 48، 62]، شامل فازینگ هدایتشده و فازینگ مبتنی بر پوشش، زمانبندیهای توان متفاوتی را پیشنهاد کردهاند.
AFLGO از بازپخت شبیهسازیشده (Simulated Annealing) برای تخصیص انرژی بیشتر به بذرهایی استفاده میکند که به مکانهای هدف نزدیکتر هستند (با استفاده از فاصله بذر)، در حالی که HAWKEYE هم ردپاهای کوتاهتر و هم ردپاهای طولانیتر منتهی به اهداف را از طریق یک زمانبندی توان مبتنی بر فاصله ردپا (Trace Distance) و شباهت در سطح تابع (Function-Level Similarity) در نظر میگیرد.
در واقع، بازپخت شبیهسازیشده (Simulated Annealing) در حوزه فازینگ (Fuzzing)، یک الگوریتم بهینهسازی احتمالاتی است که برای تخصیص هوشمندانه انرژی یا زمان آزمون به ورودیها استفاده میشود تا احتمال کشف آسیبپذیری افزایش یابد. در اینجا، یک زمانبندی توان جدید پیشنهاد میکنیم که بر اساس این دیدگاه است که در موارد زیر باید انرژی بیشتری به بذرها اختصاص دهیم:
- بذرهایی که نزدیکتر هستند (با استفاده از فاصله بذر، بخش ۴.۳.۲)؛
- بذرهایی که شباهت بیشتری به هدف دارند (با استفاده از شباهت به هدف، بخش ۴.۲.۲)؛
- بذرهایی که در محلهای اتصال بحرانی کد (Critical Code Junctions) تصمیمهای بهتری اتخاذ میکنند.
در ادامه، معیار جدیدی را برای ارزیابی حالت سوم تعریف میکنیم.
۴.۴.۱معیار پوشش یالهای برش (Cut-edge Coverage Metric)
برای ردیابی میزان پیشرفت یک بذر در طول فرایند فازینگ، یک رویکرد دقیق میتواند شامل ابزارگذاری (Instrumentation) اجرای برنامه باشد تا شباهت میان ردپای اجرای بذر فعلی و ردپای خطای هدف را در سطح بلوک پایه با یکدیگر مقایسه کند. بااینحال، این روش به دلیل سربار بالای زمان اجرا (Runtime Overhead)، بهویژه در برنامههای بزرگ، موجب کُند شدن فرایند فازینگ میشود. از سوی دیگر، یک رویکرد کلیتر این است که همانطور که در HAWKEYE [28] پیشنهاد شده، شباهت در سطح تابع اندازهگیری شود.
بااینحال، یک تابع فراخواندهشده (Callee) میتواند از مکانهای مختلف یک تابع فراخوان (Caller) چندین بار رخ دهد. همچنین، رسیدن به یک تابع هدف لزوماً به معنای رسیدن به بلوکهای پایه هدف درون آن تابع نیست. ازاینرو، ما معیار سبکوزن پوشش یالهای برش (Cut-edge Coverage Metric) را پیشنهاد میکنیم که با اندازهگیری میزان پیشرفت در سطح یال، اما تنها در گرههای تصمیمگیری بحرانی (Critical Decision Nodes)، میان دو رویکرد مذکور قرار میگیرد.
الگوریتم ۳: انباشت یالهای برش
الگوریتم ۴: محاسبه یالهای برش درون یک تابع
تعریف ۲. یک یال برش (Cut Edge) بین دو بلوک پایه، یعنی مبدأ (Source) و مقصد (Sink)، یال خروجیِ یک گره تصمیمگیری است، بهگونهای که مسیری وجود داشته باشد که از مبدأ آغاز شود، از این یال عبور کند و به مقصد برسد. یال غیر برش (Non-Cut Edge) یالی است که یال برش نیست؛ یعنی هیچ مسیری از مبدأ به مقصد وجود ندارد که از این یال عبور کند.
الگوریتم ۳ نشان میدهد که در UAFUZZ، با در اختیار داشتن یک برنامه باینری آزموده شده و یک رهگیری مورد انتظار از خطای UAF، چگونه یالهای برش و غیر برش شناسایی میشوند. ایده اصلی این است که یالهای برش میان تمامی گرههای متوالی در ردپای خطای (تخت شده) مورد انتظار را شناسایی و انباشت کنیم. برای مثال، در رهگیری خطای شکل ۳، ابتدا یالهای برش بین ۰ و ۱ را محاسبه میکنیم، سپس یالهای برش بین ۱ و ۲ و به همین ترتیب ادامه میدهیم. از آنجا که ردپای خطا یک دنبالهای از رهگیریهای پشته (Stack Traces) است، بیشتر مکانهای موجود در این ردپا مربوط به رویدادهای «فراخوانی» (Call) هستند و ما یال برش را از نقطه ورود تابع (Function Entry Point) تا رویداد فراخوانی در همان تابع محاسبه میکنیم. بااینحال، به دلیل تختسازی (Flattening)، در برخی موارد لازم است یالهای برش را بین نقاط مختلفی در یک تابع یکسان محاسبه کنیم. برای مثال، اگر در ردپای خطا، یک تابع یکسان ابتدا alloc و سپس free را فراخوانی کند، باید یال برش را از محل فراخوانی alloc تا محل فراخوانی free محاسبه کنیم.
الگوریتم ۴ نحوه محاسبه یالهای برش (Cut Edges) را درون یک تابع واحد توضیح میدهد. ابتدا باید گرههای تصمیمگیری (Decision Nodes)، یعنی پرشهای شرطی (Conditional Jumps) بین بلوکهای پایه مبدأ و مقصد را شناسایی کنیم. این کار را میتوان با استفاده از یک تحلیل جریان داده (Data-Flow Analysis) ساده انجام داد.
برای هر یال خروجیِ گره تصمیمگیری، بررسی میکنیم که آیا آن یال امکان رسیدن به بلوک پایه مقصد را فراهم میکند یا خیر؛ یالهایی که امکان رسیدن به مقصد را فراهم میکنند، یال برش هستند و سایر یالها یال غیر برش (Non-Cut Edges) محسوب میشوند. توجه داشته باشید که این تحلیل برنامه درونرویهای (Intra-procedural) است؛ بنابراین نیازی به ساخت یک گراف جریان کنترل بینرویهای (Inter-procedural CFG) نداریم. ابتکار (Heuristic) ما این است که ورودی که تعداد بیشتری یال برش و تعداد کمتری یال غیر برش را فعال میکند، احتمال بیشتری دارد که مکانهای بیشتری از هدف را پوشش دهد. فرض کنید Ecut(T) مجموعه تمام یالهای برش یک برنامه با توجه به ردپای مورد انتظار خطای UAF، یعنی T، باشد. ما امتیاز یال برش (Cut-edge Score) بذر s، یعنی es(s,T)، را به صورت زیر تعریف میکنیم:
در اینجا، hit(e) نشاندهنده تعداد دفعاتی است که یال e در طول اجرا پیمایش یا فعال میشود و δ ∈ (0, 1) وزن جریمهای است که به بذرهایی که یالهای غیر برش را پوشش میدهند، اختصاص داده میشود. در آزمایشهای اصلی خود، بر اساس آزمایشهای مقدماتی، از مقدار δ = 0.5 استفاده میکنیم. برای مقابله با انفجار مسیر (Path Explosion) ناشی از حلقهها، از روش باکتبندی (Bucketing) [1] استفاده میکنیم؛ به این صورت که تعداد دفعات پیمایش یالها به مقادیر کوچک توانهای ۲ تبدیل و در قالب دستههای مشخص (Bucket) قرار داده میشود.
۴.۴.۲ تخصیص انرژی
ما یک تابع زمانبندی توان (Power Schedule) پیشنهاد میکنیم که با استفاده از ترکیبی از سه معیاری که ارائه کردهایم، به یک بذر، انرژی اختصاص میدهد: معیار شباهت پیشوندی به هدف tP(s,T) (بخش ۴.۲.۲)، فاصله بذر مبتنی بر UAF، یعنی d(s,T) (بخش ۴.۳.۲)، و معیار پوشش یالهای برش es(s,T) (بخش ۴.۴.۱). ایده اصلی زمانبندی توان ما این است که انرژی یک بذر s را متناسب با تعداد اهدافی که بهصورت ترتیبی توسط tP(s,T) پوشش داده شدهاند اختصاص دهیم و در عین حال، یک عامل اصلاحی مبتنی بر فاصله بذر d و پوشش یالهای برش es در نظر بگیریم. در واقع، تابع توان ما (که متناظر با ASSIGN_ENERGY در الگوریتم ۱ است) بهصورت زیر تعریف میشود:
از آنجا که مقدار واقعی این دو معیار، بهاندازه طول پیشوند پوششداده شده معنادار نیست، اما برای رتبهبندی بذرها مفید هستند، از نرمالسازی بیشینه و کمینه (Min-Max Normalization) [2] استفاده میکنیم تا فاصله نرمالشده بذر ( ˜ds(s, T )) و امتیاز نرمالشده یالهای برش ( ˜es(s, T )) را به دست آوریم. به عنوان مثال: ˜ds(s, T ) = ds(s,T )−minD maxD−minD که در آن minDminD و maxDmaxD به ترتیب نشاندهنده کمینه و بیشینه مقدار فاصله بذر تا لحظه فعلی میباشند.
توجه داشته باشید که امتیاز هر دو معیار در بازه(0,1) قرار دارد؛ به این معنا که این معیارها تنها زمانی میتوانند انرژی اختصاصیافته را کاهش دهند که امتیاز بذر نامطلوب باشد.
۴.۵ پسپردازش و دستهبندی باگها (Postprocess and Bug Triage)
از آنجا که باگهای UAF اغلب بدون علامت (Silent) هستند، تمام بذرهای تولیدشده توسط یک فازر جهتدار (Directed Fuzzer) باید پیشاپیش برای تأیید اینکه آیا ورودی ایجادکننده باگ (Bug-Triggering Input) هستند یا خیر، به یک ابزار دستهبندی باگ (Bug Triager) ارسال شوند؛ این ابزار معمولاً یک ابزار پروفایلسازی (Profiling Tool) مانند VALGRIND است. بااینحال، این فرایند میتواند بسیار پرهزینه باشد، زیرا فازرها حجم بسیار زیادی بذر تولید میکنند و ابزارهای دستهبندی باگ نیز پرهزینه هستند.
خوشبختانه، معیار شباهت به هدف (Target Similarity Metric) به UAFUZZ اجازه میدهد توالی اهداف پوششدادهشده توسط هر ورودی فازشده را در زمان اجرا (Runtime) محاسبه کند. این اطلاعات پس از ایجاد و اجرای هر بذر، بدون تحمیل هزینه اضافی (For Free) در دسترس است. ما از این قابلیت استفاده میکنیم تا بذرهایی را که احتمالاً ایجادکننده باگ هستند از پیش شناسایی کنیم؛ یعنی بذرهایی که واقعاً هر سه رویداد UAF را به صورت ترتیبی پوشش میدهند. سپس ابزار دستهبندی باگ تنها روی همین بذرهای از پیش شناساییشده اجرا میشود و سایر بذرها بهسادگی کنار گذاشته میشوند؛ در نتیجه، این روش میتواند زمان بسیار زیادی را در فرایند دستهبندی باگ صرفهجویی کند.
۵. پیادهسازی (Implementation)
ما نتایج خود را در قالب یک فازر جهتدار در سطح باینری و مبتنی بر UAF با نام UAFUZZ پیادهسازی کردهایم. شکل ۶ نمای کلی اجزای اصلی UAFUZZ را نشان میدهد. ورودی کل سامانه شامل مجموعهای از بذرهای اولیه، برنامه تحت آزمون (PUT) در قالب باینری، و مکانهای هدف استخراجشده از رهگیری باگ است. خروجی سامانه نیز مجموعهای از ورودیهای منحصربهفرد ایجادکننده باگ است. نمونه اولیه (Prototype) بر پایه AFL 2.52b [1] و QEMU 2.10.0 برای فازینگ، و پلتفرم تحلیل باینری BINSEC [4] برای تحلیل ایستای سبک(Lightweight Static Analysis) ساخته شده است. این دو مؤلفه اطلاعاتی مانند مکانهای هدف، بودجه زمانی و وضعیت فازینگ را با یکدیگر به اشتراک میگذارند.
۶. ارزیابی تجربی (Experimental Evaluation)
۶.۱ پرسشهای پژوهش (Research Questions)
به منظور ارزیابی اثربخشی و کارایی رویکرد خود، چهار پرسش اصلی پژوهش را بررسی میکنیم:
پرسش پژوهشی اول (RQ1) – توانایی بازتولید باگ UAF (UAF Bug-reproducing Ability):
آیا UAFUZZ میتواند از نظر بازتولید باگهای UAF در فایلهای اجرایی، عملکرد بهتری نسبت به سایر تکنیکهای فازینگ جهتدار داشته باشد؟
پرسش پژوهشی دوم (RQ2) – سربار UAF (UAF Overhead):
UAFUZZ از نظر زمان ابزارگذاری (Instrumentation) و سربار زمان اجرا (Runtime Overhead) در مقایسه با سایر رویکردهای فازینگ جهتدار چگونه عمل میکند؟
پرسش پژوهشی سوم (RQ3) – دستهبندی باگهای UAF (UAF Triage):
UAFUZZ تا چه میزان تعداد ورودیهایی را که باید به مرحله دستهبندی باگ (Bug Triage) ارسال شوند، کاهش میدهد؟
پرسش پژوهشی چهارم (RQ4). سهم هر مؤلفه (Individual Contribution):
هر یک از مؤلفههای UAFUZZ چه میزان در نتایج کلی آن نقش دارند؟
همچنین، UAFUZZ را در زمینه آزمون وصله (Patch Testing) نیز ارزیابی خواهیم کرد؛ آزمون وصله یکی دیگر از کاربردهای مهم فازینگ جهتدار است [25, 28, 59].
۶.۲ تنظیمات ارزیابی (Evaluation Setup)
فازرهای مورد ارزیابی (Evaluation Fuzzers). هدف ما مقایسه UAFUZZ با فازرهای جهتدار پیشرفته، یعنی AFLGO [2] و HAWKEYE [28]، با استفاده از AFL–QEMU بهعنوان مبنا (Baseline) برای فازینگ مبتنی بر پوشش در سطح باینری (Binary-level Coverage-based Fuzzing) است. متأسفانه، هر دو فازر AFLGO و HAWKEYE بر روی کد منبع کار میکنند و در حالی که AFLGO متنباز است، HAWKEYE در دسترس نیست. ازاینرو، نسخههای در سطح باینری AFLGO و HAWKEYE را پیادهسازی کردیم که بهترتیب AFLGOB و HAWKEYEB نامگذاری شدهاند. ما بهدقت از مقالات اصلی پیروی کردهایم و برای AFLGO، از کد منبع آن نیز به عنوان مرجع استفاده کردهایم.
AFLGOB و HAWKEYEB بر روی AFL-QEMU پیادهسازی شدهاند و از معماری عمومی UAFUZZ پیروی میکنند، اما از سازوکارهای اختصاصی خود برای محاسبه فاصله (Distance)، انتخاب بذر (Seed Selection) و زمانبندی توان (Power Schedule) استفاده میکنند. جدول ۲ پیادهسازیهای مختلف فازرهای ما و مقایسه آنها با نمونههای متناظر اصلیشان را خلاصه میکند.
جدول ۲: نمای کلی تکنیکهای اصلی فازرهای جعبه خاکستری. پیادهسازیهای ما با علامت «؟» مشخص شدهاند
پیادهسازی AFLGOB را ارزیابی میکنیم (پیوست B) و پس از در نظر گرفتن سربار شبیهسازی (Emulation Overhead)، مشاهده میکنیم که عملکرد آن بسیار به AFLGO اصلی نزدیک است.
معیار ارزیابی فازینگ UAF (UAF Fuzzing Benchmark). معیار ارزیابی کوچک (Micro Benchmark) استاندارد UAF، یعنی Juliet Test Suite [56] که برای تحلیلگرهای ایستا (Static Analyzers) طراحی شده است، برای فازینگ بیش از حد ساده است. در حال حاضر، هیچ معیار ارزیابی بزرگمقیاسی (Macro Benchmark) برای ارزیابی اثربخشی آشکارسازهای UAF وجود ندارد؛ مجموعه LAVA [36] که بهطور گسترده مورد استفاده قرار میگیرد، تنها شامل سرریزهای بافر (Buffer Overflows) است. ازاینرو، ما یک معیار ارزیابی جدید برای UAF ایجاد میکنیم که بر اساس منطق و ملاحظات زیر طراحی شده است:
- برنامههای مورد بررسی، برنامههای محبوب، واقعی و نسبتاً بزرگی هستند که از نظر امنیتی اهمیت دارند.
- معیار ارزیابی شامل باگهای UAF کشفشده توسط فازرهای موجود در ادبیات فازینگ [1, 26, 28, 40] یا باگهای جمعآوریشده از NVD [20] است. بهطور ویژه، تمام باگهای UAF کشفشده توسط فازرهای جهتدار را در این معیار ارزیابی گنجاندهایم.
- گزارش باگ اطلاعات دقیقی، مانند نسخه دارای باگ و رهگیری پشته (Stack Trace)، ارائه میدهد؛ بهگونهای که بتوانیم مکانهای هدف (Target Locations) را برای فازرها شناسایی کنیم.
در مجموع، ما ۱۳ آسیبپذیری UAF شناخته شده (۲ مورد از فازرهای جهتدار) را در ۱۱ برنامه واقعی C در اختیار داریم که اندازه آنها از ۲۶ کیلوبایت تا ۳.۸ مگابایت متغیر است. علاوه بر این، برنامههای انتخابشده حوزههای مختلفی را شامل میشوند؛ از پردازش تصویر گرفته تا بایگانی دادهها، پردازش ویدئو و توسعه وب. بنابراین، معیار ارزیابی ما نماینده طیف متفاوتی از آسیبپذیریهای UAF در برنامههای واقعی است. جدول ۳ معیار ارزیابی مورد استفاده ما را ارائه میکند.
پیکربندیهای ارزیابی (Evaluation Configurations). ما توصیههای ارائه شده برای ارزیابی فازینگ [43] را دنبال میکنیم و برای تمام آزمایشها از پیکربندیهای یکسان فازینگ و منابع سختافزاری یکسان استفاده میکنیم.
آزمایشها ۱۰ بار تکرار میشوند و بودجه زمانی آنها با توجه به برنامه تحت آزمون (PUT) تعیین میشود. به عنوان بذر ورودی، یا از یک فایل خالی استفاده میکنیم یا از فایلهای معتبر موجود که توسط توسعهدهندگان ارائه شدهاند. ما از هیچ فرهنگ لغات توکن (Token Dictionary) استفاده نمیکنیم. تمام آزمایشها روی یک پردازنده Intel Xeon CPU E3-1505M v6 با فرکانس 3.00GHz، با 32GB حافظه RAM و سیستمعامل Ubuntu 16.04 نسخه 64 بیتی انجام شدهاند.
۶.۳ پرسش پژوهشی اول: قابلیت بازتولید باگهای UAF (UAF Bug-reproducing Ability)
پروتکل (Protocol). ما فازرهای مختلف را روی ۱۳ آسیبپذیری UAF خود، با استفاده از معیار زمان تا آشکارسازی (Time-to-Exposure یا TTE)، یعنی مدتزمان سپریشده تا مشاهده نخستین ورودی ایجادکننده باگ، و همچنین تعداد اجراهای موفق که در آنها یک فازر قادر به فعالسازی باگ است، با یکدیگر مقایسه میکنیم. در صورتی که یک فازر نتواند باگ را در محدوده زمانی تعیینشده (Time Budget) شناسایی کند، مقدار TTE آن اجرا برابر با همان محدوده زمانی در نظر گرفته میشود. مطابق با پژوهشهای پیشین [25, 28]، برای ارزیابی میزان اطمینان از اینکه یک ابزار عملکرد بهتری نسبت به ابزار دیگر دارد، از معیار آماره وارگا–دلینی (Vargha-Delaney statistic یا Â12) [69][1] استفاده میکنیم. پوشش کد (Code Coverage) برای فازرهای هدایتشده (Directed Fuzzers) مرتبط نیست.
نتایج (Results). شکل ۷ نمایی یکپارچه از نتایج ارائه میدهد؛ این نتایج شامل تعداد کل اجراهای موفق و TTE هستند. برای هر نمونه، میانگین TTE مشاهدهشده در ۱۰ اجرا را با μTTE نشان میدهیم. پیوست A اطلاعات تکمیلی مربوط به آمارههای یکپارچه وارگا–دلینی را ارائه میکند (جدول ۴).
شکل ۷ (و جدول ۴) نشان میدهند که UAFUZZ هم از نظر تعداد کل اجراهای موفق و هم از نظر TTE، بهوضوح عملکرد بهتری نسبت به سایر فازرها دارد.UAFUZZ در مقایسه با دومین فازر برتر، یعنی AFLGOB، در مجموع ۳۴٪ اجرای موفق بیشتری دارد و این بهبود در برخی موارد به ۳۰۰٪ نیز میرسد. از نظر TTE نیز UAFUZZ نسبت به AFLGOB، در مجموع ۲٫۰ برابر، از نظر میانگین ۶٫۷ برابر و در بیشترین مقدار ۴۳ برابر بهتر عمل میکند.
در برخی موارد خاص، UAFUZZ در مقایسه با AFLGOB تقریباً ۱۰٬۰۰۰ ثانیه از TTE میکاهد یا تعداد موفقیتها را از ۰ از ۱۰ به ۷ از ۱۰ افزایش میدهد. همچنین مقدار Â12 مربوط به UAFUZZ در مقایسه با سایر فازرها، بهطور معناداری بالاتر از مقدار ۰٫۷۱، که به عنوان اندازه اثر بزرگ متعارف در نظر گرفته میشود [69]، قرار دارد؛ همانطور که در جدول ۴ نشان داده شده است. در مقایسه با دومین فازر برتر، یعنی AFLGOB، این مقدار برای میانگین ۰٫۷۸، میانه ۰٫۸۰ و حداقل ۰٫۵۲ است.
UAFUZZ از نظر بازتولید باگهای UAF، با سطح اطمینان بالا، به وضوح عملکرد بهتری نسبت به فازرهای هدایتشده پیشرفته موجود (State-of-the-Art) دارد. توجه داشته باشید که عملکرد AFLGOB و HAWKEYEB در مقایسه با همتایان اصلیشان در سطح کد منبع (Source-Level)، معرف عملکرد کلی آنهاست (رجوع کنید به پیوست B). [1] مقداری بین ۰ و ۱ است و هرچه این مقدار بیشتر باشد، عملکرد بهتر است. مقادیر بالاتر از ۰٫۷۱، که بهصورت متعارف بهعنوان اندازه اثر بزرگ (Large Effect Size) در نظر گرفته میشود، از اهمیت بالایی برخوردار هستند [69].
۶.۴ پرسش پژوهشی دوم: سربار UAF(UAF Overhead)
پروتکل (Protocol). ما به هر دو مورد زیر علاقهمند هستیم: (۱) سربار زمان ابزارگذاری (Instrumentation-Time Overhead) و (۲) سربار زمان اجرا (Runtime Overhead). برای مورد اول، مجموع زمان موردنیاز برای ابزارگذاری در UAFUZZ را محاسبه کرده و آن را با زمان ابزارگذاری AFLGO مقایسه میکنیم. برای مورد دوم، تعداد کل اجراها در هر ثانیه توسط UAFUZZ را محاسبه کرده و آن را با AFL-QEMU که بهعنوان خط مبنا (Baseline) در نظر گرفته شده است، مقایسه میکنیم.
نتایج (Results). نتایج تلفیقی و یکپارچه مربوط به هر دو نوع سربار، یعنی سربار زمان ابزارگذاری و سربار زمان اجرا، در شکل ۸ ارائه شدهاند. در این شکل، بهجای تعداد اجراها در هر ثانیه، تعداد کل اجراهای انجامشده در یک محدوده زمانی یکسان نمایش داده شده است. این شکل نشان میدهد که UAFUZZ در مرحله ابزارگذاری، یک مرتبه بزرگی (Order of Magnitude) سریعتر از فازر هدایتشده مبتنی بر کد منبع و پیشرفته موجود، یعنی AFLGO عمل میکند و از نظر تعداد کل اجراها در هر ثانیه نیز تقریباً عملکردی مشابه AFL-QEMU دارد. پیوست C شامل نتایج تکمیلی است که جزئیات زمان ابزارگذاری (شکل ۱۲) و آمار زمان اجرا (شکل ۱۴)، و همچنین زمان ابزارگذاری AFLGOB و HAWKEYEB (شکل ۱۳) را ارائه میکند.
UAFUZZ از زمان ابزارگذاری سبک و همچنین سربار زمان اجرای حداقلی برخوردار است.
۶.۵ پرسش پژوهشی سوم: بررسی و دستهبندی UAF(UAF Triage)
پروتکل (Protocol). ما سه معیار را در نظر میگیریم: تعداد کل ورودیهای بررسی و دستهبندی (Triaging Inputs)، نرخ ورودیهای بررسی و دستهبندی (Triaging Inputs Rate یا TIR)، که نسبتِ بین تعداد کل ورودیهای تولید شده و ورودیهای ارسالشده به مرحله بررسی و دستهبندی است، و مجموع زمان بررسی و دستهبندی (Total Triaging Time)، یعنی زمانی که در مرحله بررسی و دستهبندی صرف میشود. از آنجا که فازرهای دیگر نمیتوانند در طول فرایند فازینگ، ورودیهایی را که به اهداف میرسند شناسایی کنند، ما بهصورت محافظهکارانه تمام ورودیهای تولیدشده توسط این فازرها را در مرحله بررسی و دستهبندی باگ تحلیل میکنیم (TIR = 1).
نتایج (Results). نتایج یکپارچه در شکل ۹ ارائه شدهاند و نتایج تفصیلی نیز در پیوست D، جدول ۶ و شکل ۱۵ آمدهاند.
- نرخ ورودیهای بررسی و دستهبندی (TIR) در UAFUZZ در مجموع ۹٫۲٪ است (میانگین: ۷٫۲۵٪، میانه: ۳٫۱۴٪، بهترین مقدار: ۰٫۲۴٪، بدترین مقدار: ۳۰٫۲۲٪)؛ این امر باعث میشود تا ۹۹٫۷۶٪ از ورودیهای اولیه (Input Seeds) که برای تأیید ارسال میشوند، حذف گردند. همچنین، این نرخ همواره کمتر از ۹٪ است، بهجز برای نمونه mjs.
- شکل ۱۵ نشان میدهد که UAFUZZ کمترین میزان زمان را در مرحله بررسی و دستهبندی باگ صرف میکند؛ یعنی در مجموع ۷۵ ثانیه (میانگین: ۶ ثانیه، حداقل: ۱ ثانیه، حداکثر: ۲۴ ثانیه) که در مقایسه با AFLGOB به افزایش سرعت کلی ۱۷ برابری (17×) منجر میشود (حداکثر: ۱۳۰ برابر، میانگین: ۳۹ برابر).
UAFUZZ در مرحله پسپردازش (Post-Processing) بخش بزرگی از ورودیهای نیازمند بررسی و دستهبندی را، یعنی بیش از ۹۰٪ آنها را، کاهش میدهد. در نتیجه، UAFUZZ تنها چند ثانیه را در این مرحله صرف میکند و در مقایسه با فازرهای هدایتشده استاندارد، به بهبودی در حد یک مرتبه بزرگی (Order of Magnitude) دست مییابد.
۶.۶ پرسش پژوهشی چهارم: سهم هر یک از مؤلفهها (Individual Contribution)
پروتکل (Protocol). ما چهار نسخه متفاوت از نمونه اولیه خود را با یکدیگر مقایسه میکنیم که یک طیف پیوسته از AFLGO تا UAFUZZ را نشان میدهند:
- AFLGOB پایه که همان AFLGO پایه است؛
- AFLGOB–ss که معیار انتخاب بذر یا ورودی اولیه (Seed Selection Metric) ما را به AFLGOB اضافه میکند؛
- AFLGOB–ds که فاصله تابع مبتنی بر UAF را به AFLGOB–ss اضافه میکند؛
- در نهایت،UAFUZZ که زمانبندی توان اختصاصی ما (Dedicated Power Schedule) را به AFLGOB–ds اضافه میکند.
ما همان معیارهای مورد استفاده در پرسش پژوهشی اول (RQ1)، یعنی تعداد اجراهای موفق (Number of Success Runs)، زمان تا آشکارسازی (TTE) و آماره وارگا–دلینی (Vargha-Delaney) را در نظر میگیریم. هدف ما این است که ارزیابی کنیم آیا این بهبودهای فنی واقعاً به بهبود عملکرد فازینگ منجر میشوند یا خیر.
نتایج (Results). نتایج یکپارچه مربوط به تعداد اجراهای موفق و TTE در شکل ۱۰ ارائه شدهاند. پیوست E شامل نتایج تفصیلی بههمراه معیار وارگا–دلینی است (جدول ۷).
همانطور که در شکل ۱۰ خلاصه شده است، مشاهده میکنیم که هر مؤلفه جدید، هم TTE و هم تعداد اجراهای موفق را بهبود میبخشد و در نتیجه، واقعاً به بهبود عملکرد فازینگ منجر میشود. نتایج تفصیلی ارائهشده در جدول ۷ و مقادیر Â12 نیز همین روند روشن را نشان میدهند.
محاسبه فاصله مبتنی بر UAF، زمانبندی توان (Power Scheduling) و راهبرد اکتشافی انتخاب بذر یا ورودی اولیه (Seed Selection Heuristic)، هر یک بهطور مستقل در بهبود عملکرد فازینگ نقش دارند و ترکیب آنها به بهبودهای بیشتری منجر میشود؛ که نشاندهنده کارآمدی و مکملبودن این مؤلفهها نسبت به یکدیگر است.
۶.۷ آزمون وصله و آسیبپذیریهای روز-صفر (Patch Testing & Zero-days)
آزمون وصله (Patch Testing). ایده این است که از ردیابیهای پشته (Stack Traces) مربوط به باگهای UAF شناختهشده، برای هدایت آزمون روی نسخه وصله شده برنامه تحت آزمون (PUT) استفاده کنیم؛ بهجای نسخه دارای باگ که در بازتولید باگ مورد استفاده قرار میگیرد. مزیت این رویکرد از دیدگاه شکار باگ (Bug Hunting) [17] آن است که از یک سو، امکان تلاش برای یافتن وصلههای دارای نقص یا ناقص را فراهم میکند و از سوی دیگر، آزمون را بر بخشهایی از کد که از پیش مستعد آسیبپذیری هستند متمرکز میسازد؛ در نتیجه، ممکن است باگهایی نیز کشف شوند که ارتباطی با خود وصله ندارند.
روش اجرا (How to). ما از رویههای مورد استفاده در شکار باگ[17] پیروی میکنیم. با شروع از باگهای UAF اخیرِ افشاشده بهصورت عمومی در برنامههای متنباز، بهصورت دستی آدرس دستورهای فراخوانی (Call Instructions) مرتبط را در ردیابیهای پشته باگهای گزارششده شناسایی میکنیم؛ زیرا کد برنامهها در طول زمان دستخوش تغییر شده است. تمرکز اصلی ما بر ۳ برنامه پرکاربرد است که بهخوبی توسط فازرها آزمایش شده و توسط توسعهدهندگان آنها نیز بهطور مستمر نگهداری میشوند؛ یعنی GNU patch، GPAC و Perl 5 که در مجموع شامل ۷۳۷ هزار خط کد C و ۵ ردیابی باگ شناختهشده هستند. همچنین ۳ کد دیگر یعنی MuPDF، Boolector و fontforge را نیز در نظر میگیریم (۱٬۱۹۶ هزار خط کد اضافی).
نتایج (Results). در مجموع، UAFUZZ تعداد ۳۰ باگ جدید را کشف و گزارش کرده است که شامل ۱۱ باگ جدید UAF و ۷ CVE جدید است (جزئیات در پیوست F، جدول ۸ ارائه شده است). تا زمان نگارش مقاله، ۱۷ باگ توسط فروشندگان (Vendors) برطرف شدهاند. نکته جالب این است که باگهای کشفشده در GNU patch (پیوست F) و GPAC، در واقع ناشی از وصلههای دارای نقص (Buggy Patches) بوده است.
UAFUZZ در محیط آزمون وصله (Patch Testing) اثربخشی خود را به اثبات رسانده و توانسته است در ۶ برنامه پرکاربرد، ۳۰ باگ جدید، از جمله هفت CVE جدید، را کشف کند.
۶.۸ تهدیدهای اعتبار (Threats to Validity)
پیادهسازی (Implementation). نمونه اولیه ما به عنوان بخشی از چارچوب تحلیل کد در سطح باینری (Binary-Level Code Analysis) یعنی BINSEC [34, 35] پیادهسازی شده است؛ چارچوبی که کارایی و استحکام آن در مطالعات گسترده پیشین، هم روی کدهای خصمانه (Adversarial Code) و هم روی کدهای مدیریتشده (Managed Code) [22,33,63]، به اثبات رسیده است. این نمونه اولیه همچنین بر مبنای فازر پرکاربرد AFL-QEMU توسعه داده شده است.
اثربخشی و صحت UAFUZZ با استفاده از چندین رهگیری باگ (Bug Trace) مربوط به برنامههای واقعی، و همچنین نمونههای کوچکی از مجموعه آزمون Juliet ارزیابی شده است. تمامی باگهای UAF گزارششده نیز بهصورت دستی بررسی و تأیید شدهاند.
معیار ارزیابی (Benchmark). معیار ارزیابی ما بر اساس هر دو مورد کدهای واقعی و باگهای واقعی ساخته شده است و چندین باگ را که با استفاده از تکنیکهای جدید فازینگ روی کدهای متنباز شناختهشده کشف شدهاند، دربر میگیرد؛ از جمله تمام باگهای UAF کشفشده توسط فازرهای هدایتشده.
رقبا (Competitors). ما بهترین تکنیکهای روز در حوزه فازینگ هدایتشده (Directed Fuzzing)، یعنی AFLGO [25] و HAWKEYE [28]، را در نظر میگیریم. متأسفانه،HAWKEYE در دسترس نیست و AFLGO نیز فقط روی کد منبع (Source Code) کار میکند. بنابراین، این فناوریها را در چارچوب اختصاصی خودمان مجدداً پیادهسازی (Re-implement) میکنیم. ما تا حد امکان، اطلاعات موجود (مقاله و کد منبع، در صورت وجود) را با دقت دنبال کردهایم و تلاش خود را برای دستیابی به پیادهسازیهای دقیق به کار گرفتهایم. هر دوی این پیادهسازیها روی برنامههای واقعی و نمونههای کوچک بررسی شدهاند و مقایسه میان AFLGO اصلی و AFLGOB پیادهسازیشده توسط خودمان (پیوست B) نیز نتایج قانعکننده و قطعی ارائه میدهد.
۷. کارهای مرتبط (Related Work)
فازینگ جعبهسیاه هدایت شده (Directed Greybox Fuzzing). AFLGO [25] و HAWKEYE [28] پیشتر مورد بحث قرار گرفتهاند. LOLLY [49] یک ابزارگذاری سبک (Lightweight Instrumentation) برای اندازهگیری پوشش توالی بلوکهای پایه (Sequence Basic Block Coverage) ورودیها ارائه میکند؛ با این حال، این روش در ازای آن، سربار زمان اجرای زیادی به همراه دارد.
SEEDEDFUZZ [71] با هدف تولید مجموعهای از ورودیهای اولیه (Initial Seeds) تلاش میکند عملکرد فازینگ هدایتشده را بهبود دهد. SEMFUZZ [74] از متون مرتبط با آسیبپذیری، مانند گزارشهای CVE، برای هدایت فرایند فازینگ بهره میگیرد. 1DVUL [59] آسیبپذیریهای یکروزه (1-Day Vulnerabilities) را از طریق وصلههای باینری (Binary Patches) کشف میکند. UAFUZZ نخستین فازر هدایتشدهای است که بهطور اختصاصی برای باگهای UAF طراحی شده است و یکی از معدود فازرها [59] است که قادر به کار با کد باینری است. در ادامه، ترجمه را با حفظ ساختار، مفاهیم و جزئیات فنی متن ارائه میکنم:
فازینگ جعبهخاکستری مبتنی بر پوشش (Coverage-based Greybox Fuzzing). AFL [1] نخستین فازر جعبهخاکستری هدایت شده مبتنی بر پوشش (Coverage-Guided Greybox Fuzzer) است. در چند سال گذشته، تلاشهای قابلتوجهی برای بهبود آن انجام شده است [26، 40، 46]. همچنین، تلاشهای بسیاری با موفقیت در جهت ترکیب فازینگ با رویکردهای دیگر، مانند تحلیل ایستا (Static Analysis) [40, 47]، تحلیل پویای آلودگی (Dynamic Taint Analysis) [29, 30, 62]، اجرای نمادین (Symbolic Execution) [58, 67, 76] یا یادگیری ماشین (Machine Learning) [41, 66] صورت گرفته است. اخیراً، UAFL [70] – که یک تلاش پژوهشی مستقل دیگر برای حل همین مسئله است — فازینگ هدایتشده مبتنی بر پوشش را برای شناسایی UAFها تخصصی کرده است؛ به این صورت که ابتدا توالیهای عملیاتی (Operation Sequences) را که بهطور بالقوه میتوانند یک ویژگی نوعحالت (Typestate Property) را نقض کنند، پیدا کرده و سپس فرایند فازینگ را برای ایجاد نقض این ویژگی هدایت میکند. بااینحال، این رویکرد بهشدت به تحلیل ایستای کد منبع وابسته است و بنابراین در سطح باینری (Binary-Level) قابل استفاده نیست. تکنیک ما نسبت به تمامی این بهبودها متعامد (Orthogonal) است و میتوان همه آنها را بدون تغییر، در UAFUZZ نیز به کار گرفت.
تشخیصUAF . تشخیص دقیق و ایستای UAF دشوار است. GUEB [12] تنها تحلیلگر ایستای UAF در سطح باینری است. این تکنیک را میتوان با اجرای نمادین پویا (Dynamic Symbolic Execution) ترکیب کرد تا ورودیهای اثبات مفهوم (PoC) تولید شوند [38]؛ بااینحال، این کار با مشکلاتی از نظر مقیاسپذیری (Scalability) مواجه است. از سوی دیگر، چندین تشخیصدهنده ایستای UAF در سطح کد منبع وجود دارند که بر پایه تفسیر انتزاعی (Abstract Interpretation) [32]، تحلیل اشارهگر (Pointer Analysis) [72]، تطبیق الگو (Pattern Matching) [57]، بررسی مدل (Model Checking) [44] یا تحلیل اشارهگر مبتنی بر تقاضا (Demand-Driven Pointer Analysis) [68] میباشند. یک ضعف مشترک میان تمامی تشخیصدهندههای ایستا این است که قادر به استنتاج ورودی ایجادکننده باگ نیستند؛ بلکه در عوض، نبود چنین ورودی را اثبات میکنند. تشخیصدهندههای پویای UAF عمدتاً به ابزارگذاری سنگین (Heavyweight Instrumentation) متکی هستند [9، 27، 55] و در نتیجه، سربار زمان اجرای زیادی ایجاد میکنند؛ این مسئله در مورد برنامههای متنبسته (Closed-Source Programs) حتی شدیدتر است. ASan [65] ابزارگذاری سبکی انجام میدهد، اما فقط در سطح کد منبع.
معیار ارزیابی فازینگ UAF(UAF Fuzzing Benchmark). اگرچه مجموعه آزمون Juliet(CWE-415, CWE-416)[1] [56] شامل برنامههایی با اندازه بسیار کوچک است، معیارهای ارزیابی رایج فازینگ [7، 11، 16، 36، 64] نیز تنها تعداد بسیار کمی باگ UAF را دربر میگیرند. علاوه بر این، بسیاری از این معیارهای ارزیابی یا شامل باگهای مصنوعی [7, 16, 36, 64] هستند یا شامل برنامههای مصنوعی [56]. با ادغام معیار ارزیابی ما برای UAFهای شناختهشده با باگهای جدید UAF کشفشده توسط خودمان، بزرگترین معیار ارزیابی فازینگ اختصاصیافته به UAF را ارائه میکنیم که شامل ۱۷ کد واقعی و ۳۰ باگ واقعی است.
۸. نتیجهگیری (Conclusion)
UAFUZZ نخستین رویکرد فازینگ جعبهخاکستری هدایت شده (Directed Greybox Fuzzing) است که بهطور اختصاصی برای تشخیص آسیبپذیریهای UAF در سطح باینری طراحی شده و تنها با در اختیار داشتن رهگیری پشته باگ (Bug Stack Trace) عمل میکند. UAFUZZ از فازرهای هدایتشده موجود، هم از نظر زمان تا آشکارسازی باگ (Time-to-Bug Exposure) و هم از نظر تعداد اجراهای موفق عملکرد بهتری دارد. اثربخشی UAFUZZ هم در بازتولید باگ و هم در آزمون وصله (Patch Testing) به اثبات رسیده است. ما کد منبع UAFUZZ و معیار ارزیابی فازینگ UAF را در نشانی زیر منتشر میکنیم:
[1] Juliet عمدتاً برای ارزیابی ابزارهای تحلیل ایستای کدهای C++ /C استفاده میشود.
https://github.com/strongcourage/uafuzz
https://github.com/strongcourage/uafbench
منابع
[1] Afl. http://lcamtuf.coredump.cx/afl/, 2020.
[2] Aflgo. https://github.com/aflgo/aflgo, 2020.
[3] Aflgo’s issues. https://github.com/aflgo/aflgo/issues, 2020.
[4] Binsec. https://binsec.github.io/, 2020.
[5] Circumventing fuzzing roadblocks with compiler transformations. https://lafintel.wordpress.com/2016/08/15/circumventing-fuzzing-roadblocks-with-compiler-transformations/,2020.
[6] Cve-2018-6952. https://savannah.gnu.org/bugs/index.php?53133, 2020.
[7] Darpa cgc corpus. http://www.lungetech.com/2017/04/24/cgc-corpus/, 2020.
[8] Double free in gnu patch. https://savannah.gnu.org/bugs/index.php?56683, 2020.
[9] Dr.memory. https://www.drmemory.org/, 2020.
[10] Gnu patch. https://savannah.gnu.org/projects/patch/, 2020.
[11] Google fuzzer testsuite. https://github.com/google/fuzzer-test-suite, 2020.
[12] Gueb: Static analyzer detecting use-after-free on binary. https://github.com/montyly/gueb, 2020.
[13] Libfuzzer. https://llvm.org/docs/LibFuzzer.html, 2020.
[14] OSS-Fuzz: Continuous Fuzzing Framework for Open-Source Projects. https://github.com/google/oss-fuzz/, 2020.
[15] Oss-fuzz: Five months later, and rewarding projects. https://opensource.googleblog.com/2017/05/oss-fuzz-five-months-later-and.html, 2020.
[16] Rode0day. https://rode0day.mit.edu/, 2020.
[17] Sockpuppet: A walkthrough of a kernel exploit for ios 12.4. https://googleprojectzero.blogspot.com/2019/12/sockpuppet-walkthrough-of-kernel.html, 2020.
[18] Uaf fuzzing benchmark. https://github.com/strongcourage/uafbench, 2020.
[19] Uafuzz. https://github.com/strongcourage/uafuzz, 2020.
[20] Us national vulnerability database. https://nvd.nist.gov/vuln/search, 2020.
[21] Cornelius Aschermann, Sergej Schumilo, Tim Blazytko, Robert Gaw-lik, and Thorsten Holz. Redqueen: Fuzzing with input-to-state correspondence. In 26th Annual Network and Distributed System Security Symposium, NDSS, 2019.
[22] Sébastien Bardin, Robin David, and Jean-Yves Marion. Backward-bounded DSE: targeting infeasibility questions on obfuscated codes.2017.
[23] Tim Blazytko, Cornelius Aschermann, Moritz Schlögel, Ali Abbasi, Sergej Schumilo, Simon Wörner, and Thorsten Holz. Grimoire: synthesizing structure while fuzzing. In USENIX Security Symposium (USENIX Security 19), 2019.
[24] Marcel Böhme. Assurance in software testing: A roadmap. In Proceedings of the 41st International Conference on Software Engineering: New Ideas and Emerging Results, ICSE-NIER ’19, 2019.
[25] Marcel Böhme, Van-Thuan Pham, Manh-Dung Nguyen, and Abhik Roychoudhury. Directed greybox fuzzing. In Proceedings of the 2017 ACM SIGSAC Conference on Computer and Communications Security (CCS’17), 2017.
[26] Marcel Böhme, Van-Thuan Pham, and Abhik Roychoudhury. Coverage-based greybox fuzzing as markov chain. In Proceedings of the 2016 ACM SIGSAC Conference on Computer and Communications Security, 2016.
[27] Juan Caballero, Gustavo Grieco, Mark Marron, and Antonio Nappa. Undangle: early detection of dangling pointers in use-after-free and double-free vulnerabilities. In Proceedings of the 2012 International Symposium on Software Testing and Analysis, 2012.
[28] Hongxu Chen, Yinxing Xue, Yuekang Li, Bihuan Chen, Xiaofei Xie, Xiuheng Wu, and Yang Liu. Hawkeye: towards a desired directed grey-box fuzzer. In Proceedings of the 2018 ACM SIGSAC Conference on Computer and Communications Security, 2018.
[29] P. Chen and H. Chen. Angora: Efficient fuzzing by principled search. In 2018 IEEE Symposium on Security and Privacy (SP), 2018.
[30] Peng Chen, Jianzhong Liu, and Hao Chen. Matryoshka: fuzzing deeply nested branches. arXiv preprint arXiv:1905.12228, 2019.
[31] Maria Christakis, Peter Müller, and Valentin Wüstholz. Guiding dynamic symbolic execution toward unverified program executions. In Proceedings of the 38th International Conference on Software Engineering, 2016.
[32] Pascal Cuoq, Florent Kirchner, Nikolai Kosmatov, Virgile Prevosto, Julien Signoles, and Boris Yakobowski. Frama-c. In International Conference on Software Engineering and Formal Methods, 2012.
[33] Robin David, Sébastien Bardin, Josselin Feist, Laurent Mounier, MarieLaure Potet, Thanh Dinh Ta, and Jean-Yves Marion. Specification of concretization and symbolization policies in symbolic execution. In ISSTA, 2016.
[34] Robin David, Sébastien Bardin, Thanh Dinh Ta, Laurent Mounier, Josselin Feist, Marie-Laure Potet, and Jean-Yves Marion. Binsec/se: A dynamic symbolic execution toolkit for binary-level analysis. In Software Analysis, Evolution, and Reengineering (SANER), 2016 IEEE 23rd International Conference on, 2016.
[35] Adel Djoudi and Sébastien Bardin. Binsec: Binary code analysis with low-level regions. In TACAS, 2015.
[36] Brendan Dolan-Gavitt, Patrick Hulin, Engin Kirda, Tim Leek, Andrea Mambretti, Wil Robertson, Frederick Ulrich, and Ryan Whelan. Lava: Large-scale automated vulnerability addition. In Security and Privacy (SP), 2016 IEEE Symposium on, 2016.
[37] Andrew Fasano, Tim Leek, Brendan Dolan-Gavitt, and Josh Bundt. The rode0day to less-buggy programs. IEEE Security & Privacy, 2019.
[38] Josselin Feist, Laurent Mounier, Sébastien Bardin, Robin David, and Marie-Laure Potet. Finding the needle in the heap: combining static analysis and dynamic symbolic execution to trigger use-after-free. In Proceedings of the 6th Workshop on Software Security, Protection, and Reverse Engineering, 2016.
[39] Andrea Fioraldi, Daniele Cono D’Elia, and Emilio Coppa. WEIZZ: Automatic grey-box fuzzing for structured binary formats. In Proceedings of the 29th ACM SIGSOFT International Symposium on Software Testing and Analysis, ISSTA 2020, 2020.
[40] S. Gan, C. Zhang, X. Qin, X. Tu, K. Li, Z. Pei, and Z. Chen. Collafl:Path sensitive fuzzing. In 2018 IEEE Symposium on Security and Privacy (SP), 2018.
[41] Patrice Godefroid, Hila Peleg, and Rishabh Singh. Learn&fuzz: Machine learning for input fuzzing. In Proceedings of the 32nd IEEE/ACM International Conference on Automated Software Engineering, 2017
[42] Wei Jin and Alessandro Orso. Bugredux: reproducing field failures for in-house debugging. In 2012 34th International Conference on Software Engineering (ICSE), 2012.
[43] George Klees, Andrew Ruef, Benji Cooper, Shiyi Wei, and Michael Hicks. Evaluating fuzz testing. In Proceedings of the 2018 ACM SIGSAC Conference on Computer and Communications Security, 2018.
[44] Daniel Kroening and Michael Tautschnig. Cbmc–c bounded model checker. In International Conference on Tools and Algorithms for the Construction and Analysis of Systems, 2014.
[45] Byoungyoung Lee, Chengyu Song, Yeongjin Jang, Tielei Wang, Taesoo Kim, Long Lu, and Wenke Lee. Preventing use-after-free with dangling pointers nullification. In NDSS, 2015.
[46] Caroline Lemieux and Koushik Sen. Fairfuzz: A targeted mutation strategy for increasing greybox fuzz testing coverage. In Proceedings of the 33rd ACM/IEEE International Conference on Automated Software Engineering, 2018.
[47] Yuekang Li, Bihuan Chen, Mahinthan Chandramohan, Shang-Wei Lin, Yang Liu, and Alwen Tiu. Steelix: program-state based binary fuzzing. In Proceedings of the 2017 11th Joint Meeting on Foundations of Software Engineering, 2017.
[48] Yuekang Li, Yinxing Xue, Hongxu Chen, Xiuheng Wu, Cen Zhang, Xiaofei Xie, Haijun Wang, and Yang Liu. Cerebro: context-aware adaptive fuzzing for effective vulnerability detection. In Proceedings of the 2019 27th ACM Joint Meeting on European Software Engineering Conference and Symposium on the Foundations of Software Engineering, 2019.
[49] Hongliang Liang, Yini Zhang, Yue Yu, Zhuosi Xie, and Lin Jiang. Sequence coverage directed greybox fuzzing. In Proceedings of the 27th International Conference on Program Comprehension, 2019.
[50] Valentin Jean Marie Manès, HyungSeok Han, Choongwoo Han, Sang Kil Cha, Manuel Egele, Edward J Schwartz, and Maverick Woo. The art, science, and engineering of fuzzing: A survey. IEEE Transactions on Software Engineering, 2019.
[51] Paul Dan Marinescu and Cristian Cadar. Katch: high-coverage testing of software patches. In Proceedings of the 2013 9th Joint Meeting on Foundations of Software Engineering, 2013.
[52] Microsoft. Project springfield. https://www.microsoft.com/en-us/security-risk-detection/, 2020.
[53] Barton P Miller, Louis Fredriksen, and Bryan So. An empirical study of the reliability of unix utilities. Communications of the ACM, 1990.
[54] Dongliang Mu, Alejandro Cuevas, Limin Yang, Hang Hu, Xinyu Xing, Bing Mao, and Gang Wang. Understanding the reproducibility of crowd-reported security vulnerabilities. In 27th USENIX Security Symposium (USENIX Security 18), 2018.
[55] Nicholas Nethercote and Julian Seward. Valgrind: a framework for heavyweight dynamic binary instrumentation. In ACM Sigplan, 2007.
[56] NIST. Juliet test suite for c/c++. https://samate.nist.gov/SARD/testsuite.php, 2020.
[57] Mads Chr Olesen, René Rydhof Hansen, Julia L Lawall, and Nicolas Palix. Coccinelle: tool support for automated cert c secure coding standard certification. Science of Computer Programming, 2014.
[58] Hui Peng, Yan Shoshitaishvili, and Mathias Payer. T-fuzz: fuzzing by program transformation. In 2018 IEEE Symposium on Security and Privacy (SP), 2018.
[59] Jiaqi Peng, Feng Li, Bingchang Liu, Lili Xu, Binghong Liu, Kai Chen, and Wei Huo. 1dvul: Discovering 1-day vulnerabilities through binary patches. In 2019 49th Annual IEEE/IFIP International Conference on Dependable Systems and Networks (DSN), 2019.
[60] Van-Thuan Pham, Marcel Böhme, Andrew Edward Santosa, Alexandru Razvan Caciulescu, and Abhik Roychoudhury. Smart greybox fuzzing. IEEE Transactions on Software Engineering, 2019.
[61] Van-Thuan Pham, Wei Boon Ng, Konstantin Rubinov, and Abhik Roychoudhury. Hercules: Reproducing crashes in real-world application binaries. In 2015 IEEE/ACM 37th IEEE International Conference on Software Engineering, 2015.
[62] Sanjay Rawat, Vivek Jain, Ashish Kumar, Lucian Cojocar, Cristiano Giuffrida, and Herbert Bos. Vuzzer: Application-aware evolutionary fuzzing. In Proceedings of the Network and Distributed System Security Symposium (NDSS), 2017.
[63] Frédéric Recoules, Sébastien Bardin, Richard Bonichon Bonichon, Laurent Mounier, and Marie-Laure Potet. Get rid of inline assembly through verification-oriented lifting. In ASE, 2019.
[64] Subhajit Roy, Awanish Pandey, Brendan Dolan-Gavitt, and Yu Hu. Bug synthesis: Challenging bug-finding tools with deep faults. In Proceedings of the 2018 26th ACM Joint Meeting on European Software Engineering Conference and Symposium on the Foundations of Software Engineering, 2018.
[65] Konstantin Serebryany, Derek Bruening, Alexander Potapenko, and Dmitriy Vyukov. Addresssanitizer: A fast address sanity checker. In USENIX Annual Technical Conference, 2012.
[66] Dongdong She, Kexin Pei, Dave Epstein, Junfeng Yang, Baishakhi Ray, and Suman Jana. Neuzz: Efficient fuzzing with neural program learning. arXiv preprint arXiv:1807.05620, 2018.
[67] Nick Stephens, John Grosen, Christopher Salls, Andrew Dutcher,Ruoyu Wang, Jacopo Corbetta, Yan Shoshitaishvili, Christopher Kruegel, and Giovanni Vigna. Driller: Augmenting fuzzing through selective symbolic execution. In NDSS, 2016.
[68] Yulei Sui and Jingling Xue. On-demand strong update analysis via value-flow refinement. In Proceedings of the 2016 24th ACM SIGSOFT International Symposium on Foundations of Software Engineering, 2016.
[69] András Vargha and Harold D Delaney. A critique and improvement of the cl common language effect size statistics of mcgraw and wong. Journal of Educational and Behavioral Statistics, 2000.
[70] Haijun Wang, Xiaofei Xie, Yi Li, Cheng Wen, Yuekang Li, Yang Liu, Shengchao Qin, Hongxu Chen, and Yulei Sui. Typestate-guided fuzzer for discovering use-after-free vulnerabilities. In 42nd International Conference on Software Engineering, 2020.
[71] Weiguang Wang, Hao Sun, and Qingkai Zeng. Seededfuzz: Selecting and generating seeds for directed fuzzing. In 2016 10th International Symposium on Theoretical Aspects of Software Engineering (TASE),2016.
[72] Hua Yan, Yulei Sui, Shiping Chen, and Jingling Xue. Spatio-temporal context reduction: a pointer-analysis-based static approach for detecting use-after-free vulnerabilities. In 2018 IEEE/ACM 40th International Conference on Software Engineering (ICSE), 2018.
[73] Wei You, Xueqiang Wang, Shiqing Ma, Jianjun Huang, Xiangyu Zhang, XiaoFeng Wang, and Bin Liang. Profuzzer: On-the-fly input type probing for better zero-day vulnerability discovery. In IEEE Symposium on Security and Privacy (SP), 2019.
[74] Wei You, Peiyuan Zong, Kai Chen, XiaoFeng Wang, Xiaojing Liao, Pan Bian, and Bin Liang. Semfuzz: Semantics-based automatic generation of proof-of-concept exploits. In Proceedings of the 2017 ACM SIGSAC Conference on Computer and Communications Security, 2017.
[75] Yves Younan. Freesentry: protecting against use-after-free vulnerabilities due to dangling pointers. In NDSS, 2015.
[76] Insu Yun, Sangho Lee, Meng Xu, Yeongjin Jang, and Taesoo Kim. Qsym: A practical concolic execution engine tailored for hybrid fuzzing. In 27th USENIX Security Symposium (USENIX Security 18), 2018.
ضمایم
A. پرسش پژوهشی اول: قابلیت بازتولید باگهای UAF
در این بخش، نتایج تکمیلی مربوط به سؤال پژوهشی اول (RQ1)، شامل گزارشهای آزمایشی با جزئیات بیشتر، ارائه میشوند.
نتایج آزمایشها (Experimental Results):
جدول ۴ عملکرد فازینگ چهار فازر مبتنی بر باینری را در برابر معیار ارزیابی ما خلاصه میکند. این جدول تعداد کل مسیرهای پوششدادهشده، تعداد کل اجراهای موفق و همچنین مقادیر حداکثر، حداقل، میانگین و میانه معیار Factor و Â12 را ارائه میدهد. جدول ۵ فازر ما، یعنی UAFUZZ، را با چندین گونه از فازر هدایتشده AFLGO مقایسه میکند.
جدول ۴: خلاصه بازتولید باگ توسط UAFUZZ در مقایسه با سایر فازرها بر روی معیار ارزیابی فازینگ ما. نتایج دارای معناداری آماری با مقدار Â12 ≥ 0.71 بهصورت پررنگ نمایش داده شدهاند
جدول ۵: بازتولید باگ توسط AFLGO در برابر معیار ارزیابی ما، بهاستثنای CVE-2017-10686 که به دلیل مشکلات کامپایل AFLGOامکانپذیر نبود. اعداد نمایشدادهشده به رنگ قرمز، بهترین مقادیر μTTE میباشند
B. درباره پیادهسازیهای AFLGOB و HAWKEYEB
مقایسه AFLGOB و AFLGO مبتنی بر کد منبع. ما میخواهیم بررسی کنیم که پیادهسازی AFLGOB ما تا چه اندازه به AFLGO اصلی نزدیک است تا بتوانیم میزان اطمینانی را که به نتایج خود داریم ارزیابی کنیم؛ این بررسی را برای HAWKEYEB انجام نمیدهیم، زیرا HAWKEYE در دسترس نیست. همانطور که انتظار میرود، AFLGO عملکرد بهتری نسبت به AFLGOB و UAFUZZ دارد (شکل ۱۱، جدول ۵ در پیوست). این موضوع تا حد زیادی ناشی از سربار زمان اجرای شبیهسازی QEMU است که یک واقعیت بهخوبی مستندسازیشده محسوب میشود. بااینحال، نکته جالب این است که UAFUZZ در ۴ نمونه میتواند باگها را سریعتر از AFLGO پیدا کند که کارایی آن را نشان میدهد. نکته جالبتر این است که شکل ۱۱ نشان میدهد پس از در نظر گرفتن سربار شبیهسازی[1] – که در نتیجه آن AFLGOF، یعنی عملکرد مورد انتظار AFLGO در سطح باینری، به دست میآید – عملکرد AFLGOB با AFLGOF همراستا است و حتی TTE بهتری نیز نشان میدهد. UAFUZZ حتی بهطور معناداری عملکرد بهتری نسبت به AFLGOF دارد.
[1] برای هر نمونه، یک ضریب سربار (Overhead Factor) به نام f را با مقایسه تعداد اجراها در هر ثانیه در دو حالت AFL و AFL-QEMU برآورد میکنیم؛ سپس زمان محاسباتی AFLGO را در f ضرب میکنیم. مقدار f در نمونههای ما از ۲٫۰۵ تا ۲۲٫۵ متغیر است.
عملکرد AFLGOB پس از در نظر گرفتن سربار QEMU، با عملکرد AFLGO اصلی همراستا است و امکان مقایسهای منصفانه با UAFUZZ را فراهم میکند. بااینحال، UAFUZZ در مقایسه با فازر هدایتشده AFLGO که مبتنی بر کد منبع است، همچنان عملکرد نسبتاً خوبی در شناسایی UAFها دارد که نشاندهنده مزیت سازوکارهای فازینگ ابتکاری ما است.
در خصوص عملکرد HAWKEYEB در پرسش پژوهشی اول (RQ1). عملکرد HAWKEYEB در بخش ۶.۳ بهطور قابلتوجهی ضعیفتر از AFLGOB و UAFUZZ است. ما نمیتوانیم HAWKEYEB را با HAWKEYE مقایسه کنیم، زیرا HAWKEYE در دسترس نیست. بااینحال، این موضوع را بررسی کردیم و دریافتیم که دلیل اصلی آن، سربار زمان اجرای زیادی است که صرف محاسبه معیار شباهت به هدف (Target Similarity Metric) میشود.
در واقع، مطابق با مقاله اصلی HAWKEYE [28]، این محاسبه شامل محاسباتی درجه دوم (Quadratic) نسبت به تعداد کل توابع موجود در کد تحت آزمون است. در نمونههای ما، این تعداد بهسرعت قابلتوجه میشود و به ۷۷۲ تابع میرسد؛ در حالی که تعداد اهداف (Targets) در UAFUZZ همچنان کم باقی میماند و حداکثر به ۲۸ هدف میرسد.
برای مثال:
- CVE-2017-10686: تعداد ۷۷۲ تابع در برابر ۱۰ هدف
- gifsicle-issue-122: تعداد ۵۱۶ تابع در برابر ۱۱ هدف
- mjs-issue-78: تعداد ۴۵۰ تابع در برابر ۱۹ هدف
بنابراین، میتوان نتیجه گرفت که عملکرد HAWKEYEB روی نمونههای ما با آنچه از الگوریتم HAWKEYE انتظار میرود، همراستا است.
C. پرسش پژوهشی دوم: سربار UAF
دادههای تکمیلی (Additional Data). ابتدا، نتایج تکمیلی مربوط به پرسش پژوهشی دوم (RQ2) را ارائه میکنیم. شکلهای ۱۲ و ۱۳ بهترتیب میانگین زمان ابزارگذاری را بین UAFUZZ و فازر هدایتشده مبتنی بر کد منبع، یعنی AFLGO، و همچنین بین UAFUZZ و دو فازر هدایتشده مبتنی بر باینری، یعنی AFLGOB و HAWKEYEB، مقایسه میکنند. شکل ۱۴ تعداد کل اجراهای انجامشده توسط AFL-QEMU و UAFUZZ را برای هر نمونه در معیار ارزیابی ما نشان میدهد.
نتایج تفصیلی (Detailed Results). اکنون نتایج آزمایشهای مربوط به سربار (Overhead) را با جزئیات بیشتری نسبت به آنچه در بخش ۶.۴ ارائه شد، بررسی میکنیم.
- شکلهای ۸ و ۱۲ نشان میدهند که UAFUZZ در مرحله ابزارگذاری (Instrumentation)، یک مرتبه بزرگی سریعتر از فازر هدایتشده مبتنی بر کد منبع و پیشرفته موجود (State-of-the-Art) یعنی AFLGO است؛ بهطور کلی، ۱۴٫۷ برابر سریعتر عمل میکند. برای مثال، UAFUZZ تنها ۲۳ ثانیه را صرف پردازش برنامه بزرگ readelf از Binutils میکند؛ یعنی ۶۴ برابر زمان کمتر نسبت به AFLGO.
- شکلهای ۸ و ۱۴ نشان میدهند که UAFUZZ تقریباً همان تعداد کل اجراها در هر ثانیه را نسبت به AFL-QEMU دارد (۴٪ کمتر در مجموع و ۱۲٪ کمتر بهطور میانگین)، که نشان میدهد سربار آن ناچیز است.
- شکل ۱۳ نشان میدهد که HAWKEYEB در برخی موارد بهطور معناداری کندتر از UAFUZZ است (۲ برابر). این موضوع عمدتاً به هزینه محاسبه بستار ردیابی توابع هدف (Target Function Trace Closure) در نمونههای بزرگ دارای تعداد زیادی تابع مربوط میشود (رجوع کنید به بخش ۶.۳).
D. پرسش پژوهشی سوم: بررسی و دستهبندی UAF
نتایج تکمیلی مربوط به پرسش پژوهشی سوم (RQ3) را ارائه میکنیم. شکل ۱۵ و جدول ۶، میانگین زمان بررسی و دستهبندی و تعداد ورودیهای بررسی و دستهبندی را برای ۴ فازر در برابر معیار ارزیابی ما نشان میدهند؛ این موارد شامل مقادیر TIR برای UAFUZZ نیز هستند.
E. پرسش پژوهشی چهارم: سهم هر مؤلفه
نتایج تکمیلی مربوط به سؤال پژوهشی چهارم (RQ4) را ارائه میکنیم. جدول ۷ عملکرد فازینگ دو گونه مبتنی بر AFLGOB، یعنی AFLGOB–ss و AFLGOB–ds، را در مقایسه با AFLGOB و ابزار ما، UAFUZZ، بر روی معیار ارزیابی ما نشان میدهد.
جدول ۶: میانگین تعداد ورودیهای بررسی و دستهبندیشده توسط ۴ فازر در برابر نمونههای تحت آزمون ما. برای UAFUZZ، مقادیر TIR داخل پرانتز آمدهاند
جدول ۷: بازتولید باگ توسط ۴ فازر در برابر معیار ارزیابی ما. Â12A و Â12U بهترتیب مقادیر Vargha-Delaney برای AFLGOB و UAFUZZ را نشان میدهند. نتایج دارای معناداری آماری برای Â12 (برای مثال، Â12A ≤ ۰٫۲۹ یا Â12U ≥ ۰٫۷۱) بهصورت پررنگ نمایش داده شدهاند
F. آزمون وصله و آسیبپذیریهای روز-صفر (Patch Testing & Zero-days)
نتایج تکمیلی مربوط به آزمون وصله (جدول ۸) و همچنین بحثی تفصیلی درباره وصله معیوب GNU Patch ارائه میکنیم.
نمای نزدیک: وصله معیوب GNU Patch. ما از CVE-2018-6952 [6] برای نشان دادن اثربخشی UAFUZZ در آشکارسازی آسیبپذیریهای UAF ناشناخته استفاده میکنیم. GNU patch [10] یک فایل وصله را که شامل فهرستی از تفاوتهاست دریافت کرده و آنها را روی فایل اصلی اعمال میکند. فهرست ۳ قطعهکدی از CVE-2018-6952 را نشان میدهد که یک آزادسازی دوباره (Double Free) در آخرین نسخه 2.7.6 از GNU patch است.
نکته جالب این است که UAFUZZ با استفاده از ردیابی پشته این CVE، همانطور که در شکل ۱۶ نشان داده شده است، با موفقیت یک رفع ناقص باگ (Incomplete Bug Fix) را با شناسه CVE-2019-20633 در آخرین کامیت 76e7758 کشف کرد؛ با تفاوت جزئی در ردیابی پشته باگ، یعنی فراخوانی ()savebuf در ()another_hunk.
از نظر فنی، GNU patch یک فایل وصله ورودی دریافت میکند که شامل چندین بخش (Hunk) است (خط ۳). این بخشها با استفاده از نویسههای ویژه بهعنوان جداکننده، از طریق *buf در حالت switch (خط ۱۴)، به چند رشته تقسیم میشوند. سپس GNU patch هر رشته ذخیرهشده در p_line را که بهصورت پویا با استفاده از ()malloc در ()savebuf (خط ۲۶) در حافظه تخصیص داده شده است، میخواند و تجزیه میکند تا زمانی که آخرین خط این بخش پردازش شود. در غیر این صورت، GNU patch جدیدترین رشته پردازششده را با استفاده از ()free آزاد میکند (خط ۱۰). باگ گزارششده توسط ما و CVE-2018-6952، رویدادهای آزادسازی (Free) و استفاده (Use) یکسانی دارند، اما دارای ردیابی پشته متفاوتی هستند که در نهایت به همان رویداد تخصیص (Alloc) منتهی میشود.
در واقع، در حالی که ورودی اثبات مفهوم (PoC) تولیدشده توسط UAFUZZ شامل دو نویسه ! است، PoC مربوط به CVE-2018-6952 فاقد این نویسه است. در نتیجه، حالت موجود در خط ۱۶ پیشتر پوشش داده نشده بود و بنابراین این CVE بهطور ناقص رفع شده بود. این مطالعه موردی اهمیت تولید ورودیهای متفاوت و منحصربهفردِ ایجادکننده باگ را نشان میدهد؛ زیرا چنین ورودیهایی میتوانند فرایند اصلاح را بهبود دهند و به تکمیل فرایند رفع باگ کمک کنند.
لیست ۳: قطعهکد GNU patch مرتبط با آسیبپذیری UAF با شناسه CVE-2018-6952
1 File: src/patch.c
2 int main (int argc , char ** argv ) {...
3 while (0 < ( got_hunk = another_hunk ( diff_type , reverse
))) { /* Apply each hunk of patch */ ... }
4 ...}
5
6 File: src/pch.c
7 int another_hunk (enum diff difftype , bool rev ) { ...
8 while ( p_end >= 0) {
9 if ( p_end == p_efake ) p_end = p_bfake ;
10 else free ( p_line [ p_end ]) ; /* Free and Use event */
11 p_end - -;
12 } ...
13 while ( p_end < p_max ) { ...
14 switch(* buf ) { ...
15 case ’+ ’: case ’! ’: /* Our bug CVE-2019-20633 */ ...
16 p_line [ p_end ] = savebuf (s , chars_read ); ...
17 case ’ ’: /* CVE-2018-6952 */ ...
18 p_line [ p_end ] = savebuf (s , chars_read ); ...
19 ...}
20 ...}
21 ... }
22
23 File: src/util.c
24 /* Allocate a unique area for a string. */
25 char * savebuf (char const *s , size_t size ) { ...
26 rv = malloc ( size ); /* Alloc event */ ...
27 memcpy (rv , s , size );
28 return rv ;
29 }
جدول ۸: خلاصه آسیبپذیریهای روز-صفر گزارششده توسط فازر ما


