top of page

実践:VulHunt CE で始めるバイナリ脆弱性ハンティング

3 日前
読了時間: 8分

ソースコードが手元にないソフトウェアを評価する場面は、実務では珍しくありません。組み込み機器、ファームウェア、サードパーティ製バイナリ、古いライブラリ、UEFI モジュール。こうした対象では、ソースコードレビューだけでは足りません。

そこで必要になるのが、バイナリからリスクを探す視点です。


VulHunt Community Edition は、Binarly が公開したバイナリ脆弱性検出フレームワークです。単純に危険な文字列を探すだけではなく、バイナリ解析、デコンパイル結果、データフロー、ルールベースの検出を組み合わせて、脆弱性候補を探すためのツールです。


この記事では、VulHunt CE を Ubuntu 上に構築し、小さな C プログラムを使って、バイナリ検査の流れを試します。


今回やること


この記事では、次の流れで進めます。

1. VulHunt CE をビルドする
2. サンプルの脆弱な C プログラムを作る
3. バイナリをコンパイルする
4. VulHunt CE でスキャンする
5. 結果を読む
6. 安全版のコードと比較する
7. ファームウェア解析への応用を考える

VulHunt CE は新しいツールなので、まずは小さな検証用バイナリで動きを見るのがよいです。いきなり複雑なファームウェアを投げるより、何を検出できて、何を検出できないかを把握しやすくなります。


検証環境

OS      : Ubuntu 24.04 LTS
CPU     : x86_64
Tool    : VulHunt Community Edition
Build   : Rust / cargo-make
Target  : 自作の小さな C バイナリ

1. 必要パッケージを入れる

まず、ビルドに必要なパッケージを入れます。

sudo apt update
sudo apt install -y \
  git curl build-essential pkg-config \
  clang cmake make python3 python3-pip

Rust も必要です。

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"

rustc --version
cargo --version

2. VulHunt CE を取得する

作業ディレクトリを作り、VulHunt CE を取得します。

mkdir -p ~/vulhunt-lab
cd ~/vulhunt-lab

git clone https://github.com/vulhunt-re/vulhunt.git
cd vulhunt

サブモジュールも取得します。

git submodule update --init --recursive

cargo-make を入れます。

cargo install cargo-make

ビルドします。

cargo make --profile release build

インストールします。

cargo make --profile release install

動作確認します。

vulhunt-ce --help

ここでヘルプが表示されれば、VulHunt CE 本体は使える状態です。


3. ビルドで詰まる場合

環境によっては、LuaJIT 周りで詰まることがあります。その場合は、LuaJIT を手動でビルドしてから VulHunt CE をビルドします。

cd ~/vulhunt-lab

git clone https://github.com/LuaJIT/LuaJIT.git -b v2.1
cd LuaJIT

VulHunt 側のパッチを当てます。

git apply ~/vulhunt-lab/vulhunt/patches/luajit-vulhunt.patch
make BUILDMODE='static'

環境変数を設定します。

export LUA_LIB=~/vulhunt-lab/LuaJIT/src/
export LUA_LIB_NAME=luajit
export LUA_LINK=static

VulHunt CE をビルドします。

cd ~/vulhunt-lab/vulhunt
cargo build --release

必要であれば手動で配置します。

sudo install -m 0755 target/release/vulhunt-ce /usr/local/bin/vulhunt-ce

4. VulHunt CE の基本形

VulHunt CE のスキャンは、基本的に次の形です。

vulhunt-ce scan <入力バイナリ> \
  -o <結果JSON> \
  -d <BIASデータ> \
  -r <ルールディレクトリ> \
  --loader component \
  --pretty

重要なのは、この3つです。

入力バイナリ       : 調査したい ELF / UEFI module など
BIAS データ       : 解析に必要な補助データ
ルールディレクトリ : 何を脆弱性候補として検出するかを定義するもの

