LoginSignup
1
2

More than 3 years have passed since last update.

PEARCH FUZZERによるファジング

Posted at

概要

  • ファジングとは?
    • 重要なセキュリティテストの一つ
    • テスト対象の外部インターフェースに対して、ホワイトノイズ、構造エラー、論理エラー、境界エラー等を狙って意図して生成されたデータ(ファジングデータ)を入力し、対象の動作(ガード、正常可動)をテストする。
  • ファジングツール PEACH FUZZER
    • community editionはMIT License
      • コピーレフト不要
      • 著作権表示、許諾表示の記載があれば無制限に利用可能
  • Linuxへのインストール
    • 実行環境として.NET frameworkを利用するため、そこそこ敷居が高い。
    • Linuxでの.NET framework環境としてMONO-completeで動作するが、最新バージョンでは正常動作しなかった。
      • MONO 5.16.0以下を推奨
  • 使用感
    • TcpListnerを使えばTCPより上位のレイヤの応答ファジングができる。(現在Listenerタイプのファジングはこれのみ)
    • 設定が複雑
    • 可変領域の定義(DataModel)に対して、値の変形(Mutator)がどのように割当られているか、なかなか掴めない。
    • とりあえず使ってみる手引きとしての纏め (余談:QiitaのMarkDown使いづらい)

Linux(Ubuntu)へのインストール

