基于突变的模糊测试
文章目錄
- 0. 前言
- 1. 摘要
- 2. 被測試程序
- 3. 突變
- 4. 突變的模糊測試
- 5. 基于突變的覆蓋率引導(dǎo)的模糊測試
- 6. 測試程序
- 附錄
0. 前言
看論文The Art, Science, and Engineering of Fuzzing: A Survey 遇到一個名詞:動態(tài)符號執(zhí)行。
The term white-box fuzzing was introduced by Godefroid [87] in 2007 and refers to dynamic symbolic execution (DSE), which is a variant of symbolic execution [39], [126], [108].
白盒模糊測試一詞是Godefroid [87]在2007年提出的,指動態(tài)符號執(zhí)行(DSE),它是符號執(zhí)行的一種變體[39],[126],[108]。
我閱讀了下這篇:符號執(zhí)行入門
符號執(zhí)行的概念早在1975年[4]就提出了,但是真正得到實用,卻是在一種方式提出之后,即混合實際執(zhí)行和符號執(zhí)行,稱為concolic execution,是真正意義上的動態(tài)符號執(zhí)行。
Concolic執(zhí)行維護(hù)一個實際狀態(tài)和一個符號化狀態(tài):實際狀態(tài)將所有變量映射到實際值,符號狀態(tài)只映射那些有非實際值的變量。Concolic執(zhí)行首先用一些給定的或者隨機(jī)的輸入來執(zhí)行程序,收集執(zhí)行過程中條件語句對輸入的符號化約束,然后使用約束求解器去推理輸入的變化,從而將下一次程序的執(zhí)行導(dǎo)向另一條執(zhí)行路徑。簡單地說來,就是在已有實際輸入得到的路徑上,對分支路徑條件進(jìn)行取反,就可以讓執(zhí)行走向另外一條路徑。這個過程會不斷地重復(fù),加上系統(tǒng)化或啟發(fā)式的路徑選擇算法,直到所有的路徑都被探索,或者用戶定義的覆蓋目標(biāo)達(dá)到,或者時間開銷超過預(yù)計。
不怎么明白符號測試,所以,我打算看下Symbolic Fuzzing 。而它需要挺多的前置要求。
所以,我不得不一個一個完成。第一個需要完成的是Mutation-Based Fuzzing
我希望可以簡單的理解符號測試的基本概念,然后跳過夯實基礎(chǔ),直接可以使用符號測試的工具。
來源:Mutation-Based Fuzzing
建議閱讀原文,我這里僅僅整理下思路。我敲的相關(guān)代碼見:fuzzing倉庫
背景要求:模糊測試簡介| 代碼覆蓋率
1. 摘要
模糊測試中隨機(jī)生成的字符串大概率是無效的。這導(dǎo)致很多時間被浪費在無效的測試輸入中。
我們可以采用合法有效的輸入進(jìn)行初期的測試。這些有效的輸入,我們稱之為種子。
我們對種子進(jìn)行突變,將其中的bit(s)進(jìn)行突變(翻轉(zhuǎn)、刪除、增加)。由于突變的部分相對于某個種子的整體,變化較小。那么突變之后的種子,也很可能是合法的。
種子突變的越多,不合法的可能性越大。那么如何控制突變呢?這里引入基于覆蓋率引導(dǎo)的模糊測試。當(dāng)突變生成中輸入可以發(fā)現(xiàn)新路徑的時候,我們將該輸入放入倉庫中。其中,倉庫使用種子進(jìn)行初始化。這樣,突變會向覆蓋率增大的方向突變。覆蓋率越大,觸發(fā)crash的可能性越大。
2. 被測試程序
URL由多個元素構(gòu)成。
scheme://netloc/path?query#fragment- scheme is the protocol to be used, including http, https, ftp, file…
- netloc is the name of the host to connect to, such as www.google.com
- path is the path on that very host, such as search
- query is a list of key/value pairs, such as q=fuzzing
- fragment is a marker for a location in the retrieved document, such as #result
下面代碼用以檢查的URL的合法性。
如果是URL通過隨機(jī)的字符串來生成,那么生成的字符串大多數(shù)是無效的。
因而使用隨機(jī)生成字符串作為輸入的模糊測試,將會非常低效。
下文將會介紹,使用覆蓋率引導(dǎo)的模糊測試。
from urllib.parse import urlparsedef http_program(url):"""檢查url的合法性"""supported_schemes = ["http","https"]result = urlparse(url)if result.scheme not in supported_schemes:raise ValueError("scheme must be one of " + repr(supported_schemes))if result.netloc == '':raise ValueError("url netloc must not be empty")return Truedef is_valid_url(url):try:http_program(url)return Trueexcept ValueError:return False3. 突變
因為以前數(shù)學(xué)建模的時候接觸過點遺傳算法的概念。所以,看到這里甚是熟悉。
下面實現(xiàn)了隨機(jī)刪除、插入、翻轉(zhuǎn)。
import randomdef delete_random_character(s):"""在原字符串的基礎(chǔ)上隨機(jī)刪除一個字符,并返回;原字符串不變"""if s == "":return spos = random.randint(0,len(s)-1)return s[:pos] + s[pos+1:]def insert_random_character(s):"""在原字符串的基礎(chǔ)上隨機(jī)插入一個字符,并返回;原字符串不變"""pos = random.randint(0,len(s)-1)# randint 左右閉區(qū)間,randrange左閉右開# randrange()功能相當(dāng)于 choice(range(start, stop, step))random_char = chr(random.randrange(32,127)) return s[:pos] + random_char + s[pos:]def flip_random_character(s):"""在原字符串的基礎(chǔ)上隨機(jī)翻轉(zhuǎn)一個bit,并返回;原字符串不變"""if s == "":return spos = random.randint(0,len(s)-1)c = s[pos]bit = 1 << random.randint(0,6) # 注意這里只有七個位置可能翻轉(zhuǎn)new_c = chr(ord(c)^bit)return s[:pos] + new_c + s[pos+1:]def mutate(s):"""隨機(jī)選擇一個突變方式"""mutators = [delete_random_character,insert_random_character,flip_random_character]# print(type(mutators[0])) # 這里面存儲的是函數(shù)類型,有意思matator = random.choice(mutators)return matator(s)4. 突變的模糊測試
在Fuzzer類的基礎(chǔ)上,fuzz方法使用突變的方式,生成字符串。
from fuzzingbook.fuzzingbook_utils.Fuzzer import Fuzzerclass MutationFuzzer(Fuzzer):def __init__(self,seed, min_mutations=2,max_mutations=10):self.seed = seedself.min_mutations = min_mutationsself.max_mutations = max_mutationsself.reset()def reset(self):self.population = self.seedself.seed_index = 0def mutate(self,inp):return mutate(inp)# 從倉庫中隨機(jī)選擇一個進(jìn)行突變;# 倉庫使用種子進(jìn)行初始化(__init__中完成)# 這里并沒有將突變生成的內(nèi)容放入populationdef create_candidate(self):candidate = random.choice(self.population)trails = random.randint(self.min_mutations,self.max_mutations)for i in range(trails):candidate = self.mutate(candidate)return candidatedef fuzz(self):if self.seed_index < len(self.seed):# 使用種子self.inp = self.seed[self.seed_index]self.seed_index += 1else:# 使用突變生成的內(nèi)容self.inp = self.create_candidate()return self.inp5. 基于突變的覆蓋率引導(dǎo)的模糊測試
模糊測試的輸入,仍然使用上面突變的方式生成。
這里需要解決的是,如何判斷突變生成的輸入,是否發(fā)現(xiàn)了新的路徑。
下面是在繼承Runner類的類的一個對象中,統(tǒng)計覆蓋率。
class MutationCoverageFuzzer(MutationFuzzer):def reset(self):super().reset()self.coverages_seen = set()# Now empty; we fill this with seed in the first fuzz runsself.population = []def run(self, runner):"""Run function(inp) while tracking coverage.If we reach new coverage,add inp to population and its coverage to population_coverage"""result, outcome = super().run(runner)new_coverage = frozenset(runner.coverage())if outcome == Runner.PASS and new_coverage not in self.coverages_seen:# We have new coverageself.population.append(self.inp)self.coverages_seen.add(new_coverage)return result6. 測試程序
現(xiàn)在可以開始著手測試上面的URL代碼了。
首先,我們需要一個類,這個類可以運(yùn)行上面的URL代碼,并統(tǒng)計覆蓋率。
from fuzzingbook.fuzzingbook_utils.Fuzzer import Runner from fuzzingbook.fuzzingbook_utils.Coverage import Coverage,population_coverageclass FunctionRunner(Runner):def __init__(self,function):self.function = functiondef run_function(self,inp):return self.function(inp)def run(self,inp):try:result = self.run_function(inp)outcome = self.PASSexcept Exception:result = Noneoutcome = self.FAILreturn result,outcomeclass FunctionCoverageRunner(FunctionRunner):def run_function(self,inp):with Coverage() as cov:try:result = super().run_function(inp)except Exception as exc:self._coverage = cov.coverage()raise excself._coverage = cov.coverage()return resultdef coverage(self):return self._coverage測試下
http_runner = FunctionCoverageRunner(http_program) seed_input = "http://www.google.com/search?q=fuzzing" mutation_fuzzer = MutationCoverageFuzzer(seed=[seed_input]) mutation_fuzzer.runs(http_runner, trials=10000) mutation_fuzzer.population看下結(jié)果
all_coverage, cumulative_coverage = population_coverage(mutation_fuzzer.population, http_program)import matplotlib.pyplot as plt plt.plot(cumulative_coverage) plt.title('Coverage of urlparse() with random inputs') plt.xlabel('# of inputs') plt.ylabel('lines covered')附錄
類的UML圖代碼見倉庫
(導(dǎo)出來的圖像清晰度不夠)
總結(jié)
- 上一篇: 总结Android屏幕适配(源自简书:李
- 下一篇: Oracle EBS 预警系统管理