
एज-कवरेज-निर्देशित फज़र PHP लाइब्रेरीज़ के लिए जो क्रैश, टाइमआउट और चेतावनियों के माध्यम से बग का पता लगाता है। कोरपस प्रबंधन, क्रैश न्यूनीकरण और कोड कवरेज रिपोर्ट का समर्थन करता है।
यह लाइब्रेरी PHP के लिए एक फ़ज़र लागू करती है, जिसका उपयोग लाइब्रेरीज़ (विशेष रूप से पार्सिंग लाइब्रेरीज़) में बग खोजने के लिए किया जा सकता है, उन्हें "यादृच्छिक" इनपुट खिलाकर। एज कवरेज इंस्ट्रुमेंटेशन से प्रतिक्रिया का उपयोग "यादृच्छिक" इनपुट के चुनाव को निर्देशित करने के लिए किया जाता है, ताकि नए कोड पथ देखे जा सकें।
Phar (अनुशंसित): आप इस लाइब्रेरी का एक phar पैकेज releases page से डाउनलोड कर सकते हैं। phar का उपयोग अनुशंसित है, क्योंकि यह PHP-Parser का उपयोग करने वाली लाइब्रेरीज़ के साथ निर्भरता विरोध से बचाता है।
Composer: composer global require nikic/php-fuzzer
सबसे पहले, लक्ष्य फ़ंक्शन की परिभाषा आवश्यक है। यहाँ microsoft/tolerant-php-parser में बग खोजने के लिए एक उदाहरण लक्ष्य है:
<?php // target.php
/** @var PhpFuzzer\Config $config */
require 'path/to/tolerant-php-parser/vendor/autoload.php';
// Required: The target accepts a single input string and runs it through the tested
// library. The target is allowed to throw normal Exceptions (which are ignored),
// but Error exceptions are considered as a found bug.
$parser = new Microsoft\PhpParser\Parser();
$config->setTarget(function(string $input) use($parser) {
$parser->parseSourceFile($input);
});
// Optional: Many targets don't exhibit bugs on large inputs that can't also be
// produced with small inputs. Limiting the length may improve performance.
$config->setMaxLen(1024);
// Optional: A dictionary can be used to provide useful fragments to the fuzzer,
// such as language keywords. This is particularly important if these
// cannot be easily discovered by the fuzzer, because they are handled
// by a non-instrumented PHP extension function such as token_get_all().
$config->addDictionary('example/php.dict');
फ़ज़र को प्रारंभिक "दिलचस्प" इनपुट के एक कोरपस के विरुद्ध चलाया जाता है, जिसे उदाहरण के लिए मौजूदा यूनिट परीक्षणों के आधार पर बीजित किया जा सकता है। यदि कोई कोरपस निर्दिष्ट नहीं है, तो इसके बजाय एक अस्थायी कोरपस निर्देशिका बनाई जाएगी।
# Run without initial corpus
php-fuzzer fuzz target.php
# Run with initial corpus (one input per file)
php-fuzzer fuzz target.php corpus/
यदि फ़ज़िंग बाधित होती है, तो उसी कोरपस निर्देशिका को निर्दिष्ट करके इसे बाद में फिर से शुरू किया जा सकता है।
एक बार क्रैश मिल जाने पर, इसे crash-HASH.txt फ़ाइल में लिखा जाता है। यह उसी रूप में प्रदान किया जाता है जिस रूप में यह मूल रूप से पाया गया था, जो अनावश्यक रूप से जटिल हो सकता है और इसमें क्रैश से संबंधित नहीं कुछ अंश शामिल हो सकते हैं। इस प्रकार, आप संभवतः पहले क्रैशिंग इनपुट को कम करना चाहेंगे:
php-fuzzer minimize-crash target.php crash-HASH.txt
यह क्रमशः छोटी minimized-HASH.txt फ़ाइलों का एक अनुक्रम उत्पादित करेगा। यदि आप क्रैशिंग इनपुट के लिए उत्पन्न अपवाद ट्रेस को जल्दी से जांचना चाहते हैं, तो आप run-single कमांड का उपयोग कर सकते हैं:
php-fuzzer run-single target.php minimized-HASH.txt
अंत में, एक HTML कोड कवरेज रिपोर्ट उत्पन्न करना संभव है, जो दिखाती है कि किसी दिए गए कोरपस से इनपुट निष्पादित करते समय लक्ष्य में कौन से कोड ब्लॉक हिट होते हैं:
php-fuzzer report-coverage target.php corpus/ coverage_dir/
इसके अतिरिक्त कॉन्फ़िगरेशन विकल्प php-fuzzer --help से दिखाए जा सकते हैं।
जब फ़ज़र चल रहा होता है, तो यह आउटपुट की एक एकल पंक्ति का उपयोग करके लगातार अपनी स्थिति रिपोर्ट करता है। इस पंक्ति में निम्नलिखित क्रम में ये भाग शामिल हैं:
NEW या REDUCED: वह कार्रवाई जिसने इस स्थिति पंक्ति को ट्रिगर किया। NEW इंगित करता है कि कोरपस में एक नया इनपुट जोड़ा गया था जबकि REDUCED इंगित करता है कि मौजूदा कोरपस प्रविष्टि को एक छोटे इनपुट से बदल दिया गया।run: N: फ़ज़र शुरू होने के बाद से किए गए फ़ज़िंग पुनरावृत्तियों (लक्ष्य निष्पादन) की कुल संख्या(N/s): वर्तमान निष्पादन गति, प्रति सेकंड रन में मापी गईft: N: अब तक खोजी गई अद्वितीय विशेषताओं की कुल संख्या(N/s): फ़ज़र शुरू होने के बाद से प्रति सेकंड खोजी गई नई विशेषताओं की औसत संख्याcorp: N: वर्तमान में कोरपस में संग्रहीत दिलचस्प इनपुट की संख्या(%s): कोरपस में सभी इनपुट का कुल आकारlen: %d/%d: पहली संख्या वर्तमान इनपुट की लंबाई (बाइट्स में) है जिसने कार्रवाई को ट्रिगर किया, दूसरी संख्या वर्तमान अधिकतम अनुमत इनपुट लंबाई हैt: फ़ज़र शुरू होने के बाद से कुल बीता हुआ समय, सेकंड मेंफ़ज़र डिफ़ॉल्ट रूप से तीन प्रकार के बग का पता लगाता है:
Error अपवाद। जहाँ Exception अपवादों को दोषपूर्ण इनपुट का सामान्य परिणाम माना जाता है, वहीं अनकॉट Error अपवाद हमेशा प्रोग्रामिंग त्रुटि का संकेत देते हैं। वे आमतौर पर PHP द्वारा ही उत्पन्न होते हैं, उदाहरण के लिए null पर एक विधि कॉल करते समय।Error अपवादों में परिवर्तित करता है।pcntl_alarm() और एक एसिंक्रोनस सिग्नल हैंडलर का उपयोग करके प्राप्त किया जाता है जो टाइमआउट पर एक Error फेंकता है।विशेष रूप से, इनमें से कोई भी यह जाँच नहीं करता है कि लक्ष्य का आउटपुट सही है या नहीं, वे केवल यह निर्धारित करते हैं कि लक्ष्य अत्यधिक दुर्व्यवहार नहीं करता है। आउटपुट शुद्धता की जाँच करने का एक तरीका दो अलग-अलग कार्यान्वयनों की तुलना करना है जिनसे समान परिणाम उत्पन्न होने चाहिए:
$fuzzer->setTarget(function(string $input) use($parser1, $parser2) {
$result1 = $parser1->parse($input);
$result2 = $parser2->parse($input);
if ($result1 != $result2) {
throw new Error('Results do not match!');
}
});
इस फ़ज़र के कई तकनीकी विवरण LLVM प्रोजेक्ट के libFuzzer पर आधारित हैं। निम्नलिखित कुछ कार्यान्वयन विवरणों का वर्णन करता है।
कुशलतापूर्वक काम करने के लिए, फ़ज़िंग को किसी विशेष फ़ज़िंग इनपुट का परीक्षण करते समय निष्पादित किए गए कोड-पथों के बारे में प्रतिक्रिया की आवश्यकता होती है। यह कवरेज प्रतिक्रिया फ़ज़िंग लक्ष्य को "इंस्ट्रुमेंट" करके एकत्र की जाती है। include-interceptor लाइब्रेरी का उपयोग सभी शामिल फ़ाइलों के कोड को गतिशील रूप से बदलने के लिए किया जाता है। PHP-Parser लाइब्रेरी का उपयोग कोड को पार्स करने और उन सभी स्थानों को खोजने के लिए किया जाता है जहाँ अतिरिक्त इंस्ट्रुमेंटेशन कोड डालने की आवश्यकता होती है।
प्रत्येक बुनियादी ब्लॉक के अंदर, निम्नलिखित कोड डाला जाता है, जहाँ BLOCK_INDEX एक अद्वितीय, प्रति-ब्लॉक पूर्णांक है:
$___key = (\PhpFuzzer\FuzzingContext::$prevBlock << 28) | BLOCK_INDEX;
\PhpFuzzer\FuzzingContext::$edges[$___key] = (\PhpFuzzer\FuzzingContext::$edges[$___key] ?? 0) + 1;
\PhpFuzzer\FuzzingContext::$prevBlock = BLOCK_INDEX;
यह मानता है कि ब्लॉक इंडेक्स अधिकतम 28-बिट बड़ा है और निष्पादन के दौरान देखे गए (prev_block, cur_block) जोड़ों की संख्या की गणना करता है। उत्पन्न कोड दुर्भाग्य से काफी महंगा है, जो अप्रारंभीकृत एज काउंट्स और स्थैतिक गुणों के उपयोग से निपटने की आवश्यकता के कारण है। भविष्य में, एक PHP एक्सटेंशन बनाना संभव होगा जो कवरेज प्रतिक्रिया को अधिक कुशलता से एकत्र कर सके।
कुछ मामलों में, बुनियादी ब्लॉक एक्सप्रेशन का हिस्सा होते हैं, ऐसी स्थिति में हम आसानी से अतिरिक्त कोड नहीं डाल सकते। इन मामलों में हम इसके बजाय एक ऐसी विधि में कॉल डालते हैं जिसमें उपरोक्त कोड होता है:
if ($foo && $bar) { ... }
// becomes
if ($foo && \PhpFuzzer\FuzzingContext::traceBlock(BLOCK_INDEX, $bar)) { ... }
भविष्य में, तुलनाओं को भी इंस्ट्रुमेंट करना लाभदायक होगा, ताकि हम स्वचालित रूप से $foo == "SOME_STRING" जैसी तुलनाओं से शब्दकोश प्रविष्टियाँ निर्धारित कर सकें।
फ़ज़िंग इनपुट को "दिलचस्प" माना जाता है यदि उनमें नई विशेषताएँ हों जो कोरपस में पहले से मौजूद अन्य इनपुट के साथ नहीं देखी गई हैं। यह लाइब्रेरी विशेषताओं के रूप में मोटे दाने वाली एज हिट काउंट का उपयोग करती है:
ft = (approx_hits << 56) | (prev_block << 28) | cur_block
अनुमानित हिट काउंट वास्तविक हिट काउंट को 8 श्रेणियों (AFL पर आधारित) तक कम कर देता है:
0: 0 hits
1: 1 hit
2: 2 hits
3: 3 hits
4: 4-7 hits
5: 8-15 hits
6: 16-127 hits
7: >=128 hits
इस प्रकार, प्रत्येक इनपुट विशेषताओं का प्रतिनिधित्व करने वाले पूर्णांकों के एक सेट से जुड़ा होता है। इसके अतिरिक्त, इसमें "अद्वितीय विशेषताओं" का एक सेट होता है, जो इनपुट के परीक्षण के समय किसी भी अन्य कोरपस इनपुट में नहीं देखी गई विशेषताएँ हैं।
यदि किसी इनपुट में अद्वितीय विशेषताएँ हैं, तो इसे कोरपस (NEW) में जोड़ा जाता है। यदि किसी इनपुट A को उत्परिवर्तित करके इनपुट B बनाया गया था, लेकिन इनपुट B छोटा है और इसमें इनपुट A की सभी अद्वितीय विशेषताएँ हैं, तो A को कोरपस में B द्वारा बदल दिया जाता है (REDUCE)।
प्रत्येक पुनरावृत्ति पर, वर्तमान कोरपस से एक यादृच्छिक इनपुट चुना जाता है, और फिर उत्परिवर्तकों के अनुक्रम का उपयोग करके उत्परिवर्तित किया जाता है। निम्नलिखित उत्परिवर्तक (libFuzzer से लिए गए) वर्तमान में कार्यान्वित हैं:
EraseBytes: कुछ बाइट्स हटाएं।InsertByte: एक नई यादृच्छिक बाइट डालें।InsertRepeatedBytes: एक यादृच्छिक बाइट को कई बार दोहराकर डालें।ChangeByte: एक बाइट को यादृच्छिक बाइट से बदलें।ChangeBit: एक एकल बिट फ्लिप करें।ShuffleBytes: एक छोटी उपस्ट्रिंग को फेरबदल करें।ChangeASCIIInt: एक ASCII पूर्णांक को बढ़ाकर/घटाकर/दोगुना करके/आधा करके बदलें।ChangeBinInt: एक बाइनरी पूर्णांक को एक छोटी यादृच्छिक राशि जोड़कर बदलें।CopyPart: स्ट्रिंग के एक भाग को दूसरे भाग में कॉपी करें, या तो अधिलेखित करके या सम्मिलित करके।CrossOver: एकाधिक रणनीतियों के साथ किसी अन्य कोरपस प्रविष्टि के साथ क्रॉस ओवर करें।AddWordFromManualDictionary: शब्दकोश (यदि कोई हो) से एक शब्द सम्मिलित या अधिलेखित करें।उत्परिवर्तन अधिकतम लंबाई की बाधा के अधीन है। जहाँ लक्ष्य द्वारा एक समग्र अधिकतम लंबाई निर्दिष्ट की जा सकती है (setMaxLength()), वहीं फ़ज़र स्वचालित लंबाई नियंत्रण (--len-control-factor) भी करता है। अधिकतम लंबाई प्रारंभ में बहुत कम मान पर सेट की जाती है और फिर पिछले len_control_factor * log(maxlen) रनों में कोई कार्रवाई (NEW या REDUCE) नहीं होने पर log(maxlen) द्वारा बढ़ा दी जाती है।
लंबाई नियंत्रण कारक जितना अधिक होगा, फ़ज़र उतनी ही अधिक आक्रामक रूप से लंबे इनपुट की अनुमति देने से पहले छोटे इनपुट का पता लगाएगा। यह उत्पन्न कोरपस के आकार को काफी कम कर देता है, लेकिन प्रारंभिक अन्वेषण को धीमा कर देता है।
mem: PHP प्रक्रिया का वर्तमान मेमोरी उपयोग