コンテンツにスキップ

上級

ターゲットへの入力方法

input

このレッスンでは、ファイル入力stdinTCP/UDP など、さまざまなタイプの入力を使用するバイナリを Mayhem でテストする方法を説明します。bacsrvlighttpd などの一般的なネットワーク アプリケーションをパッケージ化してテストする方法を順を追って学びましょう。


学習時間の目安: 20 分

このレッスンを完了すると、以下のことができるようになります。

  1. 異なるタイプの入力を必要とするバイナリの Mayhemfile を比較する。
  2. bacsrv ネットワーク アプリケーションを分析し、リスニング ポートを特定する。
  3. Mayhemfile でネットワーク オプションを構成する。
  4. bacsrv に対して Mayhem ランを実行する。
  5. lighttpd の依存関係を手動でパッケージ化する。
  6. lighttpd に対して Mayhem ランを実行する。

レッスンを駆け足で

始める前に前提条件を確認します。

  1. Mayhem は以下の入力を使用してターゲット バイナリをファジングできます。

    1. ファイル入力
    2. 標準入力
    3. TCP/UDP 入力
  2. Mayhem CLI を使用して bacsrv ターゲットをパッケージ化します。

    mayhem package ./bacsrv -o /tmp/bacsrv-pkg
    
  3. mayhem run コマンドに次の Mayhemfile を指定して、bacsrv ターゲットに対して Mayhem ランを実行します。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    project: bacsrv
    target: bacsrv
    duration: 90
    advanced_triage: true
    
    cmds:
      - cmd: /root/tutorial/bacsrv/bacsrv
    
        # For a network program, you add a "network" specification block
        network:
        url: udp://localhost:47808  # protocol, host and port to analyze
        client: false            # target is not a client program
        timeout: 2.0                # max seconds before timing out on network
    
  4. mayhem package コマンドを使用して lighttpd ターゲットをパッケージ化します。

    # Run the package command
    mayhem package ./1.4.15/sbin/lighttpd -o /tmp/lighttpd-pkg
    
    # Copy over all dependencies manually
    cp -R ./1.4.15 /tmp/lighttpd-pkg/root/root/tutorial/lighttpd/
    cp -R /www /tmp/lighttpd-pkg/root/
    
  5. mayhem run コマンドに次の Mayhemfile を指定して、lighttpd ターゲットに対して Mayhem ランを実行します。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    project: lighttpd
    target: lighttpd-1-4
    duration: 90
    
    cmds:
      - cmd: /root/tutorial/lighttpd/1.4.15/sbin/lighttpd -D -f /root/tutorial/lighttpd/1.4.15/etc/lighttpd.conf
            network:
            client: false
            timeout: 5.0
            url: tcp://localhost:8080
    

以下が必要です。

  • The Mayhem CLI がインストールされ、Mayhem サーバーで認証済みであること。
  • あらかじめビルドされた Docker イメージを実行し、チュートリアル サンプルにアクセスできること、

    docker pull forallsecure/tutorial:2.10
    docker run -ti --privileged --rm forallsecure/tutorial:2.10
    

入力タイプに対応した Mayhemfile

Mayhem は次の入力を使用してターゲット バイナリをファジングできます。

  1. ファイル入力
  2. 標準入力
  3. TCP/UDP 入力

いくつかのサンプルを提示して、Mayhemfile 構成の違いを説明します。自由にサンプルの Mayhemfile を使って Mayhem ランを実行してみてください。

Info

テスト対象はもっと複雑なカスタム ターゲットでしょうか? Mayhemfile の構成に関する詳細は、「Mayhemfile の構成」ページを参照してください。

ファイル入力

コマンドライン上で単一の入力を受け取ります (objdump から)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Project name that the target belongs to
project: objdump

# Target name (should be unique within the project)
target: objdump

# Base image to run the binary in.
image: forallsecure/debian-buster:latest

# Time that analysis will run for in seconds - if absent, run forever
duration: 90

# List of commands used to test the target
cmds:

  # Command used to start the target, "@@" is the input file
  - cmd: /usr/bin/objdump -x @@

標準入力 (stdin)

/dev/stdin から入力を受け取るあいだ、ずっと djpeg に対してテストを実行します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# Mayhemfile: configuration file for testing your target with Mayhem
# Format: YAML 1.1

# Project name that the target belongs to
project: libjpeg

# Target name (should be unique within the project)
target: djpeg

# Base image to run the binary in.
image: forallsecure/debian-buster:latest