Monoのインストール

  • peachの実行環境には".NET framework"が必要
  • .NET frameworkのオープンソース環境であるmono-completeをインストール
    tetsuo@tetsuo-VirtualBox:~$ sudo apt install gnupg ca-certificates
    tetsuo@tetsuo-VirtualBox:~$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
    tetsuo@tetsuo-VirtualBox:~$ echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list
    tetsuo@tetsuo-VirtualBox:~$ sudo apt update
    tetsuo@tetsuo-VirtualBox:~$ sudo apt install mono-complete
    ```
  • 動作確認

    • サンプルコード(C++)

      tetsuo@tetsuo-VirtualBox:~/mono_basic$ view hello.cs
      ---
      using System;
      public class HelloWorld
      {
          public static void Main(string[] args)
          {
              Console.WriteLine ("Hello Mono World");
          }
      }
      
      • コンパイル
      tetsuo@tetsuo-VirtualBox:~/mono_basic$ csc hello.cs
      Microsoft (R) Visual C# Compiler version 2.8.2.62916 (2ad4aabc)
      Copyright (C) Microsoft Corporation. All rights reserved.
      
    • 実行

      tetsuo@tetsuo-VirtualBox:~/mono_basic$ chmod 755 ./hello.exe
      tetsuo@tetsuo-VirtualBox:~/mono_basic$ ./hello.exe 
      Hello Mono World
      

peachのインストール

  • linux用バイナリをダウンロードし、ubuntuの任意フォルダに展開
  • 展開先 (ex. peach)でpeachを実行

    tetsuo@tetsuo-VirtualBox:~/peach$ ./peach | more
    [[ Peach v3.1.124.0
    [[ Copyright (c) Michael Eddington
    This is the Peach Runtime.  The Peach Runtime is one of the many ways
    to use Peach XML files.  Currently this runtime is still in development
    but already exposes several abilities to the end-user such as performing
    simple fuzzer runs and performing parsing tests of Peach XML files.
    Please submit any bugs to https://forums.peachfuzzer.com.
    Syntax:
    peach -a channel
    peach -c peach_xml_file [test_name]
    peach [--skipto #] peach_xml_flie [test_name]
    peach -p 10,2 [--skipto #] peach_xml_file [test_name]
    peach --range 100,200 peach_xml_file [test_name]
    peach -t peach_xml_file
    -1                         Perform a single iteration
    -a,--agent                 Launch Peach Agent
    -c,--count                 Count test cases
    -t,--test xml_file         Validate a Peach XML file
    -p,--parallel M,N          Parallel fuzzing.  Total of M machines, this
                                is machine N.
    --debug                    Enable debug messages. Usefull when debugging
                                your Peach XML file.  Warning: Messages are very
                                cryptic sometimes.
    --trace                    Enable even more verbose debug messages.
    --seed N                   Sets the seed used by the random number generator
    

ファジングテストの実行 (Linux + Mono)

Linux + Mono環境での実行

  • 最新版mono(6.12.0)で実行 -> NG

    tetsuo@tetsuo-ubuntu18:~/peach$ ./peach samples/HelloWorld.xml
    [[ Peach v3.1.124.0
    [[ Copyright (c) Michael Eddington
    Unhandled Exception:
    System.ArgumentException: Expression of type 'System.Nullable`1[System.Boolean]' cannot be used for parameter of type 'System.Object' of method 'Void SetValue(System.Object, System.Object)'
    Parameter name: arg0
    at System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument (System.
    
  • 上記エラー回避のワークアラウンドに従って、mono ver 5.16.0をインストール

    • 参考(パッケージパスがjessieでなくbionicなので注意)

      tetsuo@tetsuo-ubuntu18:~$ echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic/snapshots/5.16.0 main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list
      deb https://download.mono-project.com/repo/ubuntu stable-bionic/snapshots/5.16.0 main
      
      • 続く、apt updateで以下の警告がでるが、参考 にあるように無視する。
      W: ディストリビューションが競合しています: https://download.mono-project.com/repo/ubuntu stable-bionic/snapshots/5.16.0 InRelease (stable-bionic/snapshots/5.16.0 を期待していたのに bionic を取得しました)
      
  • mono(5.16.0)で実行 -> OK

    tetsuo@tetsuo-ubuntu18:~/peach$ ./peach samples/HelloWorld.xml 
    [[ Peach v3.1.124.0
    [[ Copyright (c) Michael Eddington
        ... snip ...
    

HTTP要求ファジングの実行

  • 参考 IPAファジング実践資料
  • HTTP.xmlの作成

    • peach 2.xのsamples/HTTP.xmlを3.xに変換したもの。
    • xmlns, schemaLocationの変更に注意
    <?xml version="1.0" encoding="utf-8"?>
    <Peach xmlns="http://phed.org/2012/Peach" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://peachfuzzer.com/2012/Peach ../peach.xsd">
            <DataModel name="HeaderField">
                    <String name="Header" />
                    <String value=": " />
                    <String name="Value" />
                    <String value="\r\n" />
            </DataModel>
            <!-- Create a simple data template containing a single string -->
            <DataModel name="HttpRequest">
                    <!-- The HTTP request line: GET http://foo.com HTTP/1.0 -->
                    <Block name="RequestLine">
                            <!-- Defaults can be optionally specified via the
                                value attribute -->
                            <String name="Method" value="GET"/>
                            <String value=" " />
                            <String name="RequestUri" value="/" />
                            <String value=" "/>
                            <String name="HttpVersion" value="HTTP/1.1" />
                            <String value="\r\n"/>
                    </Block>
                    <!-- This block uses the Header block as a base
                                    and overrides one field -->
                    <Block name="HeaderHost" ref="HeaderField">
                            <String name="Header" value="Host" />
                            <String value=": " />
                            <String name="Value" value="192.168.56.103" />
                            <String value="\r\n" />
                    </Block>
                    <!-- This block uses the Header block as a base
                                    and overrides two fields -->
                    <Block name="ContentLength" ref="HeaderField" >
                            <String name="Header" value="Content-Length" />
                            <String name="Value" >
                                    <Relation type="size" of="Content" />
                            </String>
                    </Block>
                    <String value="\r\n" />
                    <Blob name="Content" />
            </DataModel>
            <StateModel name="HttpRequestStateModel" initialState="HttpRequestState">
                    <State name="HttpRequestState">
                            <Action type="output">
                                    <DataModel ref="HttpRequest" />
                            </Action>
                    </State>
            </StateModel>
            <!-- Create a simple test to run -->
            <Test name="Default">
                    <StateModel ref="HttpRequestStateModel" />
                    <Publisher class="TcpClient">
                            <Param name="Port" value="80" />
                            <Param name="Host" value="192.168.56.103" />
                    </Publisher>
                    <Logger class="Filesystem">
                            <Param name="Path" value="logs"/>
                    </Logger>
            </Test>
            <!-- Configure a single run -->
            <Run name="DefaultRun" description="HTTP Request Run">
                    <!-- The set of tests to run -->
                    <Test ref="HttpGetRequestTest" />
            </Run>
    </Peach>
    
  • vbox上(hostonly-network 192.168.56.106)へのHTTP要求ファジング

    • ターゲット HTTPサーバへのGET要求
    $ curl -I 'http://192.168.56.106/crl/crl.der'
    HTTP/1.0 200 OK
    Server: SimpleHTTP/0.6 Python/3.8.6
    Date: Thu, 22 Oct 2020 08:06:46 GMT
    Content-type: application/octet-stream
    Content-Length: 489
    Last-Modified: Wed, 30 Sep 2020 09:11:17 GMT
    
  • 実行 (テスト範囲を絞って実行)

        tetsuo@tetsuo-ubuntu18:~/peach$ ./peach -seed 12345 -range 1,10 HTTP.xml 
        [[ Peach v3.1.124.0
        [[ Copyright (c) Michael Eddington
        [*] Test 'Default' starting with random seed 12345.
        [R1,-,-] Performing iteration
        [1,10,0:00:00.75] Performing iteration
        [*] Fuzzing: HttpRequest.HeaderHost.DataElement_1
        [*] Mutator: DataElementDuplicateMutator
        [*] Fuzzing: HttpRequest.Content
        [*] Mutator: BlobDWORDSliderMutator                                               
        [2,10,0:00:00.72] Performing iteration
        [*] Fuzzing: HttpRequest.HeaderHost.DataElement_6
        [*] Mutator: UnicodeUtf8ThreeCharMutator
        [*] Fuzzing: HttpRequest.HeaderHost
        [*] Mutator: DataElementRemoveMutator
        [3,10,0:00:00.332] Performing iteration
        [*] Fuzzing: HttpRequest.RequestLine.HttpVersion
        [*] Mutator: StringMutator
        [*] Fuzzing: HttpRequest.RequestLine.Method
        [*] Mutator: StringMutator
        [*] Fuzzing: HttpRequest.ContentLength.Value
        [*] Mutator: StringCaseMutator
        [*] Fuzzing: HttpRequest.ContentLength
        [*] Mutator: DataElementDuplicateMutator
        [4,10,0:00:00.205] Performing iteration
        [*] Fuzzing: HttpRequest.HeaderHost.DataElement_5
        [*] Mutator: DataElementRemoveMutator
        [*] Fuzzing: HttpRequest.RequestLine.DataElement_2
        [*] Mutator: UnicodeBomMutator
        [*] Fuzzing: HttpRequest.HeaderHost.Header
        [*] Mutator: DataElementRemoveMutator
        [*] Fuzzing: HttpRequest.RequestLine
        [*] Mutator: DataElementSwapNearNodesMutator
        [*] Fuzzing: HttpRequest.RequestLine.RequestUri
        [*] Mutator: UnicodeUtf8ThreeCharMutator
        [5,10,0:00:00.133] Performing iteration
        [*] Fuzzing: HttpRequest.HeaderHost.Value
        [*] Mutator: UnicodeBomMutator
        [6,10,0:00:00.09] Performing iteration
        [*] Fuzzing: HttpRequest.HeaderHost
        [*] Mutator: DataElementRemoveMutator
        [*] Fuzzing: HttpRequest.RequestLine.Method
        [*] Mutator: UnicodeBadUtf8Mutator
        [*] Fuzzing: HttpRequest.RequestLine.DataElement_4
        [*] Mutator: DataElementDuplicateMutator
        [*] Fuzzing: HttpRequest.RequestLine
        [*] Mutator: DataElementRemoveMutator
        [7,10,0:00:00.06] Performing iteration
        [*] Fuzzing: HttpRequest.HeaderHost.DataElement_6
        [*] Mutator: UnicodeBomMutator
        [*] Fuzzing: HttpRequest.RequestLine.DataElement_4
        [*] Mutator: StringCaseMutator
        [*] Fuzzing: HttpRequest.HeaderHost.Header
        [*] Mutator: StringCaseMutator
        [*] Fuzzing: HttpRequest.RequestLine
        [*] Mutator: DataElementSwapNearNodesMutator
        [8,10,0:00:00.039] Performing iteration
        [*] Fuzzing: HttpRequest.HeaderHost.DataElement_6
        [*] Mutator: StringCaseMutator
        [9,10,0:00:00.023] Performing iteration
        [*] Fuzzing: HttpRequest.RequestLine.DataElement_3
        [*] Mutator: UnicodeUtf8ThreeCharMutator
        [*] Fuzzing: HttpRequest.RequestLine.DataElement_2
        [*] Mutator: UnicodeBomMutator
        [*] Fuzzing: HttpRequest.DataElement_7
        [*] Mutator: DataElementRemoveMutator
        [10,10,0:00:00.01] Performing iteration
        [*] Fuzzing: HttpRequest.RequestLine.DataElement_3
        [*] Mutator: DataElementSwapNearNodesMutator
        [*] Fuzzing: HttpRequest.HeaderHost.Value
        [*] Mutator: UnicodeBomMutator
        [*] Test 'Default' finished.
        ```
    
  • 結果(正常)

    • logs/HTTP.xml_yyyyMMddHHmmにログが出力される。
    Peach Fuzzing Run
    =================
    Date of run: 2020/10/22 17:09:26
    Peach Version: 3.1.124.0
    Seed: 12345
    Command line: -seed 12345 -range 0,100 HTTP.xml
    Pit File: HTTP.xml
    . Test starting: Default
    . Iteration 1 : 2020/10/22 17:09:27
    . Iteration 1 of 100 : 2020/10/22 17:09:27
    . Iteration 100 of 100 : 2020/10/22 17:09:28
    . Test finished: Default
    
  • 実行概要(case1, case2の抽出)

    • HTTP.xmlのDataModelで定義されたHeaderFieldのHeader/Value、RequestLineのMethod,RequestUri,HttpVersionがファジング対象
    • 変換ルール: Mutator
    • case1:

      • Host/Valueの設定構成要素を 2 回から 50 回複数回繰り返す。

        [*] Fuzzing: HttpRequest.HeaderHost.DataElement_1
        [*] Mutator: DataElementDuplicateMutator
        
      • Content-Length/Value 4バイトごとにそれぞれの中身をずらす。

        [*] Fuzzing: HttpRequest.Content
        [*] Mutator: BlobDWORDSliderMutator
        
    • case2:

      • Host/Valueについて、文字エンコーディング「UTF-8」により 3 バイトで構成される文字コードで構成される長い文字列を生成する。

        [*] Fuzzing: HttpRequest.HeaderHost.DataElement_6
        [*] Mutator: UnicodeUtf8ThreeCharMutator
        
      • Content-Length/Value ノードを削除

        [*] Fuzzing: HttpRequest.HeaderHost
        [*] Mutator: DataElementRemoveMutator
        

HTTP応答ファジングの実行

  • TcpListenerで実現可能
  • TCP_LISTENER.xmlを準備(ほぼ上のサンプルそのまま)

    <?xml version="1.0" encoding="utf-8"?>
    <Peach xmlns="http://phed.org/2012/Peach" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://peachfuzzer.com/2012/Peach ../peach.xsd">
            <DataModel name="TheDataModel">
            <String name="value" length="4" />
            </DataModel>
            <StateModel name="TheState" initialState="initial">
                    <State name="initial">
                            <Action type="accept" />
                            <Action type="output">
                                    <DataModel ref="TheDataModel"/>
                                    <Data>
                                            <Field name="value" value="mike" />
                                    </Data>
                            </Action>
                            <!-- receive 4 bytes -->
                            <Action type="input">
                                    <DataModel ref="TheDataModel"/>
                            </Action>
                    </State>
            </StateModel>
            <Test name="Default">
            <!-- ... -->
            <StateModel ref="TheState" />
                    <Publisher class="TcpListener">
                            <Param name="Interface" value="192.168.56.106" />
                            <Param name="Port" value="80" />
                            <Param name="Timeout" value="30000" />
                            <Param name="AcceptTimeout" value="30000" />
                    </Publisher>
            <Logger class="Filesystem">
                <Param name="Path" value="logs"/>
            </Logger>
            </Test>
    </Peach>        
    
  • peachでTcpListenerを起動し、curlからのHTTP要求に対するHTTP応答ファジングを実行する。
  1. peachでTcpListenerを起動

    tetsuo@tetsuo-VirtualBox:~/peach$ sudo ./peach -seed 12345 TCP_LISTENER.xml 
    [[ Peach v3.1.124.0
    [[ Copyright (c) Michael Eddington
    [*] Test 'Default' starting with random seed 12345.
    [R1,-,-] Performing iteration
    
  1. curlでTcpListenerにHTTP要求
      

    $ curl -I 'http://192.168.56.106/crl/crl.der'
    curl: (52) Empty reply from server
    
    [R1,-,-] Performing iteration
    [1,-,-] Performing iteration
    
  2. 再度curlでTcpListenerにHTTP要求

    $ curl -I 'http://192.168.56.106/crl/crl.der'
    curl: (8) Weird server reply
    
    [1,-,-] Performing iteration
    [*] Fuzzing: TheDataModel.value
    [*] Mutator: StringMutator
    
  3. 再度curlでTcpListenerにHTTP要求

    $ curl -I 'http://192.168.56.106/crl/crl.der'
    curl: (8) Weird server reply
    
    [2,-,-] Performing iteration
    [*] Fuzzing: TheDataModel.value
    [*] Mutator: UnicodeBadUtf8Mutator
    
  4. 再度curlでTcpListenerにHTTP要求

    $ curl -I 'http://192.168.56.106/crl/crl.der'
    curl: (8) Weird server reply
    
    [3,-,-] Performing iteration
    [*] Fuzzing: TheDataModel.value
    [*] Mutator: DataElementRemoveMutator
    
  5. 以下、繰り返す。

  • パケットキャプチャ

    • 3-wayハンドシェイク、クローズシーケンス含め、TCPデータはファジング対象外のようである。
    • HTTP応答ヘッダ、HTTP応答データがファジング対象となっている。
  • tcp_listener.png

    • No.1-10 最初の応答
      • No.6で4byteのデータを応答
        • 6d 69 6b 65 (mike)
      • curlは「Empty reply from server」
    • No.11-20 2回目の応答

      • No.14で20byteの応答

        0030                     31 38 34 34 36 37 34 34 30 37
        0040   33 37 30 39 35 35 31 35 36 36
        
        • curlは「Weird server reply」
        • ファジングルールはTheDataModel.value/StringMutator
    • No.21-29 3回目の応答

      • No.26でHTTP Continuationの応答
      • curlは「Weird server reply」
      • ファジングルールはTheDataModel.value/UnicodeBadUtf8Mutator

        • HTTPデータとしてbad UTF-8 文字列のリストを応答。
        Data: f8808081affc80808080b8fc80808081bafc80808080bdfc…
        
    • No.30-37 4回目の応答

      • 無応答
      • curlは「Weird server reply」
      • ファジングルールはTheDataModel.value/DataElementRemoveMutator
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2