Fuzzilli 初識

PPT 資料

  • Motivation
    • 發(fā)現(xiàn)js引擎中的漏洞,比如JIT
  • Requirements
    • 提供的樣本算法有效

語法正確性

  • 基于語法的fuzz很容易實現(xiàn),比如domato
    • 基本思想:將js當作上下文無關文法語言,然后應用生成規(guī)則變異
    • 如下例子就是基于語法生成的fuzzer生成的樣本
...;
var v4 = new Array(42, v3, “foobar”);
for (var v5 = 0; v5 < 1000; v5++) {    
    v4 = v5 * 7;    
    var v6 = v4.slice(v1, v1, v2);
    }
...;
  • 但是以上例子可能出現(xiàn)異常 Exception: TypeError: v4.slice is not a function.,導致后邊的語句永遠不會被執(zhí)行。
    • 加入 try-catch? 但是會導致在JIT執(zhí)行代碼的時候和原有代碼有很大出路

高度語義正確性

  • 相對語法正確來說更難
  • 多個解決方案
    • 生成代碼階段執(zhí)行精確的類型檢查
    • 一步一步生成,每一步都檢測有效性
    • 使用變異方法,只保留語料庫中語義有效的樣本
  • 定義js代碼的合理變異

變異程序

  • 變異可能是在不同的level上進行的:源代碼、ast、bytecode
  • 觀測 程序的數(shù)據(jù)流和控制流變化的情況

FuzzIL

  • 定義一種IR語言
  • 觀測程序的控制流和數(shù)據(jù)流
  • 定義基于IL的變異
  • 將IL轉換為Js

變異:

  • 操作變異
  • 插入變異(生成新的代碼)
  • 輸入變異
  • 拼接變異(插入已有代碼)

最小化樣本

  • 上述變異只會增大樣本大小,在加入語料庫前執(zhí)行minim操作
    • 最簡單的做法就是一句一句刪除,看刪除到那一句會對最終結果造成影響

guided fuzzing

  • feedback system
  • 基于邊覆蓋

Architecture

  • Fuzzer
    • Corpus
    • Opt module
    • Mutator
    • Lifter
    • Minimizer
    • FeedBack

? 對于每一個target對應一個fuzzer實例,通過IPC或者network同步,程序可以從另一個實例導入,并與本地語料庫進行比較

  • 同步
    • Master----(IPC、network、task queue)--- worker、worker

A fuzzer instance (implemented in Fuzzer.swift) is made up of the following central components:

  • FuzzerCore: produces new programs from existing ones by applying mutations. Afterwards executes the produced samples and evaluates them.
  • ScriptRunner: executes programs of the target language.
  • Corpus: stores interesting samples and supplies them to the core fuzzer.
  • Environment: has knowledge of the runtime environment, e.g. the available builtins, property names, and methods.
  • Minimizer: minimizes crashing and interesting programs.
  • Evaluator: evaluates whether a sample is interesting according to some metric, e.g. code coverage.
  • Lifter: translates a FuzzIL program to the target language (JavaScript).

Furthermore, a number of modules are optionally available:

? The fuzzer is event-driven, with most of the interactions between different classes happening through events. Events are dispatched e.g. as a result of a crash or an interesting program being found, a new program being executed, a log message being generated and so on. See Events.swift for the full list of events. The event mechanism effectively decouples the various components of the fuzzer and makes it easy to implement additional modules.

? A FuzzIL program can be built up using a ProgramBuilder instance. A ProgramBuilder provides methods to create and append new instructions, append instructions from another program, retrieve existing variables, query the execution context at the current position (e.g. whether it is inside a loop), and more.

Scalability

There is one fuzzer instance per target process. This enables synchronous execution of programs and thereby simplifies the implementation of various algorithms such as consecutive mutations and minimization. Moreover, it avoids the need to implement thread-safe access to internal state, e.g. the corpus. Each fuzzer instance has its own dedicated OperationQueue, conceptually corresponding to a single thread. Every interaction with a fuzzer instance must then happen on the instance’s queue. This guarantees thread-safety as the queue is serial. For more details see the docs.

