TPM 通信キャプチャ・改ざんを支援するライブラリを公開しました。 本稿では環境を構築し、Windows 仮想マシンと仮想 TPM 間の通信のキャプチャ・改ざんするまでの方法を紹介します。
準備
PC が 1 台必用です。 TPM は無くても構いませんが、あった方が少しだけ楽しみが増えます。 Linux(Ubuntu) をインストール後、Linux 上で仮想マシンに Windows をインストールする流れになりますので、Windows 11 がインストール可能な程度のスペックが必用です。
PC が用意できたら次の作業を行ってください。
- BIOS(UEFI)で仮想化機能を有効化
- Ubuntu 24.04 インストール
- Linux 上で仮想マシンが動かせれば何でも構いません(Arch Linux はいいぞ)
- 本稿では Ubuntu 24.04 のコマンド、パッケージ、パスを前提に手順を解説します
- Windows 11 インストーラーの入手
- 機能が多い方が楽しめます
- 本稿では Windows 11 Pro をインストールすることにします
パッケージのインストール
必用パッケージをインストール後、現在ログイン中のユーザーで各種操作がしやすいよういくつかのグループに追加します。
sudo apt update
sudo apt install qemu-system-x86 virt-viewer ovmf swtpm swtpm-tools tpm2-tools golang libfuse-dev wireshark
sudo gpasswd -a "$USER" kvm
sudo gpasswd -a "$USER" tss
sudo gpasswd -a "$USER" wireshark
再ログイン
グループを反映するため一度ログアウトして再ログインします。 Wireshark を起動して、ループバックインターフェース(lo)のキャプチャができるか確認します。 うまくいかない場合は再起動したほうがいいかもしれません。
SWTPM 動作確認
次のコマンドを実行して、TPM の情報らしきものが表示されることを確認します。
確認後、swtpm は Ctrl+C
で終了してください。
mkdir "$HOME"/swtpm
swtpm socket --tpmstate dir="$HOME"/swtpm --tpm2 --ctrl type=unixio,path="$HOME"/swtpm/swtpm.sock.ctrl --server type=unixio,path="$HOME"/swtpm/swtpm.sock --flags not-need-init,startup-clear
tpm2_getcap --tcti=swtpm:path="$HOME"/swtpm/swtpm.sock properties-fixed
QEMU 準備
次のコマンドを実行して QEMU に Windows 11 をインストールする準備を整えます。 本稿では各種ファイルをホームディレクトリ直下に散らかしていきますが、適当に変更して頂いて構いません。
wget -O "$HOME"/virtio-win.iso https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso
qemu-img create -f qcow2 "$HOME"/win11.qcow2 64G
cp /usr/share/OVMF/OVMF_VARS_4M.ms.fd "$HOME"
QEMU 起動
QEMU Windows をシャットダウンする度に SWTPM が終了するので無限ループで実行しておきます。
while true; do swtpm socket --tpmstate dir="$HOME"/swtpm --tpm2 --ctrl type=unixio,path="$HOME"/swtpm/swtpm.sock.ctrl; done
SWTPM を QEMU の TPM デバイスに指定して QEMU を起動します。
ホームディレクトリ直下に win11.iso
というファイル名で Windows 11 インストーラーがあるものとします。
Windows をインターネットアクセスさせたい場合は -nic none
オプションは外しておいてください。
qemu-system-x86_64 \
-machine q35,accel=kvm \
-m 4096 \
-cpu host \
-smp 2,sockets=1,dies=1,cores=2,threads=1 \
-display spice-app \
-nic none \
-drive file="$HOME"/win11.qcow2,if=virtio,format=qcow2,discard=unmap \
-drive file="$HOME"/win11.iso,index=0,media=cdrom \
-drive file="$HOME"/virtio-win.iso,index=1,media=cdrom \
-drive if=pflash,format=raw,unit=0,file=/usr/share/OVMF/OVMF_CODE_4M.secboot.fd,readonly=on \
-drive if=pflash,format=raw,file="$HOME"/OVMF_VARS_4M.ms.fd \
-chardev socket,id=chrtpm,path="$HOME"/swtpm/swtpm.sock.ctrl \
-tpmdev emulator,id=tpm0,chardev=chrtpm \
-device tpm-tis,tpmdev=tpm0
Windows のインストール
エディションの選択がある場合、Windows 11 Pro を選択してください。
インストール先のストレージを選択するところまで進んだら、Load driver
をクリックします。
w11 の VirtIO を選択してインストールを進めてください。
インストール後の初期設定でインターネットアクセスや Microsoft アカウントのログインを求められるかと思います。 そのまま従うも良し、ローカルアカウントを作成する方法を調べるも良し・・(バイパス可能な手段はしばしば変わります)。 色々と理由があるのでしょうが、個人的にはこういった検証の際は余計なネットワークアクセスを排除したいので、Microsoft 社にはぜひ寛大な措置をお願いいたします..🙇
インストールが完了したら Windows をシャットダウンし、SWTPM を Ctrl+C
で停止します。
スナップショットも取っておくといいかもしれません。
qemu-img snapshot -c init "$HOME"/win11.qcow2
パケットキャプチャ
仕込みは終わりました。ここからが本番です。
SWTPM を TCP 通信に切り替えて起動します。
while true; do swtpm socket --tpmstate dir="$HOME"/swtpm --tpm2 --server port=2321 --ctrl type=tcp,port=2322; done
TPMProxy のサンプルプログラム qemu_swtpm_forward を起動します。 SWTPM の代わりに UNIX ドメインソケットの通信を受け、ファイルディスクリプタを受信し、通信を SWTPM へ転送します。
go run github.com/CyberDefenseInstitute/tpmproxy/example/qemu_swtpm_forward@latest
Wireshark さん! お仕事ですよ!
wireshark -k -i lo -f 'tcp portrange 2321-2322' -Y 'tpm'
/tmp/qemu_swtpm_fwd.sock
目掛けて TPM 通信をするよう指定しつつ QEMU Windows を起動すると・・。
qemu-system-x86_64 \
-machine q35,accel=kvm \
-m 4096 \
-cpu host \
-smp 2,sockets=1,dies=1,cores=2,threads=1 \
-display spice-app \
-nic none \
-drive file="$HOME"/win11.qcow2,if=virtio,format=qcow2,discard=unmap \
-drive if=pflash,format=raw,unit=0,file=/usr/share/OVMF/OVMF_CODE_4M.secboot.fd,readonly=on \
-drive if=pflash,format=raw,file="$HOME"/OVMF_VARS_4M.ms.fd \
-chardev socket,id=chrtpm,path=/tmp/qemu_swtpm_fwd.sock \
-tpmdev emulator,id=tpm0,chardev=chrtpm \
-device tpm-tis,tpmdev=tpm0
🎉
qemu_swtpm_forward とよく似たサンプルプログラムに qemu_swtpm_dissect があります。 パケットの解析を Wireshark だけに任せずに Go-TPM を使って自分でも解析を試みるサンプルです。 こちらはまたの機会に紹介します。
パケット改ざん
通信のキャプチャだけでなく改ざんも可能です。
パケットキャプチャしていた Windows をシャットダウンしてください。
qemu_swtpm_forward は勝手に終了しているかと思いますが、まだ残っていたら Ctrl+C
で停止します。
SWTPM、Wireshark はそのままで大丈夫です。
TPM の製造者を XYZ
に変更するだけの PoC qemu_swtpm_tamper を起動します。
go run github.com/CyberDefenseInstitute/tpmproxy/example/qemu_swtpm_tamper@latest
先程と同じコマンドで QEMU Windows を起動すると・・。
qemu-system-x86_64 \
-machine q35,accel=kvm \
-m 4096 \
-cpu host \
-smp 2,sockets=1,dies=1,cores=2,threads=1 \
-display spice-app \
-nic none \
-drive file="$HOME"/win11.qcow2,if=virtio,format=qcow2,discard=unmap \
-drive if=pflash,format=raw,unit=0,file=/usr/share/OVMF/OVMF_CODE_4M.secboot.fd,readonly=on \
-drive if=pflash,format=raw,file="$HOME"/OVMF_VARS_4M.ms.fd \
-chardev socket,id=chrtpm,path=/tmp/qemu_swtpm_fwd.sock \
-tpmdev emulator,id=tpm0,chardev=chrtpm \
-device tpm-tis,tpmdev=tpm0
💪
まとめ・次回予告
TPM 通信キャプチャ・改ざんを支援するライブラリ TPMProxy のサンプルプログラムを使うまでの手順を紹介しました。 こんなこと出来るのね、くらいの感想を持って頂けたら幸いです。
Windows 11 効果で TPM 2.0 デバイスの搭載が当たり前になり、TPM を活用したアプリケーション開発が活発化すると思われます。 しかし TPM 通信の盗聴・改ざんへの対策は、まだまだ当たり前のことではないようです。 TPM 通信は通信パラメーターの暗号化が可能ですが、情報が非常に少ない状態です。 さらにその少ない情報をじっくり確認してみると、わずかなミスにより一部の機微な情報を平文で通信している、または全く暗号化できていないことがわかりました。 実際に発生している通信の内容をキャプチャしてみると一目瞭然なのですが、TPM の専門家であっても TPM の通信を容易に確認できない状況があったり、通信を確認すること自体が当たり前ではない状況があるのではないかと思います。 そのような状況を改善すべく TPMProxy を開発、公開することにしました。
本稿はセットアップに終始してしまい十分な説明できておらず、もしかするとエミュレーターだと通信を見ることができると思われたかもしれません。 実機だとロジックアナライザなど解析用の機材が必用になりますが、通信のキャプチャは可能です(改ざんは見たことがありませんが原理的に可能です)。 そのため TPM 通信から BitLocker の鍵を窃取して暗号化ドライブの復号に成功したという記事が出る訳です(あまつさえ TPM の脆弱性などという誤情報をちょい足しした記事まで...)。 次回は TPMProxy を使って BitLocker 暗号化ドライブの復号までの流れを簡単に追ってみたいと思います。
サイバーディフェンス研究所では TPM リモートアテステーション技術を信頼の起点としたゼロトラストシステム PIV Gateway™ の研究開発を行っています。 PIV Gateway™ は TPM 通信パラメーターの暗号化に対応しています。 TPM 通信パラメーターの暗号化とは何か、実用化の課題など紹介できればと思います。
それでは、よい TPM ライフを👋