Ubuntu Weekly Recipe

第740回USBGuardを使って悪意のあるUSBデバイスからシステムを守った気になろう

USBはコンピューターになんでも繋げられる魔法のコネクターです。なんでもということは便利なものだけでなく、もちろん悪意があるももの接続できます。今回はUSBGuardを使って、悪意のあるものを繋ぎにくくすることで、不要なトラウブルを回避できるようにしてみましょう。

BadUSB対策としてのUSBGuard

先に重要なことをお伝えしておきます。「悪意のある誰かがターゲットマシンに対して、物理的にデバイスを接続できる」時点で、ソフトウェアで対策できる範疇を超えています。今回の対応を行ったところで「安全になるわけではない」旨を肝に銘じておきましょう。

USB(Universal Serial Bus) は、現代的なコンピューターにおいて最も普及した規格のひとつです。昔のマシンは9ピンとか24ピンとか36ピンとかコネクタ形状とケーブル仕様の異なる様々なケーブルを、繋ぎたいデバイスに合わせて適切に選択し、接続する必要がありました。しかしながらUSBの普及とともにプリンターやカメラ、ヘッドセットといった定番の周辺機器だけでなく、最近はACアダプターやディスプレイに至るまで、そのほとんどがUSBコネクタで事足りるようになっているのです。

結果的にどんなデバイスでも「USBケーブルさえあれば繋がる」ようになりつつあります[1]。デバイス側もPC側もほとんどUSBコネクタに統一されてきたため、接続ポートに迷うこともないでしょう[2]。ただしUSB-Cより前のコネクタ形状には明確に裏と表がありました。コンピューターが一般に普及する過程において、⁠物事には常に裏と表がある」という教育的効果ももたらしてくれたのです。USB接続時に裏と表というまったく逆の試行を繰り返すことで、自分の考えと逆が真実だった、実は最初の考えで良かったという思考が育まれます。

このようにUSBはその利便性からPCだけでなく、スマートフォンやあらゆるデバイスに搭載される、まさに名前の通り汎用的なバスとなりました。

ところでUSBにはDevice Classという概念があります。これはそのUSBデバイスがどんな種類のデバイスなのかを示すものです。USBメモリーならMass Storage Classですし、USBキーボードならHID Classになります。ひとつのUSBデバイスが複数のDevice Classを持つことも可能で、ウェブカメラなどの大元はMiscellaneous Device Classですが、内部的にはVideo Device ClassとAudio Device Classの複合となっています。

このDevice ClassはUSBデバイス側がPC(USBホスト)側に通知します。つまりプロトコルさえ守っていれば何を自称しても構いません。見た目上はUSBメモリーだけど、USBデバイスとしてはHID Device Classにすることも可能なのです。

つまりあるUSBデバイスをUSBメモリーのつもりで接続したら、Ubuntu側ではただのUSBキーボードとして認識され、USBデバイスはキーイベントを送ることで、システム上の任意のデータをインターネット上の別のところに転送したり、悪意のあるプログラムをインストールしたり、その他の悪さをできてしまいます[3]。さらに最近のUSBデバイスは、内部にPCから書き換え可能なファームウェアを保持しているものもあり、悪意のあるファームウェアに置き換えられてしまう可能性があるのです。

要するに、最近の映画・アニメのハッカー・クラッカー表現でよく出てくる、USBデバイス接続するだけでいろいろやってくれて、画面がバーン!ってなるあれです。

この手の攻撃手法はBadUSBとして話題になっています。⁠出所のよくわからないUSBデバイスは接続しない」という当たり前の手法である程度回避できるものの、皆が皆、当たり前のことを当たり前にやってくれるとは限りません。よって、システム側で特定のUSBデバイスだけ許可する手法であるUSBGuardが意味を持ってくるのです[4]

USBGuardの仕組み

USBGuard自体は非常にシンプルな作りであり、やっていることは主に次の2種類です。

  • USBデバイスのホワイトリストを管理する
  • ホワイトリストに所属しないUSBデバイスが繋がれたら拒否する

ホワイトリストでは「どのUSBを許容するか」を指定します。このときに使えるのは次の項目となります。

  • USBデバイスのベンダーIDおよびプロダクトID
  • USBデバイスディスクリプターのハッシュ値
  • USBデバイスのデバイス名
  • USBデバイスのシリアル番号
  • USBデバイスが接続されたポート番号
  • USBデバイスのデバイスクラスやサブクラス
  • USBデバイスがホットプラグなのか直結なのか

