
राउटर TL-WR902AC को उदाहरण के रूप में उपयोग करके IoT उपकरणों की फ़ज़िंग
यह मेरे टर्म पेपर का HTML संस्करण है जिसे PDF के रूप में डाउनलोड किया जा सकता है यहाँ.
फ़ज़िंग सॉफ़्टवेयर में बग खोजने का "सबसे प्रभावी तरीकों" में से एक बन गई है। इसी या समान दावों के साथ, कई वर्तमान फ़ज़िंग-संबंधित पेपर शुरू होते हैं [google-scholar]. हमारे पिछले टर्म पेपर का मुख्य लक्ष्य, जिसका विषय "Internet of Vulnerable Things" था, एक मेमोरी-संबंधित बग ढूंढना और फिर इस भेद्यता के लिए एक एक्सप्लॉइट लिखना था। हम फर्मवेयर को रिवर्स करके एक भेद्यता खोजने में सक्षम थे, लेकिन कोई मेमोरी-संबंधित बग नहीं मिला। बाइनरी को हाथ से रिवर्स करके बफर ओवरफ्लो खोजना न केवल समय लेने वाला है, बल्कि इसके लिए बहुत अधिक अनुभव की भी आवश्यकता होती है। वहीं फ़ज़िंग का उद्देश्य ऐसी मेमोरी-संबंधित भेद्यताओं को खोजने का "सबसे प्रभावी तरीका" होना है। उदाहरण के लिए, Google ने OSS-Fuzz पेश किया, जो लगातार ओपन सोर्स सॉफ़्टवेयर को फ़ज़ करता है और 1,000 प्रोजेक्ट्स में पहले ही 10,000 से अधिक भेद्यताएँ खोज चुका है [oss-fuzz].
इस टर्म पेपर का लक्ष्य फिर से एक मेमोरी-संबंधित भेद्यता खोजना है, लेकिन इस बार फ़ज़िंग का उपयोग करके। लक्षित भेद्यता व्यवस्थापक क्रेडेंशियल के ज्ञान के बिना नेटवर्क पर शोषण योग्य होनी चाहिए। यह पेपर इस लक्ष्य को प्राप्त करने का तरीका बताता है। इसके लिए, पेपर को दो भागों में विभाजित किया गया है। पहला भाग इस बात पर केंद्रित है कि एक शक्तिशाली लक्ष्य कैसे खोजा जाए, कौन से उपकरण उपयोग किए जा सकते हैं, और एक अच्छे फ़ज़िंग लक्ष्य में क्या शामिल होना चाहिए। दूसरा भाग फिर वर्णन करता है कि एक ऐसा हार्नेस कैसे विकसित और डीबग किया जाए जो बाइनरी में किसी विशिष्ट फ़ंक्शन को फ़ज़ करने में सक्षम हो। फिर विकसित हार्नेस का उपयोग AFL++ द्वारा लक्षित फ़ंक्शन को फ़ज़ करने के लिए किया जाता है। निम्नलिखित में, एक संक्षिप्त पृष्ठभूमि दी गई है और IoT डिवाइस फ़ज़िंग के मामले में वर्तमान तकनीकी स्थिति क्या है।
इस टर्म पेपर के संदर्भ में बनाई गई सभी फ़ाइलें GitHub पर पूरी तरह से प्रकाशित भी हैं और निम्न URL का उपयोग करके एक्सेस की जा सकती हैं: otsmr/blackbox-fuzzing.
IoT डिवाइस की फ़ज़िंग करना किसी ओपन सोर्स प्रोजेक्ट की फ़ज़िंग करने जितना आसान नहीं है। अक्सर सोर्स कोड स्वामित्व (proprietary) होता है, जो ग्रे-बॉक्स फ़ज़िंग को असंभव बना देता है, जो सर्वोत्तम फ़ज़िंग प्रदर्शन के लिए सोर्स कोड को इंस्ट्रूमेंट करता है [afl-persistent]. इसके अलावा, CPU आर्किटेक्चर अक्सर फ़ज़र्स द्वारा मूल रूप से समर्थित नहीं होता है, जिसके लिए QEMU [qemu] जैसे इम्यूलेटर की आवश्यकता होती है, जो फ़ज़िंग गति को भी धीमा कर देता है [afl-persistent]. एक और मुद्दा हार्डवेयर परिधीय (peripherals) है, जो एक सामान्य दृष्टिकोण के विकास को जटिल बनाता है। पेपर "Embedded Fuzzing: A Review of Challenges, Tools, and Solutions" [embedded-fuzzing] विभिन्न फ़ज़िंग रणनीतियों का अवलोकन देता है, जैसे हार्डवेयर-आधारित एम्बेडेड फ़ज़िंग। इनमें से अधिकांश रणनीतियों को लक्षित प्रोग्राम के सोर्स कोड की आवश्यकता होती है, जैसे कि AFL जैसे फ़ज़र के सोर्स कोड को ARM-आधारित IoT डिवाइसों पर पोर्ट करना ताकि फ़ज़र को IoT हार्डवेयर पर चलाया जा सके। फ़ज़र को डिवाइस के हार्डवेयर पर चलाने में भी प्रदर्शन संबंधी समस्याएँ होती हैं क्योंकि उनमें अक्सर निम्न-स्तरीय CPU होते हैं, जो सामान्य डेस्कटॉप CPU से धीमे होते हैं। इस पेपर में प्रस्तुत एक अन्य दृष्टिकोण इम्यूलेशन-आधारित एम्बेडेड फ़ज़िंग है। जहाँ या तो कवरेज-निर्देशित फ़ज़िंग करने के लिए एक इम्यूलेटर में एकल लक्षित प्रोग्राम निष्पादित किया जाता है या पूरा सिस्टम।
ऊपर उल्लिखित दृष्टिकोण सभी एक इम्यूलेटर का उपयोग करके या सोर्स कोड को इंस्ट्रूमेंट करके सीधे एक बाइनरी को लक्षित करते हैं। इन दृष्टिकोणों के लिए एक फ़ज़िंग सेटअप की आवश्यकता होती है जिसे अक्सर एकल IoT डिवाइस के लिए विशेष रूप से तैयार किया जाना चाहिए और इसे सामान्यीकृत करना कठिन होता है। इसके लिए, शोधकर्ताओं ने IoTFuzzer नामक एक प्रोग्राम बनाया, जिसका उद्देश्य एक स्वचालित फ़ज़िंग फ्रेमवर्क होना है जो "उनके फर्मवेयर इमेज तक पहुँच के बिना मेमोरी क्षति (memory corruption) भेद्यताओं को खोजने" [iotfuzzer] का लक्ष्य रखता है। IoTFuzzers इस अवलोकन पर आधारित है कि अधिकांश IoT डिवाइसों में उन्हें नियंत्रित करने के लिए एक मोबाइल ऐप होता है, और ऐसे ऐप्स में डिवाइस के साथ संचार करने के लिए उपयोग किए जाने वाले प्रोटोकॉल के बारे में जानकारी होती है। प्रोग्राम फिर IoT लक्ष्यों का प्रभावी ढंग से परीक्षण करने के लिए परीक्षण मामलों को उत्परिवर्तित (mutate) करने हेतु प्रोग्राम-विशिष्ट तर्क की पहचान करता है और उसका पुन: उपयोग करता है [iotfuzzer].
हार्नेस फ़ज़र द्वारा प्रदान किए गए इनपुट को संसाधित करने वाले API कॉल्स के अनुक्रम का वर्णन करता है। एक सामान्य अनुप्रयोग के विपरीत, जिसे अक्सर हार्नेस की आवश्यकता नहीं होती है, एक लाइब्रेरी जो पुन: प्रयोज्य फ़ंक्शन लागू करती है, उसे सही मापदंडों के साथ और सही क्रम में भी कॉल किया जाना चाहिए, ताकि कई साझा फ़ंक्शन कॉल्स के बीच की स्थिति को बनाए रखा जा सके। स्टेट मशीन बनाए बिना लाइब्रेरी को यादृच्छिक रूप से फ़ज़ करना सफल होने की संभावना नहीं है और इसके विपरीत, जब लाइब्रेरी निर्भरताएँ लागू नहीं होती हैं, तो यह बहुत सारे गलत-सकारात्मक (false-positive) क्रैश उत्पन्न करेगा। यह तब हो सकता है, उदाहरण के लिए, जब फ़ज़र द्वारा बफर आकार की जाँच को छोड़ दिया जाता है, जिसके परिणामस्वरूप एक नकली बफर ओवरफ्लो होता है।
इस पेपर में, सामान्य अनुप्रयोगों को फ़ज़ किया जाएगा, लेकिन सॉकेट्स और मल्टी-थ्रेडिंग के उपयोग की हार्डवेयर निर्भरताओं के कारण, हमें उनके लिए भी एक हार्नेस बनाने की आवश्यकता है। हार्नेस बाइनरी के संदर्भ में लोड किया जाता है और लक्षित प्रोग्राम के आंतरिक फ़ंक्शन को कॉल कर सकता है, जैसा कि कोड 10 में दिखाया गया है।
"कॉर्पस" शब्द मान्य इनपुट नमूनों या परीक्षण मामलों का वर्णन करता है और फ़ज़िंग प्रक्रिया के दौरान नए इनपुट डेटा उत्पन्न करने के लिए एक आधारभूत संदर्भ के रूप में कार्य करता है। कोड 10 में यह, उदाहरण के लिए, एक HTTP अनुरोध होगा। फ़ज़र फिर इस कॉर्पस का उपयोग उत्परिवर्तित या विविध परीक्षण मामले बनाने के लिए करते हैं, जिससे विभिन्न इनपुट परिदृश्यों की खोज के माध्यम से सॉफ़्टवेयर भेद्यताओं का पता लगाने में सहायता मिलती है।
ब्लैक बॉक्स फ़ज़िंग का सबसे समय लेने वाला हिस्सा फर्मवेयर में एक संभावित भेद्य फ़ंक्शन खोजना है। पहला कदम दिलचस्प बाइनरी ढूंढना है जो, उदाहरण के लिए, नेटवर्क पर पहुँच योग्य हों, असुरक्षित फ़ंक्शन का उपयोग करती हों, या स्टैक कैनरी जैसी सुरक्षा सुविधाएँ सक्षम न रखती हों, जो बफर ओवरफ्लो सुरक्षा है। हमारे पिछले पेपर
([iovt]) ने पहले ही वर्णन किया है कि लक्षित राउटर से फर्मवेयर कैसे निकाला जाए और एक संभावित खतरनाक बाइनरी कैसे खोजी जाए। इसके लिए, EMBA [emba] टूल का उपयोग किया गया था। EMBA फर्मवेयर में पाई जाने वाली सभी बाइनरी को strcpy जैसे असुरक्षित फ़ंक्शनों, नेटवर्क एक्सेस, और स्टैक कैनरी या NX-Bit जैसी सुरक्षा सुविधाओं की संख्या के आधार पर रैंक करता है, जो बफर ओवरफ्लो का शोषण करते समय दिलचस्प हो जाती हैं, जो कोड 1 में पाया जा सकता है।
कोड 1: फ़ंक्शन strcpy के असुरक्षित उपयोगों का EMBAs परिणाम।
क्योंकि इस पेपर का लक्ष्य एक मेमोरी भेद्यता खोजना है जिसका शोषण प्रशासक क्रेडेंशियल्स की जानकारी के बिना नेटवर्क पर किया जा सकता है, भेद्य फ़ंक्शन नेटवर्क पर कॉल करने योग्य होना चाहिए और सीधे प्रदान किए गए उपयोगकर्ता इनपुट के साथ इंटरैक्ट करना चाहिए। लेकिन नेटवर्क इंटरैक्शन होने का मतलब यह नहीं है कि बाइनरी भी नेटवर्क पर सीधे सुलभ है। यह पता लगाने के लिए कि कौन सी बाइनरी सुन रही हैं, हम UART रूट शेल का उपयोग कर सकते हैं, जो पहले से ही [iovt] में स्थापित किया गया था।
```txt ~ # netstat -tulpn Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.1:20002 0.0.0.0:* LISTEN 1045/tmpd tcp 0 0 0.0.0.0:1900 0.0.0.0:* LISTEN 1034/upnpd tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1027/httpd tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1224/dropbear udp 0 0 0.0.0.0:20002 0.0.0.0:* 1048/tdpd [...] ```कोड 2: netstat निष्पादित करने के लिए UART रूट शेल का उपयोग करना
पहला बाइनरी जो आशाजनक लगता है वह wscd है। बाइनरी में सबसे असुरक्षित strcpy कॉल हैं
(libcmm.so लाइब्रेरी को छोड़कर) और नेटवर्क इंटरैक्शन, जो wscd के मामले में इसका अर्थ है कि यह
एक UPnP डिवाइस से जुड़ता है और किसी विशिष्ट पोर्ट पर नहीं सुनता। जैसा कि बाद में दिखाया गया है, इसमें एक आसान
फ़ज़ करने लायक फ़ंक्शन है, यही कारण है कि इस बाइनरी को इस पेपर में उदाहरण के रूप में चुना गया था ताकि
सामान्य प्रक्रिया समझाई जा सके। रिवर्स करने से पहले, हम UART रूट शेल का उपयोग करके पता लगा सकते हैं कि बाइनरी
चल रहा है और इसे कैसे शुरू किया गया था।
कोड 3: सभी चल रहे प्रोग्रामों को प्रदर्शित करने के लिए ps कमांड का उपयोग करना।
ps के साथ हम न केवल यह देखते हैं कि बाइनरी चल रही है बल्कि यह भी देखते हैं कि तर्क क्या हैं, जो
यह सत्यापित करने के लिए महत्वपूर्ण हैं कि कोई संभावित फ़ंक्शन वास्तव में कॉल किया गया है या नहीं। इन तर्कों का अर्थ
CLI सहायता से प्राप्त किया जा सकता है, जो बाइनरी को बिना किसी तर्क के कॉल करने पर प्रदर्शित होती है।
कोड 4: बाइनरी wscd के विकल्प।
जैसा कि कोड 4 में दिखाया गया है, wscd को "Enabled UPnP Device service" के साथ प्रारंभ किया जाता है, जो आशाजनक लगता है। यह सत्यापित करने के बाद कि बाइनरी वास्तव में राउटर पर चल रही है, बाइनरी का विश्लेषण Ghidra का उपयोग करके संदिग्ध फ़ंक्शंस की खोज के लिए किया जा सकता है। फ़ज़िंग के लिए, पार्सिंग फ़ंक्शंस विशेष रूप से दिलचस्प होते हैं क्योंकि वे आमतौर पर जटिल होते हैं, और अक्सर उस इनपुट में निहित डेटा के लिए लंबाई फ़ील्ड होते हैं, जैसे TCP पैकेट में पेलोड की लंबाई होती है।

