1 ポイント 投稿者 GN⁺ 2024-02-24 | 1件のコメント | WhatsAppで共有
  • ThinkPad X1 Carbon 6th Genの隠れたxDCIコントローラを有効化すると、ノートPC自体がキーボードやストレージ機器のような任意のUSBデバイスをエミュレートできるようになった
  • 手がかりは/sys/class/usb_role/intel_xhci_usb_sw-role-switchだったが、xHCIのロール切り替えだけでは不十分で、別個のDWC3ベースのxDCI UDCがPCIeデバイスとして見える必要があった
  • BIOSイメージにはxDCI Support設定があったが、標準のSetup UIでは隠されており、有効化後はlspciにxDCI 8086:9d30dwc3-pciドライバが現れた
  • 成功した方法はAdvanced BIOSページのロック解除NVRAMパッチであり、PCHレジスタの再構成はPMCの一部変更にとどまり、PSFロックのため失敗した
  • xDCI有効化後、g_mass_storage、Raw Gadget、syzkallerの再現プログラム、Facedancerバックエンドが外部ハードウェアなしで動作したが、VBUSが切断されたUSBケーブルとdeviceロールへの切り替えが必要だった

ThinkPadで見つけたUSBデバイスモードの手がかり

  • Raw GadgetはLinuxカーネルのUSB Gadgetサブシステムにユーザー空間APIを提供するカーネルモジュールで、不正なUSBディスクリプタを提供してUSBホストのファジングやエクスプロイトに利用できる
  • 一般的なPCには通常USB Device Controller、つまりUDCがなく、Raspberry Piのようなシングルボードコンピュータに主に内蔵されている
    • これまでの作業にはRaspberry PiやUSB 3380ベースのEC3380-ABのような外部ハードウェアが必要だった
  • ThinkPad X1 Carbon 6th Genで/sys/class/usb_roleの下にintel_xhci_usb_sw-role-switchが見つかった
    • /sys/class/usb_roleは、ハードウェアUSBコンポーネントをhostモードとdeviceモードの間で切り替えるインターフェースである
    • roleファイルにdeviceを書いても、当初はdmesgメッセージも/sys/class/udc/の項目も現れなかった
  • xHCIはx86システムでUSB hostとして動作させるHCDで、xDCIはIntelがUSB device側インターフェースに使う名称である

カーネルコードとメーリングリストで確認した構造

  • Linuxカーネルではintel_xhci_usb_swを2か所で確認できる
    • drivers/usb/roles/intel-xhci-usb-role-switch.cは、deviceまたはhostが書き込まれたときにxHCIレジスタを操作するUSB Role Switchドライバを実装している
    • drivers/usb/host/xhci-ext-caps.cは、xHCIドライバがXHCI_INTEL_USB_ROLE_SW quirkを設定した場合にintel_xhci_usb_sw仮想プラットフォームデバイスを作成する
  • ThinkPadのxHCI PCIe device IDは0x9d2fで、カーネルのPCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCIと一致していた
  • Linuxカーネルのメーリングリストの議論から、xHCIとDWC3 UDCは別個のデバイスであり、その間にmuxがあることが確認できた
    • role switchはこのmuxを切り替える役割を持つ
    • DWC3 UDC自体がPCIデバイスとして見えないなら、device modeで使うUDCがない状態である
  • DWC3はSynopsysのDesignWare Core SuperSpeed USB 3.0 Controller IPで、Intelを含む複数ベンダーがシステムで利用している
  • ThinkPadでlspciを確認した時点では、当初はxHCI 8086:9d2fしか見えず、dwc3-pci.cに基づくSunrise Point-LPのxDCI device IDである0x9d30は見えていなかった