To scale, fuzzer instances can become workers, in which case they report newly found interesting samples and crashes to a master instance. In turn, the master instances also synchronize their corpus with the workers. Communication between masters and workers can happen in different ways, each implemented as a module:

  • Inter-thread communication: synchronize instances in the same process by enqueuing tasks to the other fuzzer’s DispatchQueue.
  • Inter-process communication (TODO): synchronize instances over an IPC channel.
  • Inter-machine communication: synchronize instances over a simple TCP-based protocol.

This design allows the fuzzer to scale to many cores on a single machine as well as to many different machines. As one master instance can quickly become overloaded if too many workers send programs to it, it is also possible to configure multiple tiers of master instances, e.g. one master instance, 16 intermediate masters connected to the master, and 256 workers connected to the intermediate masters.

安裝和運行

這里以V8為例,根據(jù)官方repo的readme依次執(zhí)行即可,checkout到特定的版本,根據(jù)以下步驟

The basic steps to use this fuzzer are:

  1. Download the source code for one of the supported JavaScript engines (currently JavaScriptCore, Spidermonkey, and v8).
  2. Apply the corresponding patch from the Targets/ directory. Also see the README.md in that directory.
  3. Compile the engine with coverage instrumentation (requires clang >= 4.0) as described in the README.
  4. Compile the fuzzer: swift build [-c release].
  5. Run the fuzzer: swift run [-c release] FuzzilliCli --profile=<profile> [other cli options] /path/to/jsshell. See also swift run FuzzilliCli --help.

最后跑起來的效果就是:

nevv@ubuntu:~/Browser/fuzz/fuzzilli-master$ .build/x86_64-unknown-linux/release/FuzzilliCli --profile=v8 ../../pwn/34c3_v9/v8/v8/v8/out/fuzzbuild/d8
[Cli] No filesystem storage configured, found crashes will be discarded!
[Coverage] Initialized, 560233 edges
[JavaScriptEnvironment] initialized static JS environment model
[Fuzzer] Initialized
[Fuzzer] Recommended timeout: at least 120ms. Current timeout: 250ms
[Fuzzer] Startup tests finished successfully
[Fuzzer] Let's go!

Fuzzer Statistics
-----------------
Total Samples:                430
Interesting Samples Found:    142
Valid Samples Found:          318
Corpus Size:                  143
Success Rate:                 73.95%
Timeout Rate:                 0.47%
Crashes Found:                0
Timeouts Hit:                 2
Coverage:                     4.17%
Avg. program size:            73.97
Connected workers:            0
Execs / Second:               114.18
Total Execs:                  7343

源碼概覽

目錄結構