VulHunt CE は、ツール本体だけ入れて終わりではありません。どのデータを使い、どのルールを使うかで結果が変わります。


5. ハンズオン用の脆弱な C プログラムを作る

まず、分かりやすい検証用プログラムを作ります。

mkdir -p ~/vulhunt-lab/sample
cd ~/vulhunt-lab/sample

vulnerable.c を作成します。

cat > vulnerable.c <<'EOF'
#include <stdio.h>
#include <string.h>

void copy_name(const char *input) {
    char buf[32];

    /*
     * 意図的に危険な実装。
     * 入力長を確認せず strcpy している。
     */
    strcpy(buf, input);

    printf("name=%s\n", buf);
}

int main(int argc, char **argv) {
    if (argc < 2) {
        puts("usage: ./vulnerable <name>");
        return 1;
    }

    copy_name(argv[1]);
    return 0;
}
EOF

コンパイルします。

gcc -g -O0 -fno-stack-protector -no-pie vulnerable.c -o vulnerable
file vulnerable

このプログラムは、検証のためにあえて危険な書き方にしています。本番コードでは、入力長を確認せずに strcpy する実装は避けるべきです。


6. ルールディレクトリを確認する

VulHunt CE は、ルールに基づいてバイナリを検査します。まずは、リポジトリ内に含まれるサンプルや既存ルールを探します。

cd ~/vulhunt-lab/vulhunt

find . -maxdepth 4 -type f \( -name "*.lua" -o -name "*.vh" \) | head -n 50

ルールらしいディレクトリも確認します。

find . -maxdepth 4 -type d | grep -Ei 'rule|rules|vulhunt'

見つかったルールを作業用ディレクトリにコピーします。

mkdir -p ~/vulhunt-lab/rules

# 実際に見つかったルールをコピーする
# cp path/to/sample_rule.lua ~/vulhunt-lab/rules/

ここは、VulHunt CE のバージョンによって構成が変わる可能性があります。そのため、記事化する場合は、自分が試した時点のディレクトリ構成をそのまま載せるのがよいです。


7. BIAS データを確認する

次に、解析用の補助データを指定します。

リポジトリ内で BIAS data に相当するディレクトリを探します。

cd ~/vulhunt-lab/vulhunt

find . -maxdepth 4 -type d | grep -Ei 'data|bias'

見つかったパスを環境変数に設定します。

export BIAS_DATA=~/vulhunt-lab/vulhunt/path/to/bias-data
export BIAS_VULHUNT_RULES=~/vulhunt-lab/rules

ここも、実際に試した環境のパスに合わせて調整します。

echo "$BIAS_DATA"
echo "$BIAS_VULHUNT_RULES"

8. サンプルバイナリをスキャンする

準備ができたら、先ほど作った vulnerable をスキャンします。

cd ~/vulhunt-lab

vulhunt-ce scan ./sample/vulnerable \
  -o ./sample/results.json \
  -d "$BIAS_DATA" \
  -r "$BIAS_VULHUNT_RULES" \
  --loader component \
  --pretty

結果を整形して確認します。

cat ./sample/results.json | python3 -m json.tool | less

9. 結果を見るときの考え方

VulHunt CE の結果は、「検出されたから即アウト」と読むべきではありません。

見るべきなのは、次の点です。

- どのルールが発火したか
- どの関数で発火したか
- 危険な入力経路があるか
- 危険な API がどう使われているか
- 実際に到達可能なコードか
- 事前に長さチェックや検証があるか

たとえば、strcpy が含まれているだけで、すべてが脆弱性になるわけではありません。固定長の安全な入力しか渡らない場合や、事前に厳密な長さ確認がある場合は、リスクの見方が変わります。

VulHunt CE は、最終判定を自動で下すツールというより、人間が確認すべき場所を絞るためのツールとして使うのが自然です。


10. 安全版と比較する

次に、安全寄りの実装も作ります。

cd ~/vulhunt-lab/sample