ACPIとBIOSに残っていたxDCIの痕跡

  • ACPI DSDTにはDevice (XDCI)_STAメソッドが存在した
    • /sys/bus/acpi/devices/device:33/path\\_SB_.PCI0.XDCIだった
    • /sys/bus/acpi/devices/device:33/status15で、これはデバイスがenabled and functioning状態であることを意味する
    • しかしlspciにはxDCIがなかったため、ACPIの観点でのみ正常に見える状態だった
  • 標準のBIOS Setup画面にはxDCIやOTGに関する設定は見当たらなかった
  • Lenovo BIOS Bootable CD更新イメージをダウンロードし、BIOSバイナリ/mnt/bios/FLASH/N23ET86W/$0AN2300.FL1を抽出してUEFIToolで確認した
  • SetupモジュールのPE32 image sectionで文字列検索すると、次の項目が見つかった
    • xDCI Support
    • Enable/Disable xDCI (USB OTG Device).
  • BIOS内にはxDCI Support設定があるが、標準UIでは隠されていた

Advanced BIOSページでxDCIを有効化

  • x1c6-hackintoshプロジェクトの文書に従って、隠されたAdvanced BIOSページを開く方法を試した
  • この方法では、BIOSが保存されたSPIチップを再フラッシュする必要があった
    • SPIクリップを使う代わりにSPIチップをソケットに交換し、何度もチップを抜き差しして実験した
    • FTDI FT2232H Mini Moduleとflashromを使ってSPIチップの内容をダンプし、再書き込みした
  • BIOSパッチは2段階で構成された
    • UEFIPatchDate/Time BIOSページのGUID参照をAdvancedページ側に変更する
    • 別のバイトパッチでTPMをMFG Modeに強制し、Boot GuardがBIOS改変を検知しないようにする
  • BIOS 1.37では最初のパッチだけを適用するとノートPCがビープ音を出して起動せず、2つ目のパッチまで適用した後にAdvancedページが現れた
  • Intel Advanced MenuPCI-IO ConfigurationUSB Configuration配下のxDCI SupportEnabledに変更すると、起動後にxDCIが見えるようになった
    • lspci00:14.1 USB controller [8086:9d30]が現れた
    • カーネルはdwc3-pciドライバを自動ロードした
    • /sys/class/udc/dwc3.1.autoが現れた

xDCIポートとケーブル条件

  • xDCIに接続された外部USBポートを見つけるため、roleをdeviceにした状態でUSBフラッシュドライブを各ポートに挿して確認した
    • 特定のポートではOSがフラッシュドライブを検出せず、roleをhostに戻すと再び動作した
    • そのポートがxDCI接続ポートだと判断された
  • ThinkPadのxDCIポートはUSB Type-Aだったため、hostコンピュータに接続するにはType-A to Type-Aのmale-to-maleケーブルが必要だった
  • role switchingではポートの電源VBUSが切れなかったため、2台の機器のVBUSが直接つながらないようにVBUSが切断されたケーブルを使用した
    • 当初はUSB 2.0ケーブル2本を切ってVBUSを切断したmale-to-maleケーブルを自作した
    • 後にはPortaPow USB Power Blockerとmale-to-male USB 2.0ケーブルを使った
  • g_zero gadget driverをロードすると、別のノートPCでテストUSBデバイスが正常にエミュレートされた
  • 2024年6月11日の更新時点では、DataPro USB 3.0 Super-Speed A/A Debugging CableはUSB 2.0のD-D+も切断しており、SuperSpeed接続確立に必要なHigh-Speed接続に失敗した
    • VBUSを手動で切断したLogiLink CU0038ケーブルはSuperSpeedデバイスのエミュレーションで動作した

