はじめに
以前にラズパイを使ってgolangやc言語などで自作のUSBデバイスを作ろうと考えた際に、linux kernelのドキュメントを眺めていて見慣れないUSBに関する機能があることに気づきました。
その機能の名前は "USB Raw Gadget" です。
USB raw-gadgetとは
Google のAndrey Konovalovさんが公開したもので、(すでにGoogleを退職して独立されているようですが) 2020年2月ごろにlinuxカーネルのmainlineにマージされました。
ラズパイ等での自作のUSBデバイス作成にはconfigfs経由で設定するUSB gadgetが有名ですが、fuzzingといったセキュリティの観点からさまざまなテストが行えるのがUSB raw-gadgetです。
また、本来はデバイスドライバなどカーネル空間で行うUSBに関する処理を、USB raw-gadgetはユーザ空間で行えるようになっています。 USBの基本的な仕様とUSB raw-gadgetのサンプルコードの動かし方についてはKLabTechBook Vol.10で私が解説しています。
USB raw-gadgetでファジング
どうやらAndrey KonovalovさんはこれをつかってlinuxカーネルのUSBドライバに関する脆弱性を大量に見つけているようです。1
また、同氏はさまざまなセキュリティカンファレンスで USB raw-gadget を用いたファジングの講演をされています。講演中にデモをされているのですが、USB経由で見事にOSがクラッシュした様子が確認できました。アーカイブ動画はYouTubeで確認できます。
軽く調べたところ、どうやらファジングを実施するUSBデバイスはラズパイZeroを用いて実施できるようです。
また、BSidesMunich 2022というセキュリティカンファレンスで発表されたスライドには面白い文章が書かれています。
How would one unlock
a locked Android device?Can we do this over USB?
「ロックされたAndroidデバイスをUSB経由でアンロックできるとしたら?」(意訳) という文章です。非常に興味をそそります。
おそらく(簡単ではないでしょうが)USB raw-gadgetを用いて物理的なUSBポート経由でファジングを実施し、見つかった脆弱性を使えば端末のロックを解除できるはずだ、という提案をされているのだと思います。
ラズパイZero WでUSBのファジングを行うには?
こちらのドキュメントが参考になります。
正確には "Running reproducers with Raspberry Pi Zero W" のセクションです。
(※ なお、ラズパイZeroのモデルとしてはwifiが使えるZero Wが推奨です。Wifiが使えないモデルの場合はある問題が発生します。というのもUSB端子がUSB Gadgetモードで一つ消費されるため、ラズパイのコンソールに気軽に入れなくなってしまうからです。Wifiが使えるモデルの場合はWifi経由でsshで入れるため楽ちんです。)
ドキュメントによるとsyzkaller USB reproducersなるものを準備すれば良いようです。
ラズパイZeroで稼働させるための、USB raw-gadgetに対応したsyzkallerをビルドする必要があるということですね。
ビルド手順を一部抜粋しましょう
git clone https://github.com/google/syzkaller
cd syzkaller
# Put the patch above into ./syzkaller.patch
git apply ./syzkaller.patch
make executor
mkdir ~/syz-bin
cp bin/linux_arm/syz-executor ~/syz-bin/
とてもシンプルですね。すんなりビルドできそうに見えます(フラグ)
では早速手持ちのラズパイZero2を取り出してソースを取得してパッチの修正を実施してmakeしてっと...。
root@raspberrypi:~/syzkaller# make executor
Makefile:32: run command via tools/syz-env for best compatibility, see:
Makefile:33: https://github.com/google/syzkaller/blob/master/docs/contributing.md#using-syz-env
go list -f '{{.Stale}}' ./sys/syz-sysgen | grep -q false || go install ./sys/syz-sysgen
make .descriptions
bin/syz-sysgen
make[1]: *** [Makefile:159: .descriptions] Killed
make: *** [Makefile:156: descriptions] Error 2
root@raspberrypi:~/syzkaller#
Killedというメッセージが見えます。どうやらビルドに失敗してしまってそうです。
ドキュメントをみると、make executorの次にmakeするexecprogについて注意事項が書かれていました。
10. Build syz-execprog on your host machine for arm32 with make TARGETARCH=arm execprog and copy to ~/syz-bin onto the SD card.
You may try building syz-execprog on the Raspberry Pi itself, but that worked poorly for me due to large memory consumption during the compilation process.
ラズパイでsyz-execprogのビルドを行うとメモリが足らなくなるので(しっかりとメモリ容量を積んだ良いスペックの)arm32のマシンを用意してそこでビルドして、ビルドできたバイナリをSDカードに持ってくるのが良いというふうに書かれています。私の環境ではexecprogの手前の手順のexecutorのビルドでメモリが枯渇したのではないかと思われます。
Killedというメッセージが吐かれていたので、oom killerが発動しているのではないでしょうか。
発動したかどうかについてはdmesgを見れば分かりそうです。dmesgを確認します。
[ 6633.994744] oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,global_oom,task_memcg=/,task=syz-sysgen,pid=29575,uid=0
[ 6633.994880] Out of memory: Killed process 29575 (syz-sysgen) total-vm:807968kB, anon-rss:375128kB, file-rss:0kB, shmem-rss:0kB, UID:0 pgtables:456kB oom_score_adj:0
[ 6634.139149] oom_reaper: reaped process 29575 (syz-sysgen), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB
メモリ不足により無事(?)にoom killerが発動してkillされていますね...。
我が家には開発用の32ビットarmアーキテクチャのハイスペックなマシンは持ち合わせておりません。。これは困りました。
次に、awsのEC2で解決できないかなと考えました。
公開されているAMIを軽く見てみると、64bitのarmアーキテクチャのものはあるのですが、32bitのものは見当たりません。。困りました。
なにか良い解決方法がないものでしょうか...。
ググったところやはり先人が良い方法を編み出しているのを見つけました。
こちらの記事に沿って、x86_64なubuntuをEC2立ち上げて、dockerでarmv7lなコンテナを動かしてそこでビルドを試みました。(もちろんx86_64のマシンが手元にあるのであれば私みたいにEC2を立ち上げる必要はありません)
結論からいうとこれでビルドは通りました。やったね。
さて、あとは出来上がったバイナリをラズパイZeroに持っていって...というところである問題にぶち当たりました。
ファジング対象のターゲットとなる(壊れても良い)PCが手元に余っていないのです。
古いPCはおうちにゴロゴロ転がっているのですが、どれもなにかしらデータは残っており、ファジングの結果、もし壊れて起動しなくなったりしたら困ります。
というわけで検証用の物理マシンを用意できるまでsyzkallerを試すのは保留となりました😇
もしも興味のある方がいらっしゃいましたら、上記の私の試行錯誤の結果を参考にビルドしてUSBのファジングを試していただけるとです。
今回は以上です。