nevv@ubuntu:~/Browser/fuzz$ tree fuzzilli-master
fuzzilli-master
├── CONTRIBUTING.md
├── Docs
│   └── ProcessingModel.md
├── LICENSE
├── Misc
│   ├── enum_properties.js
│   ├── Forkserver
│   │   ├── server.c
│   │   └── tester.c
│   └── REPRL
│       └── tester.c
├── Package.swift
├── README.md
├── Sources
│   ├── Fuzzilli
│   │   ├── Configuration.swift
│   │   ├── Core
│   │   │   ├── CodeGenerators.swift
│   │   │   ├── Component.swift
│   │   │   ├── Corpus.swift
│   │   │   ├── Environment.swift
│   │   │   ├── Events.swift
│   │   │   ├── FuzzerCore.swift
│   │   │   ├── JavaScriptEnvironment.swift
│   │   │   ├── Logging.swift
│   │   │   ├── ProgramBuilder.swift
│   │   │   └── Timers.swift
│   │   ├── Evaluation
│   │   │   ├── ProgramAspects.swift
│   │   │   ├── ProgramCoverageEvaluator.swift
│   │   │   └── ProgramEvaluator.swift
│   │   ├── Execution
│   │   │   ├── Execution.swift
│   │   │   ├── Forkserver.swift
│   │   │   ├── REPRL.swift
│   │   │   └── ScriptRunner.swift
│   │   ├── Fuzzer.swift
│   │   ├── FuzzIL
│   │   │   ├── AbstractInterpreter.swift
│   │   │   ├── Analyzer.swift
│   │   │   ├── Blocks.swift
│   │   │   ├── Instruction.swift
│   │   │   ├── Operations.swift
│   │   │   ├── Program.swift
│   │   │   ├── TypeSystem.swift
│   │   │   └── Variable.swift
│   │   ├── Lifting
│   │   │   ├── Expression.swift
│   │   │   ├── InliningPolicy.swift
│   │   │   ├── JavaScriptLifter.swift
│   │   │   ├── JSExpressions.swift
│   │   │   ├── Lifter.swift
│   │   │   └── ScriptWriter.swift
│   │   ├── Minimization
│   │   │   ├── BlockReducer.swift
│   │   │   ├── CallArgumentReducer.swift
│   │   │   ├── GenericInstructionReducer.swift
│   │   │   ├── InliningReducer.swift
│   │   │   ├── Minimizer.swift
│   │   │   ├── Reducer.swift
│   │   │   └── ReplaceReducer.swift
│   │   ├── Modules
│   │   │   ├── Module.swift
│   │   │   ├── NetworkSync.swift
│   │   │   ├── Statistics.swift
│   │   │   ├── Storage.swift
│   │   │   └── ThreadSync.swift
│   │   ├── Mutators
│   │   │   ├── BaseInstructionMutator.swift
│   │   │   ├── CombineMutator.swift
│   │   │   ├── ConcatMutator.swift
│   │   │   ├── GrowMutator.swift
│   │   │   ├── InputMutator.swift
│   │   │   ├── InsertionMutator.swift
│   │   │   ├── JITStressMutator.swift
│   │   │   ├── Mutator.swift
│   │   │   ├── OperationMutator.swift
│   │   │   └── SpliceMutator.swift
│   │   └── Util
│   │       ├── CInterop.swift
│   │       ├── Error.swift
│   │       ├── Misc.swift
│   │       ├── MovingAverage.swift
│   │       ├── OperationSource.swift
│   │       ├── Random.swift
│   │       ├── RingBuffer.swift
│   │       ├── VariableMap.swift
│   │       ├── VariableSet.swift
│   │       └── WeightedList.swift
│   ├── FuzzilliCli
│   │   ├── Arguments.swift
│   │   ├── main.swift
│   │   ├── Profiles
│   │   │   ├── JSCProfile.swift
│   │   │   ├── Profile.swift
│   │   │   ├── SpidermonkeyProfile.swift
│   │   │   └── V8Profile.swift
│   │   ├── Settings.swift
│   │   └── TerminalUI.swift
│   ├── libcoverage
│   │   ├── coverage.c
│   │   └── include
│   │       └── libcoverage.h
│   ├── libforkserver
│   │   ├── forkserver.c
│   │   └── include
│   │       └── libforkserver.h
│   ├── libreprl
│   │   ├── include
│   │   │   └── libreprl.h
│   │   └── libreprl.c
│   └── libsocket
│       ├── include
│       │   └── libsocket.h
│       └── socket.c
├── Targets
│   ├── JavaScriptCore
│   │   ├── fuzzbuild.sh
│   │   ├── README.md
│   │   └── webkit.patch
│   ├── Spidermonkey
│   │   ├── firefox.patch
│   │   ├── fuzzbuild.sh
│   │   └── README.md
│   └── V8
│       ├── fuzzbuild.sh
│       ├── README.md
│       └── v8.patch
└── Tests
    ├── FuzzilliTests
    │   ├── CodeGenerationTest.swift
    │   ├── InliningTest.swift
    │   ├── InterpreterTest.swift
    │   ├── MockFuzzer.swift
    │   ├── ProgramSerializationTest.swift
    │   ├── RingBufferTest.swift
    │   ├── TestUtils.swift
    │   ├── TypeSystemTest.swift
    │   ├── VariableMapTest.swift
    │   ├── VariableSetTest.swift
    │   └── XCTestManifests.swift
    └── LinuxMain.swift