PCHレジスタでのソフトウェア有効化の試み

  • BIOSがxDCIをどう有効化しているかを把握するため、Kaby Lake BIOSソースコードのPch/Library/Private/PeiPchInitLib/PchXdci.cを確認した
  • BIOSのxDCI無効化経路は、xDCIをPMCPSFで無効化する構造だった
  • PMC変更

    • PMCのNST_PG_FDIS_1レジスタoffset 0x628のbit #24 XDCI Function Disableをクリアすれば、xDCIの電源遮断を解除できると考えた
    • Linuxカーネルのpmc_core_devicepmc->regbaseマッピングを再利用するカーネルモジュールを書いた
    • 起動直後はST_PG_FDIS1ST_FDIS_LK bit #31が設定されていて、NST_PG_FDIS_1の値を変更できなかった
    • ノートPCをsuspendしてから復帰するとlock bitが設定されておらず、XDCI Function Disable bitのクリアに成功した
  • PSF変更

    • BIOSはPID_PSF2 Port ID 0xBBのPSF2 PCRでUSB Dual Role (OTG) Function Disable bitを設定し、xDCIを無効化していた
    • PCRアドレスはPCH_PCR_BASE_ADDRESS 0xFD000000とPort ID、offsetの組み合わせで計算される
    • カーネルモジュールでPSF2 PCRをioremapしても、読み取り値は0xffffffffとなり変更できなかった
    • P2SBをunhideしても結果は同じだった
    • BIOSのRemoveSidebandAccessEPMASK5PID_PSF2をIOSF-SBから切り離し、MASKLOCKでロックしているため、OSからPSFを再設定できなかった
    • PCHアクセスではPMCの一部設定には成功したが、PSFの再構成に失敗したため、xDCI有効化手段にはならなかった

NVRAMパッチでBoot Guard回避なしに有効化

  • BIOS設定値はSPIチップのNVRAM領域に保存されるため、隠しAdvancedページを開かずにxDCI Support値だけを変更する方法を試した
  • IFRExtractor-RSSetupモジュールのUEFI IFR情報を抽出した
  • xDCI Support設定はPchSetup UEFI変数内に保存されていた
    • VarStore GUID: 4570B7F1-ADE8-4943-8DC3-406472842384
    • VarStoreId: 0x5
    • VarOffset: 0x40
    • 値の範囲: 0x0または0x1
  • Linuxのefivarfs/sys/firmware/efi/efivars/PchSetup-4570b7f1-ade8-4943-8dc3-406472842384を確認した
    • efivarfsファイルの先頭4バイトはヘッダなので、実際のxDCI Support値はファイルoffset 0x44にある
    • xDCI無効状態では値は00で、BIOS Advancedページで有効化した後は01に変わった
  • ユーザー空間やEFI Shellでその変数を直接書き込もうとするとEFI_WRITE_PROTECTEDエラーが発生した
    • efivarfsのimmutable属性を外してもRead-only file systemエラーが発生した
    • chipsecでEFI Shellから変更しても同じエラーになった
  • SPIプログラマで読み出した元のBIOSイメージからPchSetup付近のバイト列を探し、xDCI Support bitだけを01にパッチして再フラッシュするとxDCIが有効化された
  • この方法でもSPIチップの再フラッシュは必要だが、AdvancedページのパッチやBoot Guard回避は不要で、TPMも壊れなかった
  • BIOS 1.37でNVRAMをパッチした後に1.61へ更新してもBIOS設定値は保持され、パッチを再適用する必要はなかった

xDCIでUSBデバイスをエミュレートする

  • すべての例は、xDCIがBIOSで有効化され、intel_xhci_usb_sw-role-switchdeviceに切り替えられ、VBUSが切断されたケーブルでxDCIポートとUSB hostが接続されていることを前提とする
  • Legacy gadget drivers

    • Linux USB Gadgetサブシステムのlegacy gadget driversは、モジュールをロードすると特定のUSB classデバイスをエミュレートする
    • g_mass_storageはファイルシステムイメージをUSB mass storageデバイスとして公開する
    • FATイメージdisk.imgを作成し、file.txtHi from xDCIを入れてからsudo modprobe g_mass_storage file=./disk.img stall=0でロードした
    • USB host側ではドライブが自動マウントされ、ファイル内容を確認できた
  • Raw Gadget

    • Raw GadgetをxDCIとともに実行し、USBキーボードの例を試した
    • 当初はSET_CONFIGURATION処理中に停止した
    • 原因は、dwc3と一部のUDCドライバが、長さ0のcontrol requestをgadget driverの確認なしに即座にacknowledgeできると仮定していたためだった
    • Raw Gadgetにworkaround patchを適用し、GadgetFSも修正した後、キーボードのエミュレーションに成功した
    • 関連する挙動は文書パッチのcommitで整理されている