つまりおおよそどんなUSBデバイスもこの組み合わせで指定可能です。USBデバイスが接続されると、カーネルからイベントが届きます。このイベントはueventと呼ばれるもので、たとえばUSBメモリーの自動マウントをはじめとする各種デバイスを認識した時に自動的に行われる処理を担うudevも、このueventを利用しています。ちなみにUSBGuardは以前はudev経由で動いていたようですが、現在ではueventを直接受け取って処理しているようです[5]

「許可・不許可」はカーネル側のUSBデバイスの認可システムとソフトウェア的な取り出し(remove)によって実現しています。具体的には前述の方法で特定したUSBデバイスに対して、次の状態を選択できます。

  • allow:USBデバイスが利用可能な状態
  • block:USBデバイスが利用不可な状態。USBGuardのCLIからallowへの変更も可能
  • reject:USBデバイスがソフトウェア的に切断された状態。再接続するには物理的に繋ぎ直す必要がある

ちなみに、これらはsysfsでも操作可能です。具体的にはまずlsusbでBus・Port番号を確認します。

$ lsusb -t -v
/:  Bus 06.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/2p, 10000M
    ID 1d6b:0003 Linux Foundation 3.0 root hub
    |__ Port 2: Dev 2, If 0, Class=Video, Driver=uvcvideo, 5000M
        ID 046d:0893 Logitech, Inc. StreamCam
(中略)
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/16p, 480M
    ID 1d6b:0002 Linux Foundation 2.0 root hub
    |__ Port 4: Dev 3, If 1, Class=Human Interface Device, Driver=usbhid, 12M
        ID 0853:0142 Topre Corporation
(中略)
    |__ Port 9: Dev 4, If 0, Class=Wireless, Driver=btusb, 12M
        ID 8087:0a2b Intel Corp. Bluetooth wireless interface