31 directories, 111 files
FuzzilliCli
  • Fuzzilli的命令行工具,主要功能是:
    • 解析命令行參數(shù)并配置fuzz
    • 設置終端的UI (TerminalUI.swift)
    • 設置不同js引擎的profile(JSC、spidermonkey、V8)
│   ├── FuzzilliCli
│   │   ├── Arguments.swift
│   │   ├── main.swift
│   │   ├── Profiles
│   │   │   ├── JSCProfile.swift
│   │   │   ├── Profile.swift
│   │   │   ├── SpidermonkeyProfile.swift
│   │   │   └── V8Profile.swift
│   │   ├── Settings.swift
│   │   └── TerminalUI.swift
profiles

在profile文件中會定義 processArguments 和 codePrefix 、codeSuffix,比如v8:

let v8Profile = Profile(
    processArguments: ["--debug-code",
                       "--expose-gc",
                       "--single-threaded",
                       "--predictable",
                       "--allow-natives-syntax",
                       "--interrupt-budget=1024",
                       "--fuzzing",
                       "--reprl"],
    
    processEnv: [:],
    
    codePrefix: """
                function main() {
                """,
    
    codeSuffix: """
                }
                %NeverOptimizeFunction(main);
                main();
                """,
    
    crashTests: ["fuzzilli('FUZZILLI_CRASH', 0)", "fuzzilli('FUZZILLI_CRASH', 1)", "fuzzilli('FUZZILLI_CRASH', 2)"],
    
    additionalCodeGenerators: WeightedList<CodeGenerator>([
        (ForceV8TurbofanGenerator, 10),
    ]),
    
    additionalBuiltins: [
        "gc"                : .function([] => .undefined),
    ]
)
main
  • 初始化 REPRL
  • 初始化 Mutator,這里多插入了InsertionMutator,因為它更易產(chǎn)生無效樣本
  • 初始化 FuzzerCore
  • 初始化 codeGenerators 負責代碼生成 (defaultCodeGenerators + profile.additionalCodeGenerators,在V8中是ForceV8TurbofanGenerator)
  • 初始化 ProgramCoverageEvaluator 計算覆蓋率
  • 初始化 JavaScriptEnvironment,能夠加載可用的builtins函數(shù)
  • 初始化 JavaScriptLifter ,能夠將FuzzIL轉換成js代碼
  • 初始化 Corpus,語料庫,設置樣本最大/最小值
  • 初始化 Minimizer ,minimize crashes and interesting programs.
  • 構造 Fuzzer 實例
  • 初始化 TerminalUI
Settings
  • defaultCodeGenerators 不同code生成器所占的權重
  • 權重計算? 優(yōu)化?
    (IntegerLiteralGenerator,            2),
    (FloatLiteralGenerator,              1),
    (StringLiteralGenerator,             1),
    (BooleanLiteralGenerator,            1),
    (UndefinedValueGenerator,            1),
    (NullValueGenerator,                 1),
    (BuiltinGenerator,                   5),
    (BuiltinGenerator,                   5),
    (ObjectLiteralGenerator,             10),
    (ArrayLiteralGenerator,              10),
    (ObjectLiteralWithSpreadGenerator,   5),
    (ArrayLiteralWithSpreadGenerator,    5),
    (FunctionDefinitionGenerator,        15),
    (FunctionReturnGenerator,            3),
Fuzzilli
Fuzzer
  • 定義了Fuzzer的類,其中包括
    • Fuzzer的設置
    • 能夠處理的event
    • FuzzerCore執(zhí)行fuzz
    • 語料庫corpus
    • 最小化執(zhí)行用例的Minimizer等等
  • start