cat > safe.c <<'EOF'
#include <stdio.h>
#include <string.h>

void copy_name(const char *input) {
    char buf[32];

    /*
     * 入力長を制限し、終端も保証する。
     */
    snprintf(buf, sizeof(buf), "%s", input);

    printf("name=%s\n", buf);
}

int main(int argc, char **argv) {
    if (argc < 2) {
        puts("usage: ./safe <name>");
        return 1;
    }

    copy_name(argv[1]);
    return 0;
}
EOF

gcc -g -O0 -fno-stack-protector -no-pie safe.c -o safe

同じルールでスキャンします。

cd ~/vulhunt-lab

vulhunt-ce scan ./sample/safe \
  -o ./sample/safe-results.json \
  -d "$BIAS_DATA" \
  -r "$BIAS_VULHUNT_RULES" \
  --loader component \
  --pretty

差分を見ます。

diff -u \
  <(cat ./sample/results.json | python3 -m json.tool) \
  <(cat ./sample/safe-results.json | python3 -m json.tool) \
  | less

この比較は重要です。同じような小さなプログラムでも、危険な API を直接使った場合と、長さ制限を入れた場合で、検出結果がどう変わるかを確認できます。

ツールの癖を見るには、実在の大きなファームウェアをいきなり読むより、このような小さな実験の方が向いています。


11. ファームウェア解析への応用

VulHunt CE が特に面白いのは、通常の Linux バイナリだけでなく、ファームウェア解析にもつなげやすいところです。

組み込み機器や医療機器のセキュリティ評価では、次のような場面があります。

- ソースコードが入手できないコンポーネントを確認したい
- ファームウェア内の ELF / PE / UEFI module を調べたい
- 危険 API の利用箇所を洗い出したい
- 既知脆弱性の亜種が残っていないか見たい
- SBOM だけでは分からない実装上のリスクを見たい

SBOM や SCA は、既知コンポーネントと既知脆弱性の管理には強いです。一方で、製品固有の実装ミスや、ベンダーが改変したバイナリ内の危険な処理は、それだけでは見えないことがあります。

VulHunt CE は、その隙間を補うための道具として見ると使いやすいです。


12. CI に組み込む場合

VulHunt CE は JSON で結果を出せるため、CI に組み込むこともできます。

ただし、最初から CI でブロック判定に使うのはおすすめしません。まずは artifact として結果を保存し、人間が確認する運用から始める方が安全です。

GitLab CI の例です。

stages:
  - binary_scan

vulhunt_scan:
  stage: binary_scan
  image: ubuntu:24.04
  script:
    - apt-get update
    - apt-get install -y ./vulhunt-ce.deb || true
    - vulhunt-ce scan ./build/target.bin
        -o vulhunt-results.json
        -d "$BIAS_DATA"
        -r "$BIAS_VULHUNT_RULES"
        --loader component
        --pretty
  artifacts:
    when: always
    paths:
      - vulhunt-results.json

本格的に使うなら、VulHunt CE、BIAS data、ルールセットを含めた専用 CI イメージを作る方が安定します。


まとめ


VulHunt CE は、バイナリから脆弱性候補を探すための実験的で面白いツールです。

特に価値があるのは、ソースコードがない対象に対して、実装上のリスクを探す入口を作れることです。組み込み機器、ファームウェア、UEFI モジュール、サードパーティ製バイナリを扱う現場では、この視点は重要です。


ただし、VulHunt CE の検出結果は、最終判定ではありません。検出された箇所を人間が読み、入力経路、到達可能性、境界チェック、実際の悪用可能性を確認する必要があります。

その意味で、VulHunt CE は「自動で脆弱性を見つけてくれる魔法のツール」ではなく、バイナリレビューの当たりを付けるための補助線です。


まずは小さな C バイナリで試し、検出結果の読み方に慣れてから、実際のファームウェアや製品バイナリに広げていくのがよいです。

bottom of page