# Commented out duration to run forever
# duration: 90

# List of commands used to test the target
cmds:

  # Command used to start the target. "@@" is missing so
  # input will be read from /dev/stdin.
  - cmd: /usr/bin/djpeg

TCP/UDP 入力

パブリックな Docker Hub イメージを使用し、TCP ポートの 80 でネットワークから単一の入力を受け取ります (sebp/lighttpd から)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
project: lighttpd-docker
target: lighttpd
image: sebp/lighttpd
duration: 90
cmds:
  - cmd: /usr/sbin/lighttpd -D -f /etc/lighttpd/lighttpd.conf
    network:
      client: false
      url: tcp://127.0.0.1:80
      timeout: 1.0

bacsrv の解析

bacsrv (bacserv とも呼ばれます) は BACnet ツール スイートの一部です。 BACnet のWeb サイトには次のように記載されています。

American Society of Heating, Refrigerating and Air-Conditioning Engineers (ASHRAE) の後援の下で開発された BACnet は、アメリカの国家規格、欧州規格、30 以上の国々での国家規格、そして ISO のグローバル規格です。このプロトコルは、ASHRAE Standing Standard Project Committee 135 のメンバーによってサポートおよびメンテナンスが行われています。

今回は bacsrv を解析しますが、このアプリケーションは UDP ポートの 47808 で待機します。Mayhem の利点の 1 つは、TCP および UDP サーバーについても、特別なテスト ドライバーを作成する必要がないことです。バイナリを変更せず、そのまま解析します。

アプリケーションを解析する際の最初のステップは、アプリケーションを実行できるかを確認することです。examples/bacsrv ディレクトリに移動し、bacsrv を実行します。

$ cd ~/tutorial/bacsrv
$ ./bacsrv &
BACnet Server Demo
BACnet Stack Version 0.9.1
BACnet Device ID: 260001
Max APDU: 1476

ネットワーク アプリケーションが使用するポートがわからない場合はどうすればよいのでしょうか? netstatlsof、または strace を使用するなど、調べる方法は複数あります。strace を使って、bacsrv がポート 47808 で動作していることを確認しましょう。

たとえば、bind への呼び出しを探すことで、bacsrv がポート 47808 で動作していることがわかります。

# strace -Tfe trace=bind ./bacsrv
BACnet Server Demo
BACnet Stack Version 0.9.1
BACnet Device ID: 260001
Max APDU: 1476
bind(3, {sa_family=AF_INET, sin_port=htons(47808), sin_addr=inet_addr("0.0.0.0")}, 16) = 0 <0.000018>

Note

Docker でレッスンを進める場合、必ず --privileged フラグを指定して呼び出してください。そうでなければ strace は動作しません。

Mayhemfile bacsrv

これまでのレッスンでは、Mayhemfilecmd パラメーターのターゲット パスに @@ を付加することで、testme アプリケーションはファイルを読み取ることを Mayhem に通知しました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# A user-meaningful name to group like targets
project: testme

# A user-meaningful name of the target
target: testme

# Time to spend fuzzing.
duration: 90

# Perform advanced triage and diagnosis of each test case.
# This will find many more defects, but add additional processing time.
advanced_triage: true

# List of executable programs to analyze.  For most use, you only
# need one.
cmds:

    # The full path to the executable program to test.
    #   - "@@" is the  to test.
    #     For programs that take input from a file, use '@@' to mark
    #     the location in the target's command line where the input
    #     file name should be placed. Mayhem will substitute this
    #     for you.
    - cmd: /root/tutorial/testme/v1/testme @@

一方、ネットワーク アプリケーションを解析する場合は、アプリケーションがどのネットワーク ポートを使用するか、また TCP と UDP のどちらを使用するかを Mayhem に通知する必要があります。この情報を指定する方法を示した bacsrvMayhemfile をサンプルとして挙げます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
project: bacsrv
target: bacsrv
duration: 90
advanced_triage: true

cmds:
  - cmd: /root/tutorial/bacsrv/bacsrv

    # For a network program, you add a "network" specification block
    network:
      url: udp://localhost:47808  # protocol, host and port to analyze
      client: false               # target is not a client program
      timeout: 2.0                # max seconds before timing out on network
  • urlTCP|UDP://<HOST>:PORT という形式で指定されます。Mayhem は同一ホスト上で実行されているサービスに接続するため、ほとんどの場合、HOSTlocalhost になるでしょう。
  • client は、アプリケーションが curlwget のようにネットワーク越しに通信するか、それともサーバーであるかを指定します。今回はサーバーを解析するため、値として false を設定します。
  • timeout は、リクエストを送信した後、サービスが応答していないと判断するまでにどれほど待機する必要があるかを指定します。1.0 または 2.0 が適切な値ですが、解析中にネットワーク サービスがタイムアウトする問題が発生する場合は、値を増やすことを検討するべきです。