// runFor是函數(shù)標簽 提高代碼可讀性。_ 參數(shù)前綴代表調用該函數(shù)的時候可以不加該參數(shù)的名字
public func start(runFor maxIterations: Int) {
        precondition(isInitialized)
        assert(OperationQueue.current == queue)
        
        self.maxIterations = maxIterations
        
        self.runStartupTests()
        // 測試是不是都正確的配置
        logger.info("Let's go!")
        
        // Start fuzzing
        queue.addOperation {
           self.fuzzOne()
        }
    }
  • fuzzOne
    /// Performs one round of fuzzing.
    private func fuzzOne() {
        guard maxIterations == -1 || iterations < maxIterations else {
            return
        }
        iterations += 1
        
        core.fuzzOne()
        
        // Fuzz another one.
        // We only want do actual fuzzing if there is nothing else to do. For that
        // reason we enqueue the corresponding operations with low priority.
        let op = BlockOperation(block: self.fuzzOne)
        op.queuePriority = .veryLow
        queue.addOperation(op)
    }
  • execute
    • 提升FuzzIL,并通過runner來執(zhí)行
    /// Executes a program.
    ///
    /// This will first lift the given FuzzIL program to the target language, then use the configured script runner to execute it.
    ///
    /// - Parameters:
    ///   - program: The FuzzIL program to execute.
    ///   - timeout: The timeout after which to abort execution. If nil, the default timeout of this fuzzer will be used.
    /// - Returns: An Execution structure representing the execution outcome.
    public func execute(_ program: Program, withTimeout timeout: UInt32? = nil) -> Execution {
        assert(runner.isInitialized)
        assert(OperationQueue.current == queue)
        
        events.PreExecute.dispatch(with: program)
        
        let script: String
        if config.speedTestMode {
            script = lifter.lift(makeComplexProgram())
        } else {
            script = lifter.lift(program)
        }
        let execution = runner.run(script, withTimeout: timeout ?? config.timeout)
        
        events.PostExecute.dispatch(with: execution)
        
        return execution
    }

FuzzerCore

Generating and executing programs.

  • programPrefixGenerators 生成一些基本的變量類型

  • fuzzOne:變異后調用fuzzer的execute執(zhí)行樣本。

JavaScriptEnvironment
  • 定義了一些比較特殊的數(shù)字用來變異:比如smi的最大值、最大負數(shù)等
  • 定義了一些特殊的jsTypeNames以及字符串
  • 定義了js的不同數(shù)組類型比如jsTypedArrays、Uint16Array等
  • 定義了一些buildin比如Object、ArrayBuffer、Boolean、DataView
ProgramBuilder
  • 構造和附加程序中不同類型操作的隨機實例。
  • 記錄了屬性名和當前程序中出現(xiàn)的整數(shù)值。
  • 用于當前程序的各種分析程序scopeAnalyzer和contextAnalyzer
Events
  • Events模版類,可以在fuzzer中調度的所有事件的列表。
    • observe 為當前event注冊listener
    • dispatchAsync 異步運行所有注冊的listener
    • dispatch 同步運行所有注冊的listener
Evalueation
  • ProgramCoverageEvaluator
    • 計算當前邊覆蓋的百分比
    • 將共享內(nèi)存地址設置到環(huán)境變量中,每次執(zhí)行前clear共享內(nèi)存
Execution
  • ExecutionOutcome 可能的結果
  • Execution 類保存執(zhí)行一個程序后的結果
    • pid
    • outcome
    • termsig
    • output
    • execTime
Forkserver
  • init 初始化執(zhí)行時的環(huán)境變量,執(zhí)行的參數(shù)、outfile路徑
  • run 獲取執(zhí)行后的結果