syzkallerとFacedancerの活用

  • syzkallerはRaw Gadgetを使ってLinuxカーネルのUSBスタックを外部からファジングできる
  • 標準のsyzkaller USBファジングは、カーネル内部でvirtual USB DeviceとHost controllerを接続するDummy HCD/UDCを使用する
  • xDCIを使うため、syzkallerのUDC指定コードをdummy_udcではなくdwc3-gadgetdwc3.1.autoに変更した
  • syzkallerが見つけたWARNING in smsusb_start_streaming/usb_submit_urbバグのreproducerをsyz-execprog -enable=usbで実行した
    • Ubuntu 5.15.0-91-genericカーネルをhostにしたノートPCでusb_submit_urb警告が再現した
    • このバグはsmsusbドライバがUSB endpoint typeの検証を省いていたために発生したWARNINGで、有害な挙動ではなかった
  • FacedancerはPythonベースのUSBデバイスエミュレーションフレームワークである
    • Raw GadgetベースのFacedancer backendはまだprototypeで、out-of-treeなRaw Gadgetパッチに依存している
    • BACKEND=rawgadgetRG_UDC_DRIVER=dwc3-gadgetRG_UDC_DEVICE=dwc3.1.autoを設定してrubber-ducky.pyの例を実行すると正常に動作した
  • Raw GadgetベースのUSB ProxyもxDCIとともに動作し、xDCIを使えばノートPCをUSB snifferやUSB Man-in-the-Middleツールとして使える

他の機器とソフトウェア方式の可能性

  • ThinkPad X1 Carbon 6th Gen以外のPCでもxDCIを有効化できる可能性がある
    • 最も単純な場合は、BIOS設定でxDCIを有効にするだけで動く可能性がある
    • ACPIとrole switchingのサポートがあり、xDCIポートが外部ポートに配線されている必要がある
  • 他のThinkPadでもxDCIのBIOS設定が隠され保護されている可能性があり、SPIチップの再フラッシュが必要かもしれない
    • NVRAM方式はBoot Guard回避なしで適用可能である
    • 一部の新しいシステムのSPIチップはRPMCで再フラッシュが保護されていることがあり、そのようなチップはW25R128FWのように型番にRが含まれることがある
  • 更新によれば、Shiny QuagsireはXPS 13とSurface Go 1でgrub-mod-setup_varによりPchSetup変数を変更してxDCIを有効化したことがある
    • それらの機器ではPchSetup変数が保護されておらず、純粋なソフトウェア方式が可能だった
  • 別のソフトウェア的アプローチとしては、BIOS脆弱性を利用してxDCI関連のNVRAM変数を変更したり、起動中にDMAでBIOSを攻撃して任意コードを実行したりする方法が考えられる
  • タイトルの“secret”は、意図的な隠蔽というより、文書化されていない機能を指す表現に近い