Mayhem bacsrv

Mayhemfile を作成したら、他のプログラムと同様に、パッケージ化して Mayhem ランを実行することで bacsrv を解析します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#!/bin/sh

# Package bacsrv
mayhem package ./bacsrv -o /tmp/bacsrv-pkg

# Write your Mayhemfile (here copying over provided one)
cp Mayhemfile /tmp/bacsrv-pkg/Mayhemfile

# Run Mayhem!
id=$(mayhem run /tmp/bacsrv-pkg)

# Wait for the run to finish
mayhem wait $id

# Sync the test suite to the "testsuite" directory.
mayhem sync /tmp/bacsrv-pkg

ハードウェアにもよりますが、10 分もかからないうちに、無効な読み取り (CWE-125) によるクラッシュが再現され、その他、未初期化変数が使用されているというエラーがいくつか再現されるはずです。

lighttpd

lighttpd は高速で標準に準拠した柔軟な Web サーバーであり、他の Web サーバーに比べて何倍もスケール性に優れています。

このサンプルでは、lighttpd のパッケージ化と実行の方法を示し、パッケージ化で遭遇しやすいエラーについて説明します。Mayhem パッケージとは、実行可能プログラムと共有オブジェクトや構成ファイルを含む実行環境全体であることを思い出してください。

lighttpd を実行できることを確認する

tutorial/lighttpd にあるサンプルには、lighttpd の悪用可能な脆弱性を持つバージョンである lighttpd-1.4.15 が事前インストールされています。

./1.4.15/sbin/lighttpd -D -f ./1.4.15/etc/lighttpd.conf
2019-09-02 23:22:20: (log.c.75) server started
2019-09-02 23:22:21: (server.c.1252) [note] graceful shutdown started
2019-09-02 23:22:21: (log.c.135) server stopped

Info

Ctrl+C キーを押して lighttpd に SIGINT (signal interrupt) を送信すると、グレースフルにシャットダウンできます。

初回のトライ: lighttpd をパッケージ化する

mayhem package コマンドは、実行ファイルを検査して、静的に解決可能なすべての依存関係を特定します。しかし、多くの Web サーバーと同様に、lighttpd は実行時にライブラリを解決します。共有ライブラリ自体が問題なのではないことに注意してください。dlopen() などによる実行時の動的解決がいつ行われるかが問題なのです。

残念ながら、lighttpdmayhem package が静的に発見できない依存関係を使用しています。理由を知るため、以下を実行します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/bin/sh

# Run the package command
mayhem package ./1.4.15/sbin/lighttpd -o /tmp/lighttpd-pkg

# Inspect the contents of the package
ls -R /tmp/lighttpd-pkg/root/

# Run mayhem on the package
id=$(mayhem run /tmp/lighttpd-pkg/)

# Wait for Mayhem to finish. It will return 'failed', indicating
# a failed run.
mayhem wait $id

mayhem run は Mayhem ランを開始しますが、mayhem wait はランが失敗したことを示します。UI に移動して [Event Log] をクリックすると、lighttpd のランが失敗した理由が次のように表示されます。

  1. Mayhem ランのビヘイビア テストが失敗しました。
  2. Mayhem はプログラムが入力を読み取っていないことを検出しました。
  3. lighttpd がエラーを出力しました。

failed-lighttpd-run.png

lighttpd の動作を少し調べてみると、理由がわかります。

  1. アップロードしたパッケージは、すべての lighttpd の依存関係を含んではいませんでした。特に、このランでは、lighttpd の起動時に構成ファイルが見つからず、いくつかの dlopen() によって動的にロードされる動的ライブラリがありませんでした。結果として、mayhem packagelighttpd ターゲットを適切にパッケージ化できませんでした。
  2. デフォルトの Mayhemfile には、lighttpd ターゲットと通信するためのネットワーク プロトコルが指定されていませんでした。

mayhem package コマンドを使用して生成された Mayhemfile を見ると、このパッケージ化方法の限界が明らかになります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# Namespaced project name that the target belongs to
project: lighttpd

