
libFuzzer लक्ष्यों के साथ संगत वितरित कवरेज-निर्देशित फ़ज़िंग इंजन; हजारों समवर्ती जॉब्स तक स्केल होता है, बग्स उजागर करने के लिए सैनिटाइज़र और कॉर्पस डिस्टिलेशन का उपयोग करता है।
क्यों नहीं? हम वर्तमान में कुछ बहुत बड़े और बहुत धीमे लक्ष्यों को फ़ज़ करने का प्रयास कर रहे हैं, जिनके लिए libFuzzer, AFL, और इसी तरह के उपकरण आवश्यक रूप से अच्छी तरह से स्केल नहीं करते हैं। हमारे प्रेरक उदाहरणों में से एक के लिए SiliFuzz देखें। Centipede पर काम करते हुए हम बड़े पैमाने पर विभेदक फ़ज़िंग (differential fuzzing) के नए तरीकों के साथ प्रयोग करने की योजना बना रहे हैं, जो मौजूदा फ़ज़िंग इंजन करने की कोशिश नहीं करते हैं।
उल्लेखनीय विशेषताएँ:
LLVMFuzzerTestOneInput() का उपयोग करने के लिए आपको केवल अपने लक्ष्य को Centipede के कंपाइलर और लिंकर विकल्पों के साथ बनाने की आवश्यकता है।एक प्रोग्राम जो किसी लक्ष्य के लिए इनपुट की अनंत धारा उत्पन्न करता है और निष्पादन को व्यवस्थित (orchestrate) करता है।
एक बाइनरी, एक लाइब्रेरी, एक API, या बल्कि कोई भी चीज़ जो इनपुट के लिए बाइट्स का उपभोग कर सकती है और आउटपुट के रूप में किसी प्रकार का कवरेज डेटा उत्पन्न कर सकती है। libFuzzer का लक्ष्य Centipede का लक्ष्य हो सकता है। और पढ़ें यहाँ।
बाइट्स का एक अनुक्रम जो किसी लक्ष्य को खिलाया जा सकता है। इनपुट बाइट्स का एक मनमाना (arbitrary) समूह हो सकता है, या कुछ संरचित डेटा, जैसे कि सीरियलाइज़्ड प्रोटो (serialized proto)।
एक संख्या जो लक्ष्य के कुछ अद्वितीय व्यवहार का प्रतिनिधित्व करती है। उदाहरण के लिए, एक फ़ीचर 1234567 इस तथ्य का प्रतिनिधित्व कर सकता है कि लक्ष्य में बेसिक ब्लॉक संख्या 987 को 7 बार निष्पादित किया गया है। किसी लक्ष्य के साथ एक इनपुट निष्पादित करते समय, फ़ज़र निष्पादन के दौरान देखे गए फ़ीचर्स को एकत्र करता है।
एक विशिष्ट इनपुट से जुड़े फ़ीचर्स का एक सेट।
किसी दिए गए इनपुट को निष्पादित करते समय लक्ष्य के व्यवहार के बारे में कुछ जानकारी। कवरेज को आमतौर पर फ़ीचर सेट के रूप में दर्शाया जाता है जिसे इनपुट ने लक्ष्य में ट्रिगर किया है।
एक फ़ंक्शन जो इनपुट के रूप में बाइट्स लेता है और इनपुट का एक छोटा यादृच्छिक उत्परिवर्तन (mutation) आउटपुट करता है। यह भी देखें: संरचना-जागरूक फ़ज़िंग।
एक फ़ंक्शन जो जानता है कि किसी लक्ष्य में इनपुट कैसे फीड करना है और बदले में कवरेज कैसे प्राप्त करना है (अर्थात निष्पादित करना)।
एक अनुकूलन योग्य फ़ज़िंग इंजन जो उपयोगकर्ता को म्यूटेटर और एक्ज़ीक्यूटर को बदलने (substitute) की अनुमति देता है।
एक लाइब्रेरी जो Centipede फ़ज़र द्वारा अपेक्षित एक्ज़ीक्यूटर इंटरफ़ेस को लागू करती है। रनर जानता है कि sancov-इंस्ट्रूमेंटेड लक्ष्य को कैसे चलाना है, परिणामी कवरेज को एकत्र करना है, और इसे Centipede को वापस पास करना है। संभावित Centipede फ़ज़ लक्ष्यों को इस लाइब्रेरी के साथ लिंक किया जा सकता है ताकि उन्हें Centipede द्वारा चलाने योग्य बनाया जा सके।
इनपुट्स का एक सेट।
एक बड़े कॉर्पस का एक उपसमुच्चय चुनने की प्रक्रिया, जिससे उपसमुच्चय में मूल कॉर्पस के समान कवरेज फ़ीचर्स हों।
एक फ़ाइल जो कॉर्पस के एक उपसमुच्चय का प्रतिनिधित्व करती है और एक अन्य फ़ाइल जो कॉर्पस के उसी उपसमुच्चय के लिए फ़ीचर सेट का प्रतिनिधित्व करती है।
शार्ड B को शार्ड A में मर्ज करने का अर्थ है: शार्ड B के प्रत्येक इनपुट के लिए जिसमें शार्ड A में अनुपस्थित फ़ीचर्स हैं, उस इनपुट को A में जोड़ें।
एक एकल फ़ज़र प्रक्रिया। एक जॉब केवल एक शार्ड में लिखता है, लेकिन कई शार्ड्स पढ़ सकता है।
एक स्थानीय या दूरस्थ निर्देशिका जिसमें फ़ज़र द्वारा उत्पादित या उपभोग किया गया डेटा होता है।
git clone https://github.com/google/centipede.git
cd centipede
CENTIPEDE_SRC=`pwd`
BIN_DIR=$CENTIPEDE_SRC/bazel-bin
bazel build -c opt :all
अगले चरणों के लिए आपको क्या चाहिए:
$BIN_DIR/centipede - इंजन (फ़ज़र) की बाइनरी।$BIN_DIR/libcentipede_runner.pic.a - वह लाइब्रेरी जिसे आपको अपने फ़ज़ लक्ष्य (रनर) के साथ लिंक करने की आवश्यकता है।$CENTIPEDE_SRC/clang-flags.txt - लक्ष्य के लिए अनुशंसित clang कंपाइलेशन फ़्लैग्स।आप इन फ़ाइलों को जहाँ हैं वहाँ रख सकते हैं या उन्हें कहीं और कॉपी कर सकते हैं।
हम लक्ष्य बनाने के दो उदाहरण प्रदान करते हैं: एक छोटा सिंगल-फ़ाइल लक्ष्य और libpng। एक बार जब आप अपना लक्ष्य बना लें, तो फ़ज़ लक्ष्य चलाने के चरण पर जाएँ।
यह उदाहरण Centipede रिपॉजिटरी में शामिल सरल उदाहरण फ़ज़ लक्ष्यों में से एक का उपयोग करता है, जिन्हें puzzles (पहेलियाँ) भी कहा जाता है।
नोट: नीचे दिए गए कमांड $CENTIPEDE_SRC/clang-flags.txt के फ़्लैग्स का उपयोग करते हैं। आप इंस्ट्रूमेंटेशन फ़्लैग्स का कोई अन्य सेट चुन सकते हैं: clang-flags.txt केवल एक सरल डिफ़ॉल्ट विकल्प प्रदान करता है।
FUZZ_TARGET=byte_cmp_4 # or any other source under $CENTIPEDE_SRC/puzzles
clang++ @$CENTIPEDE_SRC/clang-flags.txt -c $CENTIPEDE_SRC/puzzles/$FUZZ_TARGET.cc -o $BIN_DIR/$FUZZ_TARGET.o
यह चरण अभी-अभी बने फ़ज़ लक्ष्य को libcentipede_runner.pic.a और अन्य आवश्यक लाइब्रेरीज़ के साथ लिंक करता है।
clang++ $BIN_DIR/$FUZZ_TARGET.o $BIN_DIR/libcentipede_runner.pic.a \
-ldl -lrt -lpthread -o $BIN_DIR/$FUZZ_TARGET
चलाने के चरण पर जाएँ।
LIBPNG_BRANCH=v1.6.37 # You can experiment with other branches if you'd like
git clone --branch $LIBPNG_BRANCH --single-branch https://github.com/glennrp/libpng.git
cd libpng
CC=clang CFLAGS=@$CENTIPEDE_SRC/clang-flags.txt ./configure --disable-shared
make -j
FUZZ_TARGET=libpng_read_fuzzer
clang++ -include cstdlib \
./contrib/oss-fuzz/$FUZZ_TARGET.cc \
./.libs/libpng16.a \
$BIN_DIR/libcentipede_runner.pic.a \
-ldl -lrt -lpthread -lz \
-o $BIN_DIR/$FUZZ_TARGET
स्थानीय रूप से चलाने पर पूर्ण स्केल नहीं मिलेगा, लेकिन यह फ़ज़र विकास चरण के दौरान उपयोगी हो सकता है। हम अनुशंसा करते हैं कि नेटवर्क फ़ाइल सिस्टम पर तनाव डालने से बचने के लिए चलाने से पहले फ़ज़र और लक्ष्य दोनों को एक स्थानीय निर्देशिका में कॉपी कर लिया जाए।
WD=$HOME/centipede_run
mkdir -p $WD
नोट: Centipede की कुछ कार्यक्षमताओं को काम करने के लिए आपको अपने $PATH में llvm-symbolizer जोड़ने की आवश्यकता हो सकती है। सिंबलाइज़र को LLVM वितरण के भाग के रूप में स्थापित किया जा सकता है:
sudo apt install llvm
which llvm-symbolizer # normally /usr/bin/llvm-symbolizer
rm -rf $WD/*
$BIN_DIR/centipede --binary=$BIN_DIR/$FUZZ_TARGET --workdir=$WD --num_runs=100
कार्यशील निर्देशिका में क्या है देखें
tree $WD
...
├── <fuzz target name>-d9d90139ee2ccc687f7c9d5821bcc04b8a847df5
│ └── features.0
└── corpus.0
चेतावनी: --j फ़्लैग के लिए अपनी मशीन पर कोर की संख्या से अधिक न जाएँ।
rm -rf $WD/*
$BIN_DIR/centipede --binary=$BIN_DIR/$FUZZ_TARGET --workdir=$WD --num_runs=100 --j=5
कार्यशील निर्देशिका में क्या है देखें:
tree $WD
...
├── <fuzz target name>-d9d90139ee2ccc687f7c9d5821bcc04b8a847df5
│ ├── features.0
│ ├── features.1
│ ├── features.2
│ ├── features.3
│ └── features.4
├── corpus.0
├── corpus.1
├── corpus.2
├── corpus.3
└── corpus.4
प्रत्येक Centipede शार्ड आमतौर पर उन सभी फ़ीचर्स को कवर नहीं करता है जिन्हें पूरा कॉर्पस कवर करता है। कॉर्पस को डिस्टिल करने के लिए, एक Centipede प्रक्रिया को सभी शार्ड्स पढ़ने की आवश्यकता होगी। वर्तमान में, डिस्टिलेशन इस प्रकार काम करता है:
--distill_shards=N के साथ। इससे पहले N जॉब्स N स्वतंत्र डिस्टिल्ड कॉर्पस फ़ाइलें (प्रति जॉब एक) उत्पन्न करेंगे। प्रत्येक डिस्टिल्ड कॉर्पस में पूरे कॉर्पस के समान फ़ीचर्स होने चाहिए, लेकिन इन डिस्टिल्ड कॉर्पस के बीच इनपुट बहुत भिन्न हो सकते हैं।यदि आपको डिस्टिल्ड कॉर्पस को libFuzzer-शैली निर्देशिका (प्रति इनपुट एक फ़ाइल वाली स्थानीय निर्देशिका) में निर्यात करने की भी आवश्यकता है, तो --corpus_dir=DIR जोड़ें।
Centipede एक टेक्स्ट फ़ाइल के रूप में एक सरल कवरेज रिपोर्ट उत्पन्न करता है। शार्ड 123 वास्तविक फ़ज़िंग शुरू होने से पहले workdir/coverage-report-BINARY.000123.txt फ़ाइल उत्पन्न करता है, अर्थात रिपोर्ट कॉर्पस लोड करने के बाद शार्ड 123 द्वारा देखे गए कवरेज को दर्शाती है।
रिपोर्ट उन फ़ंक्शनों को दिखाती है जो पूरी तरह से कवर हैं (सभी कंट्रोल फ़्लो एज कम से कम एक बार देखे गए हैं), कवर नहीं हैं, या आंशिक रूप से कवर हैं। आंशिक रूप से कवर किए गए फ़ंक्शनों के लिए रिपोर्ट में सभी कवर और अनकवर एज की प्रतीकात्मक जानकारी होती है।
रिपोर्ट कुछ इस प्रकार दिखेगी:
FULL: FUNCTION_A a.cc:1:0
NONE: FUNCTION_BB bb.cc:1:0
PARTIAL: FUNCTION_CCC ccc.cc:1:0
+ FUNCTION_CCC ccc.cc:1:0
- FUNCTION_CCC ccc.cc:2:0
- FUNCTION_CCC ccc.cc:3:0
TBD