1件のコメント

 
GN⁺ 2024-02-24
Hacker News のコメント
  • ノートPCを別のコンピュータ用の汎用キーボード兼モニターとして使う機能が、なぜ一般的でないのかずっと不思議に思っている
    ヘッドレス機のメンテナンスのためにキーボードとモニターを新たに買うのは変だ。すでにノートPCがその役割を果たせるから

    • GPD Pocket 3 には、まさにこれを実現するKVMモジュールがある。HDMIとUSB-C入力を受け取り、接続されたマシンのディスプレイ・キーボード・マウスのように動作する: https://gpd.hk/gpdpocket3
      近く登場する Minisforum V3 タブレットにも映像入力があり、外部モニターのように使えるが、キーボードとマウスまでは共有されなさそう
      こういう機能がもっと一般的になると本当にうれしい
    • その通り。友人がこれだけのためにサイバーデッキを作った
      マウスとキーボードを外部ポートに切り替えて別のマシンへケーブル接続でき、側面にはHDMIポートが2つあって、一方は出力、もう一方は入力
      内部スイッチで、デッキ自体のマシンか外部入力のどちらをモニターソースにするか選べる
    • ヘッドレス機をよく扱う立場として、ノートPCをモニターとして使う最良の解決策はこの製品だった: https://www.amazon.com/dp/B0BJ2YDV7Q
      基本的には、ヘッドレス機の HDMI信号をキャプチャしてUSB経由でノートPCにエミュレートされたWebカメラとして見せるチップで、電子部品がケーブル内に収まるほど小さい
      キーボードについては、標準的なワイヤレスキーボードを使い、まれにマウスが必要になる場合に備えてトラックパッド内蔵型を買う以外に、良い解決策は見つけられていない
    • コンピュータ同士をそのまま接続できないのは、いつも不便に感じていた
      聞いたところでは、USB 4 にはこれを可能にするモードがあるらしい
    • hidclient を使えば、ノートPCをBluetoothキーボードとマウスのふりをさせられる
      https://web.archive.org/web/20180610141436/http://anselm.hoffmeister.be/computer/hidclient/index.html.en
      https://github.com/benizi/hidclient
  • 数年前に Travis Goodspeed がやっていたことで、思い出す事例がある
    スマートTVが一般的なUSBメモリ上のファイルからファームウェア更新を受け取り、TVはデジタル署名を確認するためにファイルを最初から最後まで読み、その後アップデートのためにもう一度読んでいた
    USBストレージ装置のふりをするデバイスなら、1回目の読み取りではメーカーのファームウェアファイルを送り、2回目には非公式ファームウェアを送れる

  • 本当に興味深い。似たように実現可能な選択肢を調べてきた
    使う口実をずっと探している強力な NASサーバー があり、家中のあちこちに40Gと10Gの接続も引いてある
    PS5とXBoxは追加ストレージとしてUSBハードディスクを使っている
    NASのストレージをPS5とXBoxに公開できるか調べたところ、可能だった。NAS共有をiSCSIまたはNFSでマウントし、g_mass_storage モジュールでUSBストレージをエミュレートして、そのストレージをUSBホストに公開すればよい
    現在の大きな障害は、時間と費用に加えて、このようなシステムが提供できる帯域幅だ。アップグレード幅はそれほど大きくない
    Raspberry PiはUSB-OTG対応で有名だがUSB 2.0速度にとどまり、調べた同クラスの他のSBCもほとんど同じだった。例外はRockPi4で、RockPiにはギガビットEthernetポートがあり、Ethernetを使い切ればPS5とXboxの両方に安定したHDD速度を提供できる
    カスタムPCIeネットワークカードやExpressCardを挿して 1GbEを超えられる解決策 を作れれば、USB 3.0インターフェースをきちんと飽和させられるので、かなり面白そうだ

    • 似たようなホームラボ構成があり、https://www.virtualhere.comIPベースのUSB を実験中
      サーバーと通信するクライアントは必要だが、かなりうまく動くようだ
      机の上に「薄いUSBクライアント」を置いてUSBデバイスをそこに接続し、実験室内の任意の物理ホストやVMホストへ接続または切り替える構成を考えている
    • PS5とNASを物理的にどう接続するのか気になる
      NAS → SBC → PS5だと推測しているが、それともNASをPS5にUSBケーブルで直接接続するのだろうか?
      PS5でのコンテンツ再生がどうなるのかも気になる。内蔵NVMeは特定の速度を満たす必要があると理解しているが、外付けドライブには適用されないのだろうか?
    • Raspberry Pi 5 にはギガビットEthernetポートがあり、FriendlyElecのNanoPi系のような他のSBCも同様
      実際、NanoPi R6Sは1ギガビットポートに加えて 2.5G Ethernetポートを2つ 搭載している
  • とても詳しい記事でありがたい。品質が高い
    これが可能だったという事実のおかげで、改造したUSBケーブルだけで、ネットワークなしの Synergy / Mouse Without Borders のような構成ができるかもしれないという希望が湧いた
    複数のコンピュータを同時に操作しなければならない人にとっては、とても良い方向性だ

    • KVMスイッチはあるし、映像なしでUSBキーボード・マウスだけを切り替える装置もある
      それでも、提案された方式は興味深く聞こえる
  • 最近、似たようなことを調べていて、USB-C搭載マシン2台をUSB-Cケーブル1本でつないで10Gbps通信をしたかった。
    残念ながら、ほとんどのRyzenボードにはまだThunderboltがなく、Thunderboltならこれをネイティブで簡単にサポートできる。
    「デュアルロール」のUSBコントローラも、USB 3.2/4.0以前はかなり珍しく、この記事で扱われているように、可能であってもサポートされていない場合が多かった。
    ThunderboltなしでUSB-Cケーブルを使ってマシン2台をネットワーク化するのがここまで難しいのは、まったくおかしく感じる。
    サスペンド後にロックビットが解除される部分もよかった。こういう問題は本当によくある。たとえば、SATAデバイスをセキュア消去を妨げる状態から抜け出させるときにも非常によく使われる方法だ。

  • 深さが印象的なレベル。
    PCをUSBデバイスとして使えると、面白い可能性がたくさん開ける。
    必要なxDCIオプションがこのデバイスのハードウェアにはあるのに公開されておらず、アクセスするにはファームウェアのハックが必要という点は少し残念。
    ハードウェア的には可能なのに、メーカーが単に無効化して制御機能をロックしているというわけだ。

  • Lenovoが、ThinkPad 1台で別のThinkPadをデバッグする経路としてこれを使っているのか気になる。
    https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/secure-coding/intel-debug-technology.html
    更新:おそらくそうらしい。
    https://www.youtube.com/watch?v=ExUvQa_jB7c
    Intel FirmwareをDCIとUSB 3.0でデバッグする動画だ。

  • 「しかしRaspberry Piを扱うのは面倒だ:配線、ボードの起動、シェルへの接続など」という部分は、おそらくUSB OTGを設定すればよかったのではないかと思う。
    接続先のデバイスから電源を受け取り、シェルを有効にしてからスクリプトで処理できたはず。
    そうすればSSHでマウント設定やコマンド実行を行い、おそらくデバイスのマウントまで可能だったと思う。
    それでも、自分のデバイスを理解し、カーネルを読んでみようとする試みは推奨したい。これからももっと上手くやりたいことであり、自分たちの機材は自分たちで直そう、という感じだ。

    • この実験の核心自体がUSB OTGを扱うことで、PiにはOTGポートが1つしかないため、接続するには別の方法が必要になる。
      実際にやってみた。
  • 初期インターネットのハッカー文化を強く思い起こさせて、郷愁を誘うほどだ。本当に素晴らしい記事。
    ESRが「ハッカーになること」や、Hacker News的な意味でのハッカーについて語ったときに指していたのは、まさにこういうタイプの人だった。
    好奇心、粘り強さ、技術力が組み合わさった姿は極めて珍しく、大きな印象を残す。

  • Celeron N3350/Pentium N4200/Atom E3900ベースのシステムを持っているが、BIOSにxDCIスイッチが公開されている。
    有効にすると他にもいくつかオプションが現れ、Linuxで起動するとPCIデバイス USB controller: Intel Corporation Device 5aaa (rev 0b) が見える。
    しかしUDCデバイスノードは現れない。元記事のヒントをもとに、もう少し掘ってみる必要がありそうだ。