Fuzz 流程

  • fuzzer 的 start() 函數(shù)開始執(zhí)行fuzz
    /// Starts the fuzzer and runs for the specified number of iterations.
    ///
    /// This must be called after initializing the fuzzer.
    /// Use -1 for maxIterations to run indefinitely.
    public func start(runFor maxIterations: Int) {
        precondition(isInitialized)
        assert(OperationQueue.current == queue)
        
        self.maxIterations = maxIterations
        
        self.runStartupTests()
        
        logger.info("Let's go!")
        
        // Start fuzzing
        queue.addOperation {
           self.fuzzOne()
        }
    }
  • 調用fuzzOne()
    /// Performs one round of fuzzing.
    private func fuzzOne() {
        guard maxIterations == -1 || iterations < maxIterations else {
            return
        }
        iterations += 1
        
        core.fuzzOne()
        
        // Fuzz another one.
        // We only want do actual fuzzing if there is nothing else to do. For that
        // reason we enqueue the corresponding operations with low priority.
        let op = BlockOperation(block: self.fuzzOne)
        op.queuePriority = .veryLow
        queue.addOperation(op)
    }

  • 實際調用的是 Fuzzercore 下的 fuzzOne()

    只要沒有runtime異常,就會多次變異

    /// Perform one round of fuzzing.
    ///
    /// High-level fuzzing algorithm:
    ///
    ///     let parent = pickSampleFromCorpus()
    ///     repeat N times:
    ///         let current = mutate(parent)
    ///         execute(current)
    ///         if current produced crashed:
    ///             output current
    ///         elif current resulted in a runtime exception or a time out:
    ///             // do nothing
    ///         elif current produced new, interesting behaviour:
    ///             minimize and add to corpus
    ///         else
    ///             parent = current
    ///
    ///
    /// This ensures that samples will be mutated multiple times as long
    /// as the intermediate results do not cause a runtime exception.
  • 首先會根據(jù)numConsecutiveMutations(運行時指定,默認是5),對樣本連續(xù)變異
  • 隨后通知fuzzer program生成完畢
  • fuzzer 調用 execute 執(zhí)行,根據(jù)返回結果判斷成功、crash、超時或者失敗

Mutate

  • 對于單一指令的類都繼承自 BaseInstructionMutator,這個類中有兩個mutate方法,一個是針對整個程序的,另外一個是變異單一statement
    public func mutate(_ program: Program, for fuzzer: Fuzzer) -> Program? {
        beginMutation(of: program)
        
        var candidates = [Int]()
        for instr in program {
            if canMutate(instr) {
                candidates.append(instr.index)
            }
        }
        
        guard candidates.count > 0 else {
            return nil
        }
        
        var toMutate = Set<Int>()
        for _ in 0..<Int.random(in: 1...maxSimultaneousMutations) {
            toMutate.insert(chooseUniform(from: candidates))
        }
        
        let b = fuzzer.makeBuilder()
        b.adopting(from: program) {
            for instr in program {
                if toMutate.contains(instr.index) {
                    mutate(instr, b)
                } else {
                    b.adopt(instr)
                }
            }
        }
        
        return b.finish()
    }
    

具體來說,主要有以下幾類Mutator:

  • CombineMutator

    • A mutator that inserts a program in full into another one.
  • ConcatMutator

    • A mutator that concatenates two programs together.
  • GrowMutator

    • A mutator that inserts new instructions at the end of the program.
  • InputMutator

    • A mutator that changes the input variables of instructions in a program.
  • InsertionMutator

    • A mutator that inserts new code at random positions in a program.
  • JITStressMutator

    • A mutator designed to call already JITed functions with different arguments or environment. In a way, this is a workaround for the fact that we don't have coverage feedback from JIT code.由于不能直接從JIT代碼中獲得覆蓋率的反饋信息,因此使用不同參數(shù)和環(huán)境強制調用一些JIT后的函數(shù)。
  • OperationMutator

    • A mutator that randomly mutates parameters of the Operations in the given program.
  • SpliceMutator

    • A mutator that inserts a "slice" of a program at a random position in another program. A "slice" is defined as (not necessarily contiguous) sequence of instructions that define all used variables.
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容