# Target name (should be unique within the project)
target: lighttpd

# Base image to run the binary in.
# image: $MAYHEM_DOCKER_REGISTRY/forallsecure/debian-buster:latest

# Time that analysis will run for in seconds - if absent, run forever
duration: 90

# Turns on extra test case processing (completing a run will take longer)
advanced_triage: false

# List of commands used to test the target
cmds:

  # Command used to start the target, "@@" is the input file
  # (when "@@" is omitted Mayhem defaults to stdin inputs)
  - cmd: /root/tutorial/lighttpd/1.4.15/sbin/lighttpd @@
    env: {}

    ## Uncomment to change default dir (/) from which the target is invoked
    #cwd: /

    ## If this is a network target, uncomment the block below and remove
    ## the @@ input file reference in the cmd (you can either test network or
    ## file inputs, not both).
    #network:
    ## Use "127.0.0.1" instead of "localhost" below if you want to test only
    ## for IPv4. For IPv6, use "[::1]". By leaving as "localhost", Mayhem will
    ## attempt to autodetect the one used by the target.
    #  url: tcp://localhost:8080  # protocol, host and port to analyze
    #  client: false           # target is a client-side program
    #  timeout: 2.0               # max seconds for sending data

生成された Mayhemfilelighttpd に必要なネットワーク オプションを指定していなかったことがわかります

二回目のトライ: lighttpd を正常に解析する

正常に lighttpd を解析するには、以下を行う必要があります。

  1. 正しく TCP の 8080 ポートで lighttpd をテストするよう指示する Mayhemfile を作成します。
  2. すべての依存関係を Mayhem パッケージにコピーします。

一般的に、すべての依存関係を洗い出すのは少々やっかいです。このレッスンでは、すべての依存関係を 1 つのディレクトリの下に配置することで、問題を単純化してあります。現実では、依存関係はファイル システムのあちこちに散らばっている場合もあります。上で触れた strace などのツールは、そういった依存関係を見つけるのに役立ちます。

次の Mayhemfile は、Mayhem に lighttpd の解析方法を指示します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
project: lighttpd
target: lighttpd-1-4
duration: 90

cmds:
- cmd: /root/tutorial/lighttpd/1.4.15/sbin/lighttpd -D -f /root/tutorial/lighttpd/1.4.15/etc/lighttpd.conf
  network:
    client: false
    timeout: 5.0
    url: tcp://localhost:8080

次のスクリプトを実行すると、lighttpd と依存関係が正しくパッケージ化されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/sh

# Run the package command
mayhem package ./1.4.15/sbin/lighttpd -o /tmp/lighttpd-pkg

# Copy over all dependencies manually
cp -R ./1.4.15 /tmp/lighttpd-pkg/root/root/tutorial/lighttpd/
cp -R /www /tmp/lighttpd-pkg/root/

# Copy over the Mayhemfile needed to run lighttpd
cp Mayhemfile /tmp/lighttpd-pkg/