上記だと「Bus 06、Port 2(6-2⁠⁠」にウェブカメラのStreamCamが、⁠Bus 01、Port 4(1-4⁠⁠」にUSBキーボードが、⁠Bus 01、Port 9(1-9⁠⁠」にBluetoothレシーバーが繋がっていることがわかります。このとき次のような操作が可能です。

  • /sys/bus/usb/devices/6-2/authorizedに0を書くとデバイスが利用不可になる
  • /sys/bus/usb/devices/6-2/authorizedに1を書くとデバイスが利用可能になる
  • /sys/bus/usb/devices/6-2/removeに1を書くとソフトウェア的に取り外し状態になる(ファイルパスも見えなくなる)
  • /sys/bus/usb/devices/usb6/authorized_defaultに1を書くと、Bus 6以下がすべて利用不可になる

今回紹介するUSBGuardは、このあたりのファイルの操作を肩代わりしてくれるものだと思っておけば良いでしょう。

USBGuardのインストールと設定

USBGuardはパッケージをインストールするだけで使えるようになります。これはシンプルな操作用プログラム、デーモン、DBusサービスファイルなどの集合体です。もちろんUbuntuサーバーにもインストール可能です。

$ sudo apt install usbguard

インストール直後は「インストール時に接続済みのすべてのUSBデバイスはallowとし、新規に接続されたUSBデバイスはblockとする」設定になっています。これにより、USBGuardをインストールしたり、そのまま再起動したらUSBキーボードが認識せずに詰む、という状態を回避するのが目的です[6]

USBGuardの設定ファイルは次のとおりです。

  • /etc/usbguard/usbguard-daemon.conf:USBGuard本体の設定。設定ファイルのパスやデフォルトの挙動を指定する
  • /etc/usbguard/IPCAccessControl.d:USBGuardを操作できるユーザー・グループとその権限の設定
  • /etc/usbguard/rules.conf:USBGuardで設定したルール情報。インストール直後はその時つないでいたUSBデバイスが入っている

普通に使う分にはこれらのファイルを直接編集する必要はないでしょう。USBGuardの設定はusbguardコマンドで行い、このコマンドがrules.confの中身を更新してくれます。また「新規に追加されたUSBデバイスはblockとする」になっているのはusbguard-daemon.confImplicitPolicyTarget=blockと設定しているためです。

usbguardコマンドを実行できるのは、plugdevグループに所属するユーザーのみです。これはIPCAccessControl.d以下の設定ファイルで変更可能です。Ubuntuをインストール時に作成したユーザーであれば、最初からplugdevグループに所属しています。

まずは既存のルールの適用状態を確認してみましょう。インストール直後はすべて「allow」になっています。

$ usbguard list-devices
15: allow id 1d6b:0002 (略) name "xHCI Host Controller" (略)
16: allow id 1d6b:0003 (略) name "xHCI Host Controller" (略)
17: allow id 1d6b:0002 (略) name "xHCI Host Controller" (略)
18: allow id 1d6b:0003 (略) name "xHCI Host Controller" (略)
19: allow id 1d6b:0002 (略) name "xHCI Host Controller" (略)
20: allow id 1d6b:0003 (略) name "xHCI Host Controller" (略)
21: allow id 046d:0ab7 (略) name "Blue Microphones" (略)
22: allow id 0853:0142 (略) name "REALFORCE 91 JP" (略)
23: allow id 8087:0a2b (略) name "" (略)
24: allow id 046d:0893 (略) name "Logitech StreamCam" (略)
25: allow id 046d:c52b (略) name "USB Receiver" (略)
26: allow id 045b:0209 (略) name "" (略)
27: allow id 04b4:5210 (略) name "Billboard Device" (略)
28: allow id 056d:4036 (略) name "EIZO USB HID Monitor" (略)

またルール自体はusbguard list-rulesで確認可能です。

USBGuardによるデバイスの管理

ここで例えば、何か別のUSBデバイスを繋いだ上で、もう一度デバイス一覧を見てみましょう。

$ usbguard list-devices
(中略)
30: block id 046d:0a9b (略) name "G431 Gaming Headset" (略)

新規に繋いだデバイスは「block」になっていることがわかります。実際、上記ヘッドセットはサウンドデバイス一覧に現れませんし、USBメモリーなら「ファイル」アプリケーションからも選択できません。ちなみにrejectではなくblockなので、lsusbコマンドからなら存在を確認できます。

$ lsusb
(中略)
Bus 003 Device 005: ID 046d:0a9b Logitech, Inc. G431 Gaming Headset

$ cat /sys/bus/usb/devices/3-2/authorized
0

ただしusbguard list-rulesを見ると、このデバイスは表示されません。ルールファイルのほうはあくまで設定を恒久化するために使うものなのです。

次にこのデバイスを許可してみましょう。これはallow-deviceコマンドにlist-devicesでblockにされた行の先頭の数字を指定するだけです。ちなみにusbguard list-devices --blockedを実行するとblockになっているデバイスだけ一覧化できます。

$ usbguard list-devices --blocked
30: block id 046d:0a9b (略) name "G431 Gaming Headset" (略)

$ usbguard allow-device 30

$ usbguard list-devices --blocked
  => 何も表示されなくなる

許可するとUSBデバイスが使えるようになっているはずです。ちなみに今回は数字で指定しましたが、list-devicesに表示されている各フィールドでも許可は可能です。

Vendor ID/Device IDで許可する
$ usbguard allow-device id 046d:0a9b

デバイス名で許可する(ダブルクオートの前にバックスラッシュが必要)
$ usbguard allow-device name \"G431 Gaming Headset\"

ちなみにDevice IDにしろVendor IDにしろデバイス名にしろ、USBデバイス側が伝えてきた情報が使われます。つまり不正なUSBデバイスは、ルール上にある許可しているデバイスの名前を詐称することも原理的には可能なのです。繰り返しになってしまいますが、必ずしも不正なUSBデバイスをすべて弾ける仕組みでない旨を、理解しておきましょう。

恒久的に許可したい場合は-pオプションを付けます。

$ usbguard allow-device -p 30

$ usbguard list-rules | grep G431
31: allow id 046d:0a9b (略) name "G431 Gaming Headset" (略)

恒久的に許可されたルールは、再起動後も許可された状態が維持されます。同様にusbguard block-deviceでデバイスを不許可、usbguard reject-deviceを無効化できます。

現在のusbguardコマンドの-pオプションで作成したルールは、そのデバイスの「完全一致」となります。つまりDevice IDやベンダー名だけでなく、USBGuardがサポートするプロパティがすべて一致したもののみ適用されるルールが作成されます。

たとえば「Device ID/Vendor IDだけ一致したものをルールを作成したい場合は、append-ruleを使ってください。

$ usbguard append-rule "allow id 046d:0a9b"

これでルールファイルには、部分的なルールが追加されます。

$ usbguard list-rules | grep 0a9b
33: allow id 046d:0a9b

このようにUSBGuardを使えば、気軽に特定のUSBデバイスのみ許可したシステムを構築できます。意図しないUSBデバイスを接続してしまい、予期しない問題を起こさないよう、カジュアルに防止したい場合に便利でしょう。

おすすめ記事

記事・ニュース一覧