चित्र 1: पार्सिंग फ़ंक्शंस खोजने के लिए Ghidra का उपयोग।
पार्सिंग फ़ंक्शंस का एक और लाभ यह है कि वे अक्सर कोड के अन्य भागों के साथ इंटरैक्ट नहीं करते और न ही नेटवर्क पर उपयोगकर्ता इंटरैक्शन करते हैं। इसलिए पार्सिंग फ़ंक्शन को बाइनरी को संशोधित किए बिना या अन्य फ़ंक्शंस को अधिलेखित किए बिना सीधे इनपुट के साथ बुलाया जा सकता है, और इस प्रकार फ़ंक्शन को फ़ज़ किया जा सकता है।
फ़ंक्शन को फ़ज़ करना शुरू करने से पहले, यह जाँचा जाना चाहिए कि फ़ंक्शन बिल्कुल ट्रिगर होता है या नहीं, क्योंकि फ़ंक्शन तभी दिलचस्प होता है जब उसे उपयोगकर्ता-नियंत्रित इनपुट के साथ बुलाया जाता है। इसके लिए, Ghidra का उपयोग लक्ष्य फ़ंक्शन के संदर्भों को खोजने के लिए किया जा सकता है। parser_parse फ़ंक्शन के मामले में कई तरीके हैं। चूँकि हम जानते हैं कि प्रोग्राम कैसे प्रारंभ होता है, कॉल्स को एक एकल फ़ंक्शन कॉल ट्री तक सीमित किया जा सकता है, जैसा कि कोड 5 में दिखाया गया है।
कोड 5: फ़ंक्शन parser_parse का कॉल ट्री
एक लक्ष्य फ़ंक्शन मिल जाने के बाद, अब हम उस फ़ंक्शन को फ़ज़ करने के लिए एक फ़ज़िंग सेटअप बना सकते हैं, जिसका वर्णन अगले भाग में किया गया है। लेकिन पहले अन्य प्रबल फ़ंक्शन प्रस्तुत किए गए हैं।
इस पेपर के लिए, संदिग्ध फ़ंक्शनों के लिए कई संभावित बाइनरीज़ का मैन्युअल रूप से विश्लेषण किया गया। निम्नलिखित अन्य संभावित लक्ष्यों का एक संक्षिप्त सारांश है जो पाए गए।
बाइनरी httpd एडमिन वेब इंटरफ़ेस का बैकएंड है। बाइनरी नेटवर्क पर
पोर्ट 80 के माध्यम से एक्सेस की जा सकती है। httpd में एक दिलचस्प फ़ंक्शन httpd_parser_main है। जबकि
पार्सर इम्प्लीमेंटेशन को Ghidra के उपयोग से देखने पर कई अलग-अलग संदिग्ध कोड भागों की पहचान की जा सकती है।
इनमें से एक संदिग्ध भाग Content-Type का पार्सिंग है। निम्नलिखित में, एक बुनियादी
HTTP अनुरोध पाया जा सकता है।```txt
POST / HTTP/1.1\r\n
Content-Type: multipart/form-data; boundary=X;\r\n
Host: example.com\r\n
\r\n
\r\n
DATA\r\n
नीचे `httpd_parser_main` फ़ंक्शन का एक स्निपेट है जो उपयोगकर्ता द्वारा प्रदान किए गए http अनुरोध से `Content-Type` को पार्स करता है।
<div id="c6"></div>```c
// user_input_ptr points to
// "Content-Type: multipart/form-data; boundary=X;\r\nHost: example.com\r\n..."
cursor = strstr(user_input_ptr,"multipart/form-data");
if (user_input_ptr == cursor) {
cursor = strstr(user_input_ptr,"boundary=");
user_input_ptr = cursor + 9;
// user_input_ptr points now to "X;\r\nHost: example.com\r\n..."
if (cursor != (char *)0x0) {
do {
while (cursor = user_input_ptr, *cursor == " ") {
user_input_ptr = cursor + 1;
}
user_input_ptr = cursor + 1;
} while (*cursor == "\t");
// cursor points now to "X;\r\nHost: example.com\r\n..."
// strchr returns a pointer to the first occurrence of ";" in the user request.
// If ";" is not found, the function returns a null pointer.
user_input_ptr = strchr(cursor, ";");
if (user_input_ptr != (char *)0x0) {
// The character ";" is replaced by an null byte to terminate the string
*user_input_ptr = "\0";
// cursor points now to "X\0\r\nHost: example.com\r\n..."
}
// DAT_00444050 global array from 0x00444050 to 0x0044414f (255 Bytes)
strcpy(&DAT_00444050, cursor);
// DAT_00444050 contains now "X"
}
}
कोड 6: फ़ंक्शन parser_parse का कॉल ट्री
इस कोड में कमजोरी strcpy फ़ंक्शन कॉल और यह धारणा है कि Content-Type एक अर्धविराम के साथ समाप्त होता है। क्योंकि strcpy अगले नल बाइट तक बफर की नकल करता है, और जैसा कि कोड 6 में दिखाया गया है, नल बाइट केवल तब जोड़ा जाता है जब कोई अर्धविराम मिलता है। अर्धविराम हटाने से, अगला नल बाइट इनपुट बफर के अंत में होता है, जैसे कि HTTP अनुरोध के अंत में। इसलिए वैश्विक चर DAT_00444050 ओवरफ्लो हो सकता है, जो फिर पते 0x0044414f से आगे के डेटा को अधिलेखित कर देता है। चुनौतीपूर्ण हिस्सा केवल इस पते से आगे एक दिलचस्प वैश्विक चर खोजना नहीं है जिसे अधिलेखित किया जा सके, बल्कि यह भी कि strcpy के कारण कोई नल बाइट इस्तेमाल नहीं की जा सकती। लेकिन जब एक ऐसी गलती होती है, तो संभवतः और भी मिलेंगी।
बाइनरी tdpd मोबाइल ऐप द्वारा उपयोग की जाती है और स्थानीय नेटवर्क पर UDP के माध्यम से सुलभ है। tdpd में लगभग वही फ़ंक्शन हैं जो tmpd में हैं, जिनमें से अधिकांश को कभी कॉल नहीं किया जाता। मुख्य फ़ंक्शन केवल UDP पोर्ट पर संदेशों को सुनता है और हमेशा राउटर के बारे में बुनियादी जानकारी, जैसे नाम या मॉडल, के साथ उत्तर देता है। उपयोगकर्ता द्वारा प्रदान किए गए इनपुट के साथ शायद ही कोई इंटरैक्शन होता है, इसलिए यह फ़ज़िंग के लिए दिलचस्प नहीं है।
बाइनरीज़ की एक और दिलचस्प जोड़ी upnpd और ushare हैं। दोनों बाइनरीज़ UPnP संदेशों को संभालती हैं, जिन्हें इसलिए XML पार्स करने की आवश्यकता होती है। चूंकि बाइनरी में एक कॉपीराइट स्ट्रिंग पाई जा सकती है, यह माना जा सकता है कि ये प्रोग्राम TP-Link द्वारा विकसित नहीं किए गए थे।```sh
$ strings usr/bin/ushare | grep "(C)"
Benjamin Zores (C) 2005-2007, for GeeXboX Team.
दोनों बाइनरी साझा लाइब्रेरी `libupnp.so` और `libixml.so` लोड कर रही हैं, जिनमें
ओपन सोर्स प्रोजेक्ट `pupnp` [\[pupnp\]](https://github.com/pupnp/pupnp/) के समान फ़ंक्शन हैं। चूंकि
इस पेपर का ध्यान ब्लैक बॉक्स फज़िंग पर है, इन बाइनरी को अनदेखा किया गया है। लेकिन इस लाइब्रेरी की ग्रे बॉक्स फज़िंग में
संभावना हो सकती है, क्योंकि 2021 में `libixml.so` में एक मेमोरी लीक पाई गई थी
[\[pupnp-mem-leak\]](https://github.com/pupnp/pupnp/issues/249)।
बाइनरी **tmpd** मोबाइल ऐप का बैकएंड है। दिलचस्प बात यह है कि राउटर और
मोबाइल ऐप एक कस्टम बाइनरी प्रोटोकॉल के माध्यम से संचार कर रहे हैं। निम्नलिखित में, क्लाइंट से
सर्वर को एक संदेश दिखाया गया है।
<div id="c7"></div>```txt
00000000 01 00 05 00 00 08 00 00 00 00 00 17 50 7b 6e fe |............P{n.|
00000010 01 01 02 00 00 00 00 00 |........ |
Code 7: मोबाइल ऐप से राउटर को संदेश।
बाइनरी प्रोटोकॉल को समझने के लिए, tmpd बाइनरी को Ghidra का उपयोग करके रिवर्स किया गया था। इस
जानकारी के साथ, Code 7 में दिए गए संदेश को निम्न प्रकार से विभाजित किया जा सकता है:```txt
01 00 05 00 : Version
00 08 00 00 : Size (8 Bytes)
00 00 00 17 : Datatype
50 7b 6e fe : Checksum (CRC32)
01 01 : Options
02 00 : Function id
00 00 00 00 : Function parameters
<p class="text-align: center">कोड 8: कस्टम बाइनरी प्रोटोकॉल का विघटन।</p>
यह आशाजनक लगता है क्योंकि ऐसे बाइनरी प्रोटोकॉल को पार्स किया जाना चाहिए। लेकिन बाइनरी प्रोटोकॉल का सबसे संदिग्ध हिस्सा लंबाई फ़ील्ड नहीं है, बल्कि फ़ंक्शन आईडी और फ़ंक्शन पैरामीटरों का उपयोग है।
<figure id="f2">
<p><img src="https://assets.kitploit.com/production/public/readmes/48851/43cb089d7f85d30ba036234ff0fdb29b6a54dd4a3382b2bd68e065f9f6c75369.png" style="width:90.0%" /></p>
<figcaption>
<p style="text-align: center">चित्र 2: tmpd से रिवर्स किया गया फ़ंक्शन जो फ़ंक्शन आईडी और उनके पैरामीटरों को पार्स करता है।</p>
</figcaption>
</figure>
[चित्र 2](#f2) कस्टम प्रोटोकॉल के डीकंपाइल किए गए पार्सर फ़ंक्शन का एक हिस्सा दिखाता है। पंक्ति 16 में, फ़ंक्शन आईडी निकाली जाती है, और उसके बाद पंक्ति 29 में संबंधित फ़ंक्शन को कॉल किया जाता है। संदिग्ध व्यवहार यह है कि फ़ंक्शन को बिना किसी जाँच के उपयोगकर्ता-नियंत्रित इनपुट बफर से निकाले गए पैरामीटरों के साथ कॉल किया जाता है। अब हम [चित्र 3](#f3) में दिखाई गई जंपिंग टेबल में एक ऐसा फ़ंक्शन खोजने की कोशिश कर सकते हैं जहाँ यह खतरनाक हो सकता है, जैसे जब पैरामीटर का उपयोग बफर को इंडेक्स करने के लिए किया जाता है या स्ट्रिंग के रूप में व्याख्या किया जाता है। 100 से अधिक फ़ंक्शनों को मैन्युअल रूप से रिवर्स करने और खोजने के बजाय, जो समय लेने वाला होगा, हम एक फ़ज़र का उपयोग कर सकते हैं जो इसे स्वचालित रूप से करेगा।
<figure id="f3">
<p><img src="https://assets.kitploit.com/production/public/readmes/48851/708cf59aee459b840ca6dbcac32948d2b80fb426c3d4cc745157eac4f9b7c28e.png"
style="width:90.0%" /></p>
<figcaption><p style="text-align: center">चित्र 3: tmpd से रिवर्स किया गया फ़ंक्शन जो फ़ंक्शन आईडी
और उसके पैरामीटरों को पार्स करता है।</p></figcaption>
</figure>
दुर्भाग्य से, `tmpd` बाइनरी केवल नेटवर्क पर स्थानीय रूप से पहुंच योग्य है, जैसा कि [कोड 2](#c2) में दिखाया गया है। इस बाइनरी से कनेक्ट करने के लिए, ऐप पहले SSH के माध्यम से `direct-tcpip` मोड में राउटर से कनेक्ट होता है, जो पैकेटों को स्थानीय प्रक्रिया में अग्रेषित करता है। और SSH कनेक्शन एडमिन क्रेडेंशियल्स द्वारा सुरक्षित होता है। लेकिन जैसा कि [\[iovt\]](https://raw.githubusercontent.com/otsmr/internet-of-vulnerable-things/main/Internet_of_Vulnerable_Things.pdf) में वर्णित है, SSH कनेक्शन आसानी से समझौता किया जा सकता है क्योंकि सर्वर होस्ट कुंजी की जाँच ऐप द्वारा कभी नहीं की जाती है। इंटरनेट पर रूट किए गए हर पैकेट को गिराकर, एडमिन को राउटर में लॉग इन करने के लिए धोखा दिया जा सकता है, जबकि क्रेडेंशियल्स चुराने के लिए मैन-इन-द-मिडिल हमला किया जाता है।
## AFL++ और QEMU के साथ फ़ज़िंग
इस अनुभाग में, पहले से पाए गए किसी एक फ़ंक्शन को लक्षित करते हुए एक हार्नेस विकसित किया गया है। हार्नेस विकसित होने के बाद, अत्याधुनिक फ़ज़र AFL++ [\[aflpp\]](https://github.com/AFLplusplus/AFLplusplus) का उपयोग लक्षित फ़ंक्शन को फ़ज़ करने के लिए किया जाता है। चूँकि बाइनरी `mipsel` आर्किटेक्चर के लिए संकलित की जाती हैं, बाइनरी को निष्पादित करने के लिए एमुलेटर QEMU का उपयोग किया जाता है। इस पेपर में उपयोग किया गया मूल फ़ज़िंग सेटअप अधिकतर Adam Van Prooyen द्वारा लिखित ब्लॉग प्रविष्टि "Firmware Fuzzing 101" [\[b101\]](https://www.mayhem.security/blog/firmware-fuzzing-101) से प्रेरित है।
### फ़ज़िंग वातावरण
आसानी से एक पुनरुत्पादक फ़ज़िंग वातावरण बनाने के लिए, Docker सबसे अच्छा विकल्प है। हमने एक Dockerfile बनाई है जो सभी आवश्यक उपकरण स्थापित करती है, जैसे `mipsel` CPU आर्किटेक्चर के लिए क्रॉस-कंपाइलर या `gdb-multiarch`, जिसका उपयोग हार्नेस को डीबग करने के लिए किया जा सकता है।
इसके अलावा, AFLplusplus को डाउनलोड करके QEMU के साथ संकलित किया जाता है, जिसे मामूली बदलावों के साथ एक संस्करण में बनाया गया है ताकि गैर-इंस्ट्रूमेंटेड बाइनरी को afl-fuzz के अंतर्गत चलाया जा सके।```docker
FROM debian:latest
RUN apt update && apt install -y \
curl \
vim \
gcc-mipsel-linux-gnu \
openssh-server \
qemu-user-static \
gdb-multiarch
# Qemu statics are installed at /usr/bin/qemu-mipsel-static
# Compiling AFL++
RUN apt install -y git make build-essential clang ninja-build pkg-config libglib2.0-dev libpixman-1-dev
RUN git clone https://github.com/AFLplusplus/AFLplusplus /AFLplusplus
WORKDIR /AFLplusplus
RUN make all
WORKDIR /AFLplusplus/qemu_mode
RUN CPU_TARGET=mipsel ./build_qemu_support.sh
RUN echo "#!/bin/bash\n\nsleep infinity" >> /entry.sh
RUN chmod +x /entry.sh
WORKDIR /share
ENTRYPOINT [ "/entry.sh" ]
Dockerfile जो आवश्यक टूल्स इंस्टॉल करता है।
इसके बाद इमेज को docker build का उपयोग करके बनाया जा सकता है।```sh
docker build -t fuzz .
जब इमेज बन जाती है, तो इसे आसानी से `docker run` के साथ उपयोग किया जा सकता है, जो
फिर कंटेनर को प्रारंभ करता है।```sh
docker run -d --rm -v $PWD/:/share --name fuzz fuzz
-d विकल्प का उपयोग करने पर कंटेनर पृष्ठभूमि में प्रारंभ हो जाएगा। docker exec से कंटेनर के अंदर कई शेल
प्रारंभ किए जा सकते हैं, जो एक सत्र में QEMU का उपयोग करके निष्पादन योग्य को तथा दूसरे सत्र में
gdb-multiarch को प्रारंभ करने में सहायक है।```sh
docker exec -it fuzz /bin/bash
### मुख्य फ़ंक्शन को ओवरराइट करें
पिछले अनुभाग में, एक शक्तिशाली फ़ज़ टारगेट की पहचान की गई थी। समस्या यह है कि जब निष्पादित करते हुए
बाइनरी, हम कभी भी फ़ंक्शन कॉल तक नहीं पहुँच पाएँगे क्योंकि `parser_parse` फ़ंक्शन केवल तभी कॉल किया जाता है यदि
किसी सॉकेट पर एक TCP पैकेट प्राप्त होता है। यह न केवल प्रदर्शन के लिए खराब होगा, बल्कि सेट अप करना भी
कठिन होगा। यही कारण है कि फ़ज़र की प्रविष्टि सामान्य main
फ़ंक्शन से भिन्न स्थान पर होनी चाहिए। इसके लिए, `LD_PRELOAD` पर्यावरण चर का उपयोग किया जा सकता है,
जो एक ऐसा हार्नेस इंजेक्ट करने में सक्षम बनाता है जिसके पास आंतरिक फ़ंक्शनों तक पहुँच होती है।
जैसा कि `ld.so` का मैन पेज, जो रनटाइम पर एक निष्पादनयोग्य के लिए आवश्यक साझा लाइब्रेरीज़ को जोड़ने के
लिए ज़िम्मेदार है, वर्णन करता है, `LD_PRELOAD` का उपयोग "अन्य साझा ऑब्जेक्ट्स में फ़ंक्शनों को चुनिंदा रूप से ओवरराइड करने के लिए
[\[man-pages\]](https://www.man7.org/linux/man-pages/man8/ld.so.8.html)."
इस उद्देश्य के लिए `__uClibc_main` फ़ंक्शन सबसे उपयुक्त है। इस फ़ंक्शन को ओवरराइट करने के लिए, एक C फ़ाइल
बनानी होगी जिसमें इसी नाम वाला एक फ़ंक्शन हो।```c
void __uClibc_main(void *main, int argc, char** argv) {
// Harness code, e.g. call the function parser_append
printf("My custom __uClibc_main was called!");
}
सी फ़ाइल को फिर mipsel आर्किटेक्चर में एक शेयर्ड ऑब्जेक्ट में क्रॉस-कम्पाइल करने के लिए
mipsel-linux-gnu-gcc का उपयोग किया जा सकता है। -fPIC विकल्प "Position Independent Code" को सक्षम करता है, जिसका अर्थ है कि
मशीन कोड किसी विशिष्ट पते पर स्थित होने पर निर्भर नहीं करता है, बल्कि सापेक्ष एड्रेसिंग का उपयोग करता है
निरपेक्ष एड्रेसिंग के बजाय।```txt
$ mipsel-linux-gnu-gcc parser_parse_hook.c -o parser_parse_hook.o -shared -fPIC
नव निर्मित साझा लाइब्रेरी को तब QEMU कमांड में पर्यावरण चर `LD_PRELOAD` जोड़कर लोड किया जा सकता है।```txt
$ chroot root /qemu-mipsel-static -E LD_PRELOAD=/parser_parse_hook.o /usr/bin/wscd
My custom __uClibc_main was called!
chroot कमांड के साथ, दिए गए कमांड के लिए वर्तमान और रूट डायरेक्टरी बदली जा सकती हैं।
यह सहायक है क्योंकि एक्ज़िक्यूटेबल wscd अन्य फाइलें खोलता है, जैसे
फर्मवेयर की साझा लाइब्रेरीज़। हम इस व्यवहार को QEMU में -strace तर्क जोड़कर देख सकते हैं।```txt
chroot root /qemu-mipsel-static -E LD_PRELOAD=/parser_parse_hook.o -strace /usr/bin/wscd /corpus/notify.txt
38180 mmap(NULL,4096,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS|0x4000000,-1,0) = 0x7f7e7000
38180 stat("/etc/ld.so.cache",0x7ffffa48) = -1 errno=2 (No such file or directory)
38180 open("/parser_parse_hook.o",O_RDONLY) = 3
38180 fstat(3,0x7ffff920) = 0
38180 close(3) = 0
38180 munmap(0x7f7e6000,4096) = 0
38180 open("/lib/libpthread.so.0",O_RDONLY) = 3
38180 open("/lib/libc.so.0",O_RDONLY) = 3
[...]
जैसा कि हम देख सकते हैं, निष्पादन योग्य फर्मवेयर पर `/lib/` फ़ोल्डर में कई लाइब्रेरी खोलता है, न कि
होस्ट पर।
### हार्नेस का विकास और डिबगिंग
सेटअप बन जाने के बाद, अब हम हार्नेस विकसित करना शुरू कर सकते हैं। जैसा कि पृष्ठभूमि
अनुभाग में वर्णित है, हार्नेस फ़ज़र और लक्ष्य फ़ंक्शन के बीच का ड्राइवर है। हार्नेस
फ़ज़ इनपुट लोड करता है, जिसे AFL++ एक फ़ाइल में संग्रहीत करता है। फ़ाइल पथ को पैरामीटर के
रूप में लेते हुए, हार्नेस फिर फ़ज़िंग लक्ष्य को कॉल करता है; इस मामले में, यह `parser_append` होगा। फ़ंक्शन को कॉल किया जा सकता है
एड्रेस का उपयोग करके।
<div id="c10"></div>```c
void __uClibc_main(void *main, int argc, char** argv)
{
// Verify that a filename is provided
if (argc != 2) exit(1);
// Create function pointer to the fuzz target
int (*parser_request_init)(void *, int) = (void *) 0x00412564;
int (*parser_append)(void *, void *, int) = (void *) 0x00412e98;
// Open the fuzz input file
int fd = open(argv[1], O_RDONLY);
char fuzz_buf[2048 + 1];
int fuzz_buf_len = read(fd, fuzz_buf, sizeof(fuzz_buf) - 1);
if (fuzz_buf_len < 0) exit(1);
fuzz_buf[fuzz_buf_len] = 0;
// Call the target functions
uint8_t parsed_data[220];
parser_request_init(parsed_data, 8);
int status = parser_append(parsed_data, fuzz_buf, fuzz_buf_len);
printf("Response is %d\n", status);
exit(0);
}
कोड 10: बाइनरी wscd में फ़ज़ टारगेट `parser_append` के साथ हार्नेस कोड।
जैसा कि कोड 10 में दिखाया गया है, फ़ंक्शन parser_parse को सीधे कॉल नहीं किया जाता है, बल्कि
फ़ंक्शन parser_append का उपयोग करके कॉल किया जाता है। इस फ़ंक्शन को कॉल करने से पहले, इनिशियलाइज़ेशन फ़ंक्शन
parser_request_init को कॉल करना आवश्यक है, जो parser_parse फ़ंक्शन के आउटपुट स्ट्रक्चर को इनिशियलाइज़ करता है।
जबकि parser_parse के मामले में हार्नेस सेट करना काफी आसान है, अन्य टारगेट्स को अधिक परिष्कृत हार्नेस की आवश्यकता होती है,
जैसे httpd_parser_main फ़ंक्शन। उदाहरण के लिए, टारगेट को कॉल करने से पहले, फ़ंक्शन http_init_main को कॉल करना आवश्यक है,
जो SIGSEGV में समाप्त होता है। यह पता लगाने के लिए कि यह segmentation fault कहाँ होता है, कोड को gdb जैसे डीबगर से
डीबग करना उपयोगी होता है। ऐसा करने के लिए, QEMU को -g विकल्प के साथ शुरू किया जा सकता है, जो प्रदान किए गए पोर्ट पर
एक gdb-server शुरू करता है।```sh
chroot root /qemu-mipsel-static -strace -g 1234 -E LD_PRELOAD="/httpd_parser_main.o" /usr/bin/httpd
corpus/httpd/simple.txt
क्योंकि बाइनरी `mipsel` आर्किटेक्चर में है, `gdb-multiarch` का उपयोग किया जाना चाहिए। gdb
शुरू होने के बाद, निम्न init स्क्रिप्ट को gdb में `sources <path to script>` का उपयोग करके लोड किया जा सकता है।```sh
set solib-absolute-prefix /share/root/
file /share/root/usr/bin/httpd
target remote :1234
# break bevor fuzz target is called
# break __uClibc_main
break http_parser_main
display/4i $pc
chroot के कारण स्क्रिप्ट ने पहले absolute prefix path को बदल दिया, ताकि जब बाइनरी
किसी shared object को load करे तो gdb को फ़ाइल मिल जाए। फिर targeted file सेट की जाती है, क्योंकि QEMU का
gdb-server file transfer का समर्थन नहीं करता, इसलिए gdb इसके बजाय डिस्क से फ़ाइलों को load करने की कोशिश करता है।
gdb कॉन्फ़िगर होने के बाद, स्क्रिप्ट target remote के साथ gdb-server से कनेक्ट होती है और
target फ़ंक्शन की शुरुआत में breakpoint बनाती है। display से आउटपुट बेहतर होता है, इसलिए step
करते समय अगली चार assembly lines दिखाई जाती हैं। si का उपयोग करके हम एक
instruction step कर सकते हैं, जो तब उपयोगी होता है जब harness को default corpus के साथ
segmentation fault आए, जो हमेशा काम करना चाहिए। जैसा कि Code 11 में दिखाया गया है, बाइनरी में
fprintf फ़ंक्शन में segmentation fault होता है।
Program received signal SIGSEGV, Segmentation fault.
<p style="text-align: center">कोड 11: printf में Segmentation fault.</p>
त्रुटि की जाँच करने के लिए, Ghidra का उपयोग यह पता लगाने के लिए किया जा सकता है कि फ़ंक्शन को किन पैरामीटरों के साथ
कॉल किया गया है।```c
fprintf(
*(FILE **)(iVar1 + 0x101c),
"HTTP/1.1 %d %s\r\n",
*(undefined4 *)(&DAT_0042ee68 + (uint)(byte)(&DAT_00414570)[statuscode & 0x3f] * 8),
(&PTR_DAT_0042ee6c)[(uint)(byte)(&DAT_00414570)[statuscode & 0x3f] * 2]
);
SIGSEGV शायद इस तथ्य के कारण होता है कि पहला पैरामीटर फ़ाइल डिस्क्रिप्टर नहीं बल्कि एक
null pointer है। जहाँ iVar1 केवल httpd_parser_main फ़ंक्शन के इनपुट का एक संदर्भ है।
इसका मतलब है कि फ़ज़िंग इनपुट में स्थिति 0x101c पर एक फ़ाइल डिस्क्रिप्टर होना चाहिए। इसलिए इनपुट को
निम्नलिखित struct के अनुसार समायोजित किया जाना चाहिए।```c
typedef struct {
int _a; // 4 Bytes
int _b; // 4 Bytes
int socket; // 4 Bytes
int ip; // 4 Bytes
int mac; // 4 Bytes
unsigned char body[0x1008]; // 0x101c - 4*5 = 0x1008 Bytes
FILE * fd_out; // expected to be a valid file descriptor
} HttpMainT;
चूँकि `fd_out` केवल एक मान्य फ़ाइल डिस्क्रिप्टर पॉइंटर होना चाहिए, इसे आसानी से `stdout` पर सेट किया जा सकता है।
`httpd_parser_main` को फिर से निष्पादित करने पर अब एक मान्य HTTP आउटपुट उत्पन्न होगा।```c
$ chroot root /qemu-mipsel-static -E LD_PRELOAD=/httpd_parser_main.o \
/usr/bin/httpd /httpd_corpus.txt
bind: No such file or directory
[ dm_shmInit ] 086: shmget to exitst shared memory failed. Could not create shared memory.
rdp_getObj is called with: 4274932gdpr_getSystemGDPREntry Error
gdpr_getNewSystemGDPREntry OK
#Msg: getsockname error
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 24257
Set-Cookie: JSESSIONID=deleted; Expires=Thu, 01 Jan 1970 00:00:01 GMT; Path=/; HttpOnly
Connection: close
<!DOCTYPE html>
[...]
The harness works now and can be used to fuzz the function using AFL++ which will be explained in the next section.
As mentioned in the background, a seed corpus describes valid input samples, which serves as a foundational reference for generating new input data during the fuzzing process.
These inputs are typically chosen to represent different aspects of the target programs. The seed corpus is used by a fuzzer to generate mutated or evolved test cases that are then run against the target software to uncover bugs, crashes, or other problems. This corpus plays an important role in directing the fuzzer to relevant areas of the program and increasing the probability of detecting vulnerabilities or unexpected behaviors. By providing a diverse and representative set of initial inputs, the seed corpus helps the fuzzer explore different paths in the target faster and thereby increases coverage.
When it comes to functions parsing network data, these inputs can be created by using Wireshark to record different packets.
For the function, httpd_parse_main four different corpora were created. Each targeting different
paths in the binary. One example is the login request, which contains the username and password. For
this corpus, the harness had to be modified because TP-Link uses (weak) cryptography to "protect"
the password. For this, the password is encrypted in the browser using AES and then decrypted on the
backend. Whereby the password is generated in the browser and then encrypted using RSA. Then the
encrypted data is signed. Because a fuzzer can not create a signature or encrypt data, some
functions were overwritten and now just decodes the data from base64. For this, the data were first
extracted in plaintext from the browser using the debugger shown in Figure 4.

चित्र 4: एन्क्रिप्शन से पहले डेटा निकालना।
In the target, the function rsa_tmp_decrypt_bypart was then overwritten to replace the logic from
decrypting the data to just decoding from base64.```c
// Replacing the logic with b64_decode
int rsa_tmp_decrypt_bypart(uint8_t *input, int input_len, uint8_t *output) { // other params just key data
int (*b64_decode)(uint8_t *, int, uint8_t *, int) = (void *) 0x0040bf00;
b64_decode(output, 0x1000, input, input_len);
int * seqnumber = (int *) 0x00444db0;
*seqnumber = 0x3ac28e29-input_len+12;
return 0; // says it was okay
}
<p style="text-align: center">कोड 12: फ़ंक्शन rsa_tmp_decrypt_bypart अब डेटा को डिक्रिप्ट करने के बजाय सिर्फ़ base64 डिकोड करता है।</p>
कॉर्पस निष्पादित करते समय, लक्ष्य फ़ंक्शन हमेशा "408
Request Timeout" त्रुटि वाला एक HTML दस्तावेज़ लौटाता है। Ghidra और GDB का उपयोग करके समस्या की पहचान की जा सकी। त्रुटि हमेशा
`http_stream_fgets` फ़ंक्शन कॉल के बाद होती है। समस्याग्रस्त पंक्ति लाइन ब्रेक कैरेक्टर `\n` की जाँच थी।```c
if (((cVar1 == '\n') && (param_3 < pcVar4)) && (pcVar4[-1] == '\r')) {
यह स्थिति सुनिश्चित करती है कि हर लाइन ब्रेक के बाद, एक कैरिज रिटर्न आवश्यक रूप से होना चाहिए। कैरिज रिटर्न जोड़ने के बाद, सभी निर्मित कॉर्पोरा ने काम किया।
पिछले अनुभाग में, हमने कई हार्नेस विकसित किए और उन्हें QEMU का उपयोग करके निष्पादित किया। इस अनुभाग में,
QEMU को AFL++ द्वारा प्रतिस्थापित किया जाता है, जो उत्पन्न कॉर्पोरा को लक्ष्य
फ़ंक्शन को फ़ज़ करने के लिए बीज इनपुट के रूप में प्राप्त करता है। "फ़ज़िंग वातावरण" अनुभाग में, एक डॉकर इमेज बनाई गई थी जो पहले से ही AFL++
को GitHub से प्राप्त करती है और फिर एक AFL++-प्रदत्त स्क्रिप्ट का उपयोग करके QEMU का एक पैच किया हुआ संस्करण बनाती है। इसलिए AFL++ को
अब निम्नलिखित कमांड के साथ शुरू किया जा सकता है, जो विभिन्न पैरामीटर प्राप्त करता है, जैसे -Q जो AFL++ को बताता है
कि QEMU के पैच किए गए संस्करण का उपयोग करें।```sh
QEMU_LD_PREFIX=/share/root AFL_PRELOAD=/share/root/httpd_parser_main.o
/AFLplusplus/afl-fuzz -Q
-i /share/root/corpus/httpd/ -o /share/afl-out/httpd/
-- /share/root/usr/bin/httpd @@
<p style="text-align: center">कोड 13: हार्नेस और <code>afl-fuzz</code> का उपयोग करके बाइनरी <code>httpd</code> को फज़ करना।</p>
पहले की तरह, `chroot` कमांड अब आवश्यक नहीं है और इसे `QEMU_LD_PREFIX` वेरिएबल द्वारा प्रतिस्थापित किया जाता है। यह QEMU को बताता है कि साझा ऑब्जेक्ट्स कहाँ खोजने हैं। साथ ही, `LD_PRELOAD` वेरिएबल को AFL-विशिष्ट संस्करण `AFL_PRELOAD` द्वारा प्रतिस्थापित किया जाता है। कमांड में अंतिम तर्क दो `@` वर्ण हैं। उन्हें AFL++ द्वारा एक फ़ाइल पथ से बदल दिया जाएगा जिसमें फ़ज़िंग इनपुट होता है। आरंभ होने पर, AFL++ [Figure 5](#f5) में दिखाए गए टर्मिनल UI का उपयोग करके प्रगति दिखाता है।
<div id="f5"></div>
<figure>
<p><img src="https://assets.kitploit.com/production/public/readmes/48851/ac4b12fcf84c2041c9edc2a0871c5bb89b576d976b2eba0a2d0f843e7e708795.png" style="width:95.0%" /></p>
<figcaption><p style="text-align: center">चित्र 5: AFL++ से स्टेटस स्क्रीन।</p></figcaption>
</figure>
`AFL++` स्टेटस स्क्रीन वर्तमान फ़ज़िंग प्रक्रिया में आवश्यक अंतर्दृष्टि प्रदान करती है। `AFL++` के दस्तावेज़ों में स्टेटस स्क्रीन में उपयोग किए गए शब्दों का एक अच्छा अवलोकन है
[\[afl-screen\]](https://aflplus.plus/docs/status_screen/)। निम्नलिखित पर्यावरण चरों के साथ कॉर्पस को डीबग करते समय, UI को अक्षम किया जा सकता है और `AFL_DEBUG` के साथ विस्तृत लॉगिंग सक्षम की जा सकती है, जो वर्तमान फ़ज़र इनपुट और लक्ष्य प्रोग्राम से `stdout` दिखाती है।```sh
export AFL_DEBUG=1 && export AFL_NO_UI=1
unset AFL_DEBUG && unset AFL_NO_UI
जैसा कि चित्र 5 में दिखाया गया है, किसी बाइनरी को फज़ करने में काफी समय लग सकता है। दस्तावेज़ के अनुसार इसे "दिनों या हफ्तों तक चलने की उम्मीद की जानी चाहिए" और "कुछ जॉब को महीनों तक चलने की अनुमति दी जाएगी।" आवश्यक समय में सुधार के लिए, exec गति 100 execs/sec से ऊपर होनी चाहिए। उदाहरण के लिए, जब लक्ष्य httpd_main_parser को फज़ किया गया था, तो शुरुआत में exec गति लगभग 30/sec थी। गति में सुधार के लिए, लक्ष्य बाइनरी में संदिग्ध फ़ंक्शनों की खोज की गई, जो संभवतः धीमेपन का कारण हैं। संदिग्ध फ़ंक्शनों में से एक rsa_gdpr_generate_key था क्योंकि RSA कुंजी उत्पन्न करना धीमा माना जाता है। फ़ंक्शन को अधिलेखित करने के बाद, गति बढ़कर 600 निष्पादन प्रति सेकंड हो गई।
एक संकेतक जो यह बताने में मदद करता है कि फज़िंग कब बंद करनी है, वह cycle counter है। AFL++ उस संख्या को हरे रंग में हाइलाइट करेगा जब "फज़र ने कुछ समय से कोई गतिविधि नहीं देखी है," जो फज़र को रोकने का निर्णय लेने में मदद करता है।
लेकिन सबसे दिलचस्प संख्या शायद "total crashes" है। यह दिखाता है कि प्रोग्राम कब वर्तमान फज़िंग इनपुट के कारण क्रैश होता है और यह संभवतः मेमोरी-संबंधित बग है। यह सत्यापित करने के लिए कि यह वास्तविक बग है, बग की स्थिति खोजने के लिए gdb का फिर से उपयोग किया जा सकता है।
फज़िंग सुरक्षा कमजोरियों को खोजने का सबसे प्रभावी तरीका हो सकता है। इस टर्म पेपर में, तीन अलग-अलग फ़ंक्शनों को फज़ किया गया, लेकिन कोई नहीं मिला। जबकि ब्लैक बॉक्स फज़िंग सेटअप स्वयं उतना जटिल और समय लेने वाला नहीं है, एक शक्तिशाली लक्ष्य ढूंढना और एक कार्यशील हार्नेस विकसित करना समय लेने वाला है। अधिकांश समय, हार्नेस को डीबग करना पड़ता है, और फिर बाइनरी में अंतर्निहित तर्क को उलटना पड़ता है, जिसमें फिर से लंबा समय लगता है।