# Copy over an example test suite. This speeds up the example by
# seeding analysis with an example input.
cp testsuite/* /tmp/lighttpd-pkg/testsuite/

# Run mayhem on the package
id=$(mayhem run /tmp/lighttpd-pkg/)

# Wait for Mayhem to finish. It will succeed this time because we included
# lighttpd's dependencies.
mayhem wait $id

Warning

Mayhem は特権なしのユーザーとして実行されることを思い出してください。ネットワーク サービスが 1024 未満のポートにバインドされる場合、Mayhemfile の最上位レベルで uid: 0 を指定して、root としてプログラムを実行する必要があります。

✏️ まとめと振り返り

このレッスンでは、bacsrvlighttpd などのネットワーク ターゲットをファジングする方法を学びました。


学習内容

1.さまざまな入力を必要とするバイナリの Mayhemfile 構成を比較します。
  • コマンドライン上で単一の入力を受け取ります (objdump から)。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    # Project name that the target belongs to
    project: objdump
    
    # Target name (should be unique within the project)
    target: objdump
    
    # Base image to run the binary in.
    image: forallsecure/debian-buster:latest
    
    # Time that analysis will run for in seconds - if absent, run forever
    duration: 90
    
    # List of commands used to test the target
    cmds:
    
      # Command used to start the target, "@@" is the input file
      - cmd: /usr/bin/objdump -x @@
    

  • /dev/stdinから入力を受け取るあいだ、ずっと djpeg に対してテストを実行します。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # Mayhemfile: configuration file for testing your target with Mayhem
    # Format: YAML 1.1
    
    # Project name that the target belongs to
    project: libjpeg
    
    # Target name (should be unique within the project)
    target: djpeg
    
    # Base image to run the binary in.
    image: forallsecure/debian-buster:latest
    
    # Commented out duration to run forever
    # duration: 90
    
    # List of commands used to test the target
    cmds:
    
    # Command used to start the target. "@@" is missing so
    # input will be read from /dev/stdin.
    - cmd: /usr/bin/djpeg
    

  • パブリックな Docker Hub イメージを使用し、TCP ポートの 80 でネットワークから単一の入力を受け取ります (sebp/lighttpd から)。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    project: lighttpd-docker
    target: lighttpd
    image: sebp/lighttpd
    duration: 90
    cmds:
      - cmd: /usr/sbin/lighttpd -D -f /etc/lighttpd/lighttpd.conf
        network:
          client: false
          url: tcp://127.0.0.1:80
          timeout: 1.0
    

2.bacsrv ネットワーク アプリケーションを解析し、リスニング ポートを特定します。
  • strace ユーティリティを使用して bacsrv がポート 47808 で待機することを確認できます。
$ strace -Tfe trace=bind ./bacsrv
BACnet Server Demo
BACnet Stack Version 0.9.1
BACnet Device ID: 260001
Max APDU: 1476
bind(3, {sa_family=AF_INET, sin_port=htons(47808), sin_addr=inet_addr("0.0.0.0")}, 16) = 0 <0.000018>
3.Mayhemfile でネットワーク オプションを構成します。
  • ネットワーク アプリケーションを解析する場合は、アプリケーションがどのネットワーク ポートを使用するか、また TCP と UDP のどちらを使用するかを Mayhem に通知する必要があります。この情報を指定する方法を示した bacsrv の Mayhemfile をサンプルとして挙げます。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
project: bacsrv
target: bacsrv
duration: 90
advanced_triage: true

cmds:
  - cmd: /root/tutorial/bacsrv/bacsrv

    # For a network program, you add a "network" specification block
    network:
      url: udp://localhost:47808  # protocol, host and port to analyze
      client: false            # target is not a client program
      timeout: 2.0                # max seconds before timing out on network
4.bacsrv に対して Mayhem ランを実行します。
  • 次のスクリプトは、bacsrv ターゲットに対して Mayhem ランを実行します。

    #!/bin/sh
    
    # Package bacsrv
    mayhem package ./bacsrv -o /tmp/bacsrv-pkg
    
    # Write your Mayhemfile (here copying over provided one)
    cp Mayhemfile /tmp/bacsrv-pkg/Mayhemfile
    
    # Run Mayhem!
    id=$(mayhem run /tmp/bacsrv-pkg)
    
    # Wait for the run to finish
    mayhem wait $id
    
    # Sync the test suite to the "testsuite" directory.
    mayhem sync /tmp/bacsrv-pkg
    
5.lighttpd の依存関係を手動でパッケージ化します。
  • 次のスクリプトは、lighttpd の依存関係を正しくパッケージ化します。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #!/bin/sh
    
    # Run the package command
    mayhem package ./1.4.15/sbin/lighttpd -o /tmp/lighttpd-pkg
    
    # Copy over all dependencies manually
    cp -R ./1.4.15 /tmp/lighttpd-pkg/root/root/tutorial/lighttpd/
    cp -R /www /tmp/lighttpd-pkg/root/
    
    # Copy over the Mayhemfile needed to run lighttpd
    cp Mayhemfile /tmp/lighttpd-pkg/
    
    # Copy over an example test suite. This speeds up the example by
    # seeding analysis with an example input.
    cp testsuite/* /tmp/lighttpd-pkg/testsuite/
    
    # Run mayhem on the package
    id=$(mayhem run /tmp/lighttpd-pkg/)
    
    # Wait for Mayhem to finish. It will succeed this time because we included
    # lighttpd's dependencies.
    mayhem wait $id
    
6.lighttpd に対して Mayhem ランを実行します。
  • 次のスクリプトは、lighttpd に対して Mayhem ランを実行します。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    project: lighttpd
    target: lighttpd-1-4
    duration: 90
    
    cmds:
    - cmd: /root/tutorial/lighttpd/1.4.15/sbin/lighttpd -D -f /root/tutorial/lighttpd/1.4.15/etc/lighttpd.conf
        network:
        client: false
        timeout: 5.0
        url: tcp://localhost:8080