ドライバーのセキュリティ チェックリスト
この記事では、ドライバーの開発者がセキュリティ侵害リスクを軽減できるよう、ドライバーのセキュリティ チェックリストを提供します。
ドライバーのセキュリティの概要
セキュリティ上の欠陥とは、攻撃者がドライバーを誤動作させ、システムがクラッシュしたり使用できなくなったりする原因となるあらゆる欠陥です。 さらに、攻撃者は、ドライバー コードの脆弱性を利用してカーネルにアクセスできるので、OS 全体のセキュリティが侵害される可能性があります。 ほとんどの開発者は、ドライバーの開発に従事しているときは、悪意のある攻撃者によってコード内の脆弱性が悪用されるリスクにではなく、ドライバーを適切に動作させることに意識が向くものです。
しかし、ひとたびドライバーがリリースされれば、攻撃者はセキュリティ上の欠陥を調査して特定しようとします。 開発者は、このような脆弱性の可能性を最小限に抑えるために、設計と実装の段階でこれらの問題を考慮する必要があります。 ドライバーがリリースされる前に、既知のセキュリティ上の欠陥をすべて排除することを目指しましょう。
より安全なドライバーを作成するには、システム アーキテクト (ドライバーに対する潜在的な脅威を意識して考える)、コードを実装する開発者 (悪用の原因となりやすい操作を防御的にコーディングする)、テスト チーム (積極的に弱点と脆弱性を見つけようとする) の協力が必要です。 これらの活動をすべて適切に調整することで、ドライバーのセキュリティは大幅に強化されます。
ドライバーへの攻撃に関連する問題を回避することに加え、カーネル メモリのより正確な使用など、説明されている手順の多くは、ドライバーの信頼性を向上させます。 これにより、サポート コストが削減され、製品に対する顧客満足度が向上します。 以下のチェックリストのタスクを完了することが、これらすべての目標を達成することにつながります。
セキュリティ チェックリスト: 各トピックに説明されているセキュリティ タスクを完了します。
BinSkim バイナリ アナライザーを使用してコードを確認する
Microsoft Vulnerable and Malicious Driver Reporting Center を使用してドライバーを報告する方法について理解する
「重要なポイントのまとめ」を確認する
カーネル ドライバーが必要であることを確認する
セキュリティ チェックリスト項目 1: カーネル ドライバーが必須であり、それよりもリスクの低いアプローチ (Windows サービスやアプリなど) が最良の選択肢ではないことを確認する。
ドライバーが Windows カーネルに存在し、カーネルでの実行時に問題が発生すると、オペレーティング システム全体が危険にさらされます。 他の選択肢がある場合は、新しいカーネル ドライバーを作成するよりもコストが低くなり、関連するリスクが低くなる可能性があります。 ビルトイン Windows ドライバーの使用の詳細については、「ドライバーの作成の必要性」を参照してください。
バックグラウンド タスクの使用については、「バックグラウンド タスクによるアプリのサポート」を参照してください。
Windows サービスの使用については、「サービス」を参照してください。
ドライバー フレームワークを使用する
セキュリティ チェックリスト項目 2: ドライバー フレームワークを使用して、コードのサイズを小さくし、信頼性とセキュリティを向上させる。
Windows Driver Framework を使用してコードのサイズを小さくし、信頼性とセキュリティを向上させます。 最初に、「WDF を使用したドライバーの開発」を参照してください。 リスクの低いユーザー モード フレームワーク ドライバー (UMDF) の使用については、「ドライバー モデルの選択」を参照してください。
従来の Windows ドライバー モデル (WDM) ドライバーの作成は、時間とコストがかかり、ほとんどの場合、ドライバー フレームワークにあるコードの再作成を伴います。
Windows Driver Framework のソース コードはオープンソースであり、GitHub で入手できます。 これは、Windows 10 に付属する WDF ランタイム ライブラリのソース コードと同じです。 ドライバーと WDF のやりとりを追跡できるとき、ドライバーをさらに効果的にデバッグできます。 https://github.com/Microsoft/Windows-Driver-Frameworks からダウンロードしてください。
ソフトウェア専用ドライバーへのアクセスを制御する
セキュリティ チェックリスト項目 3: ソフトウェア専用ドライバーを作成する場合は、追加のアクセス制御を実装する。
ソフトウェア専用カーネル ドライバーは、プラグアンドプレイ (PnP) を使用して特定のハードウェア ID に関連付けられることはなく、任意の PC で実行できます。 このようなドライバーは、本来意図された以外の目的で使用され、攻撃ベクトルを形成する可能性があります。
ソフトウェア専用カーネル ドライバーには別途リスクが伴うため、特定のハードウェアで実行するように制限する必要があります (一意の PnP ID を使用して PnP ドライバーの作成を可能にするか、特定のハードウェアの存在を SMBIOS テーブルで確認するなど)。
たとえば、OEM Fabrikam がシステムのオーバークロック ユーティリティに対応にするドライバーを配布するとします。 このソフトウェア専用ドライバーが別の OEM のシステムで実行された場合、システムが不安定になったりダメージを受けたりする可能性があります。 Fabrikam のシステムには、Windows Update でも更新可能な PnP ドライバーの作成を可能にするために、一意の PnP ID を含める必要があります。 これが不可能で、Fabrikam がレガシ ドライバーを作成する場合、そのドライバーは、Fabrikam システムで実行されていることを確認する別の方法を見つける必要があります (機能を有効にする前に SMBIOS テーブルを調べるなど)。
テスト コードに製品用の署名をしない
セキュリティ チェックリスト項目 4: 開発、テスト、製造段階のカーネル ドライバー コードに製品用のコード署名を行わない。
開発、テスト、または製造段階で使用されるカーネル ドライバー コードには、セキュリティ リスクを引き起こす危険な機能が含まれる場合があります。 この危険なコードに、Windows によって信頼されている証明書で署名することは避けてください。 UEFI セキュア ブートを無効にし、BCD "TESTSIGNING" を有効にして、信頼されていない証明書 (たとえば、makecert.exe によって生成されたもの) を使用して開発、テスト、製造段階のコードに署名するのが、危険なドライバー コードを実行する正しい方法です。
信頼のおける SPC (Software Publishers Certificate) または WHQL (Windows Hardware Quality Labs) 署名によって署名されたコードは、Windows コードの整合性やセキュリティ テクノロジを回避するための手段となってはいけません。 信頼のおける SPC または WHQL 署名によってコードを署名する前に、まず、「信頼性の高いカーネルモード ドライバーの作成」のガイダンスに準拠していることを確認します。 さらに、以下で説明する危険な動作をコードに含めてはいけません。 ドライバーの署名の詳細については、この記事で後述する「リリース ドライバーの署名」を参照してください 。
危険な動作の例としては、次のようなものがあります。
- 任意のカーネル メモリ、物理メモリ、またはデバイス メモリをユーザー モードにマッピングする機能を提供する。
- ポート入出力 (I/O) を含む、任意のカーネル メモリ、物理メモリ、デバイス メモリを読み書きする機能を提供する。
- Windows アクセス制御を回避するストレージへのアクセスを提供する。
- ドライバーが管理する設計上の範囲を超えてハードウェアまたはファームウェアを変更する機能を提供する。
脅威分析を実行する
セキュリティ チェックリスト項目 5: 既存のドライバー脅威モデルを変更するか、ドライバーのカスタム脅威モデルを作成する。
セキュリティを考慮する際の一般的な手法は、想定される攻撃の種類を記述することを目指す特定の脅威モデルを作成することです。 この手法は、ドライバーを設計するときに役立ちます。ドライバーに対する潜在的な攻撃ベクトルを事前に考慮するよう開発者に強く働きかけるためです。 潜在的な脅威を認識したドライバー開発者は、ドライバー コンポーネントの全体的なセキュリティを強化するために、これらの脅威から防御する手段を検討できます。
この記事では、軽量の脅威モデルを作成するためのドライバー固有のガイダンスとして、ドライバーの脅威モデリングについて取り上げます。 この記事では、ドライバー開発の出発点として利用できるドライバー脅威モデル ダイアグラムの例を紹介します。
セキュリティ開発ライフサイクル (SDL) のベスト プラクティスと関連ツールは、製品のセキュリティを向上させるために IHV と OEM が使用できます。 詳細については、「OEM 向けの SDL 推奨事項」を参照してください 。
ドライバーの安全なコーディング ガイドラインに従う
セキュリティ チェックリスト項目 6: コードを確認し、既知のコードの脆弱性を排除する。
セキュアなドライバーの開発の核となる活動は、既知のソフトウェアの脆弱性を回避するために変更を要するコード内領域を特定することです。 既知のソフトウェアの脆弱性の多くは、メモリの使用を厳密に追跡して、ドライバーが使用するメモリ位置が他のプログラムによって上書きされるなど、何らかの方法で侵害される問題に対処します。
CodeQL やドライバー固有のテストなどのコード スキャン ツールを使用すると、これらの脆弱性の一部 (すべてではなく) を特定できます。 これらのツールとテストについては、このトピックの後半で説明します。
メモリ バッファー
常に入力バッファーと出力バッファーのサイズをチェックして、要求されたすべてのデータをバッファーが確実に保持できるようにします。 詳細については、「バッファーのサイズの確認エラー」を参照してください。
すべての出力バッファーを、呼び出し元に返す前にゼロで適切に初期化します。 詳細については、「出力バッファーの初期化失敗」を参照してください。
可変長バッファーを検証します。 詳細については、「可変長バッファーの検証失敗」を参照してください。 バッファーの操作の詳細、および ProbeForRead と ProbeForWrite を使用してバッファーのアドレスを検証する方法の詳細については、「バッファー処理」を参照してください。
IOCTL で適切な方法を使用してデータ バッファーにアクセスする
Windows ドライバーの主な役割のひとつとして、ユーザーモード アプリケーションとシステムのデバイス間でのデータ転送があります。 データ バッファーにアクセスするための 3 つの方法を次の表に示します。
IOCTL バッファーの種類 | まとめ | 詳細情報 |
---|---|---|
METHOD_BUFFERED | ほとんどの状況で推奨 | バッファー付き I/O の使用 |
METHOD_IN_DIRECT または METHOD_OUT_DIRECT | 一部の高速 HW I/O で使用 | ダイレクト I/O の使用 |
METHOD_NEITHER | 可能であれば避ける | バッファー付き I/O とダイレクト I/O のどちらも使用しない |
一般に、バッファー付き I/O が、最も安全なバッファリング方法を提供するため推奨されます。 ただし、バッファー付き I/O を使用する場合でも、軽減すべきリスクがあります (埋め込みポインターなど)。
IOCTL でのバッファーの操作の詳細については、「データ バッファーにアクセスする方法」を参照してください。
IOCTL のバッファー付き I/O を使用しているときのエラー
IOCTL 関連バッファーのサイズを確認します。 詳細については、「バッファーのサイズの確認エラー」を参照してください。
出力バッファーを適切に初期化します。 詳細については、「出力バッファーの初期化失敗」を参照してください。
可変長バッファーを適切に検証します。 詳細については、「可変長バッファーの検証失敗」を参照してください。
バッファー付き I/O を使用する場合は必ず、IO_STATUS_BLOCK 構造体の Information フィールドに OutputBuffer の適切な長さを返します。 READ 要求から直接長さを返すことは避けてください。 たとえば、ユーザー空間から返されたデータが、4K のバッファーの存在を示している状況を考えてみましょう。 ドライバーが実際に返すべきデータが 200 バイトしかないのに、Information フィールドに 4K を返した場合、情報漏えいの脆弱性が生じます。 この問題が生じる理由は、以前のバージョンの Windows では、I/O マネージャーがバッファー付き I/O に使用するバッファーがゼロ クリアされないためです。 したがって、ユーザー アプリには、元の 200 バイトのデータに加えて、バッファー内に存在するあらゆるデータ (非ページ プールの内容) を含んだ 4K-200 バイトが返されます。 このシナリオは、IOCTL だけでなく、バッファー付き I/O のあらゆる用途で発生する可能性があります。
IOCTL ダイレクト I/O でのエラー
長さ 0 のバッファーを正しく処理します。 詳細については、「ダイレクト I/O のエラー」を参照してください。
ユーザー空間アドレスを参照する際のエラー
バッファー付き I/O 要求に埋め込まれたポインターを検証します。 詳細については、「ユーザー空間アドレスを参照する際のエラー」を参照してください。
必要に応じて ProbeForRead や ProbeForWrite などの API を使用して、ユーザー空間内のアドレスを使用する前に検証します。
MSR モデル固有のレジスタの読み取りと書き込み
__readmsr や __writemsr などのコンパイラ組み込みを使用して、モデル固有のレジスタにアクセスできます。 このアクセスが必要な場合、ドライバーは常に読み取りまたは書き込み対象のレジスタが予想されるインデックスまたは範囲に制限されていることをチェックする必要があります。
詳細とコード例については、「Windows ドライバー開発者向けの開発セキュリティのベスト プラクティス」の「MSR の読み取りと書き込みの機能の提供」を参照してください。
TOCTOU の脆弱性
ダイレクト I/O (IOCTL または読み取り/書き込み) を使用する場合、TOCTOU (time of check to time of use) の脆弱性が発生する可能性があります。 ドライバーがユーザー データ バッファーにアクセスしているとき、同時にユーザーもバッファーにアクセスしている可能性があることに注意してください。
このリスクに対応するには、検証が必要なパラメーターをユーザー データ バッファーから、カーネル モードからのみアクセス可能なメモリ (スタックやプールなど) にコピーします。 その後、ユーザー アプリケーションがデータにアクセスできなくなったら、渡されたデータを検証したうえで、データに対する操作を行ってください。
ドライバー コードでメモリを正しく使用する必要がある
すべてのドライバー プールの割り当ては、非実行可能 (NX) プール内にある必要があります。 NX メモリ プールの使用は、本質的に、実行可能な非ページ (NP) プールを使用するよりも安全であり、オーバーフロー攻撃に対する保護が強化されます。
デバイス ドライバーは、さまざまなユーザーモードの I/O 要求に加え、カーネル間の I/O 要求も適切に処理する必要があります。
ドライバーで HVCI 仮想化をサポートする場合、メモリの要件が増えます。 詳細については、この記事で後述する「HVCI 対応コードを実装する」を参照してください。
ハンドル
- ユーザーモードとカーネルモード メモリの間で渡されたハンドルを検証します。 詳細については、「ハンドル管理」および「オブジェクト ハンドルの検証失敗」を参照してください。
デバイス オブジェクト
デバイス オブジェクトをセキュリティで保護します。 詳細については、「デバイス オブジェクトの保全」を参照してください。
デバイス オブジェクトを検証します。 詳細については、「デバイス オブジェクトの検証失敗」を参照してください。
IRP
WDF と IRP
WDF を使用する利点の 1 つは、通常、WDF ドライバーが IRP に直接アクセスせずに済むことです。 たとえば、フレームワークは、読み取り、書き込み、デバイス I/O 制御操作を表す WDM IRP を、KMDF/UMDF が I/O キューで受信するフレームワーク要求オブジェクトに変換します。
WDM ドライバーを作成する場合は、次のガイダンスを確認してください。
IRP I/O バッファーを適切に管理する
次の記事では、IRP 入力値の検証に関する情報を提供しています。
バッファー付き I/O を使用した DispatchReadWrite
ダイレクト I/O を使用した DispatchReadWrite
バッファーのアドレスや長さなど、IRP に関連付けられている値を検証することを検討してください。
Neither I/O を使用する場合は注意が必要です。読み取りや書き込み、バッファー付き I/O、ダイレクト I/O とは異なり、Neither I/O IOCTL を使用する場合、バッファー ポインターと長さは I/O マネージャーによって検証されません。
IRP 完了操作を適切に処理する
ドライバーは、IRP を実際にサポートし、処理しない限り、STATUS_SUCCESS の状態値を持つ IRP を絶対に完了してはなりません。 IRP 完了操作の正しい処理方法については、「IRP の完了」を参照してください。
ドライバーの IRP 保留状態を管理する
ドライバーは、IRP を保存する前に IRP を保留としてマークする必要があります。また、IoMarkIrpPending の呼び出しと代入の両方をインターロックされたシーケンスに含める必要があります。 詳細については、「ドライバーの状態の確認エラー」および「デバイスが一時停止したときの着信 IRP の保留」を参照してください。
IRP のキャンセル操作を適切に処理する
キャンセル操作は、通常、非同期的に実行されるため、適切なコーディングが難しい場合があります。 多くの場合、このコードは実行中のシステムであまり実行されないため、キャンセル操作を処理するコードの問題は長期間気付かれないことがあります。 「IRP のキャンセル」に記載されているすべての情報を必ず読んで理解してください。 特に、「IRP キャンセルの同期」と「 IRP をキャンセルするときに考慮すべき点」に注意してください。
キャンセル操作に関連する同期の問題を最小限に抑えるために推奨される 1 つの方法は、キャンセル セーフな IRP キューを実装することです。
IRP のクリーンアップを処理し、操作を正常に終了する
IRP_MJ_CLEANUP 要求と IRP_MJ_CLOSE 要求の違いを正確に理解してください。 クリーンアップ要求は、アプリケーションがファイル オブジェクトのすべてのハンドルを閉じた後に届きますが、届いた時点で I/O 要求がまだすべて完了していない場合があります。 閉じる要求は、ファイル オブジェクトのすべての I/O 要求が完了または取り消された後に届きます。 詳細については、次の記事をご覧ください。
DispatchCreate、DispatchClose、DispatchCreateClose ルーチン
IRP の正しい処理方法の詳細については、「IRP の処理におけるその他のエラー」を参照してください。
その他のセキュリティの問題
ロックまたはインターロック シーケンスを使用して競合状態を防ぎます。 詳細については、「マルチプロセッサ環境におけるエラー」を参照してください。
さまざまなユーザーモードの I/O 要求に加え、カーネル間の I/O 要求をデバイス ドライバーで適切に処理します。
ドライバーや関連するソフトウェア パッケージのインストール中または使用中に、TDI フィルターまたは LSP がインストールされないようにします。
安全な関数を使用する
安全な文字列関数を使用します。 詳細については、「セーフ文字列関数の使用」を参照してください。
安全な算術演算関数を使用します。 詳細については、「安全な整数ライブラリ ルーチン」を参照してください。
安全な変換関数を使用します。
その他のコードの脆弱性
ここで取り上げた脆弱性リスクに加え、「信頼性の高いカーネルモード ドライバーの作成」の記事では、カーネル モード ドライバー コードのセキュリティ強化に関する追加情報が提供されています。
C および C++ のセキュア コーディングの詳細については、この記事の最後にあるセキュア コーディング リソースを参照してください。
ドライバーのアクセス制御を管理する
セキュリティ チェックリスト項目 7: ドライバーを点検して、アクセスを適切に制御していることを確認する。
ドライバーのアクセス制御の管理 - WDF
ドライバーには、コンピューターのデバイスやファイルに対してユーザーが不適切にアクセスするのを防ぐ機能が必要です。 デバイスとファイルへの不正アクセスを防ぐには、次の手順を実行する必要があります。
必要な場合にのみ、デバイス オブジェクトに名前を付けます。 一般的に、旧来の環境に対応する目的でのみ名前付きデバイス オブジェクトが必要になります。たとえば、特定の名前を使用してデバイスを開くアプリケーションや、PNP 以外のデバイスやコントロール デバイスを使用する場合が挙げられます。 WDF ドライバーは、WdfDeviceCreateSymbolicLink を使用してシンボリック リンクを作成するために、その PnP デバイスに FDO という名前を付ける必要はありません。
デバイス オブジェクトとインターフェイスへのアクセスをセキュリティで保護します。
アプリケーションまたはその他の WDF ドライバーから PnP デバイス PDO にアクセスできるようにするにはデバイス インターフェイスを使用する必要があります。 詳細については「デバイス インターフェイスの使用」を参照してください。 デバイス インターフェイスは、デバイス スタックの PDO へのシンボリック リンクとして機能します。
PDO へのアクセスを制御する、より優れた方法の 1 つは、INF で SDDL 文字列を指定することです。 INF ファイルに SDDL 文字列がない場合は、Windows によって既定のセキュリティ記述子が適用されます。 詳細については「デバイス オブジェクトのセキュリティ保護」と「デバイス オブジェクトを表す SDDL」を参照してください。
アクセスの制御の詳細については、次の記事を参照してください。
Names, Security Descriptors and Device Classes - Making Device Objects Accessible… and SAFE (January February 2017 The NT Insider Newsletter、OSR 発行)
ドライバーのアクセス制御の管理 - WDM
WDM ドライバーを使用していて、名前付きデバイス オブジェクトを使用している場合は、IoCreateDeviceSecure を使用し、SDDL を指定してセキュリティで保護できます。 IoCreateDeviceSecure を実装する場合は、DeviceClassGuid のカスタム クラス GUID を必ず指定します。 その場合、既存クラスの GUID は指定しないでください。 指定すると、そのクラスに属する他のデバイスのセキュリティ設定や互換性が損なわれることがあります。 詳細については「WdmlibIoCreateDeviceSecure」を参照してください。
詳細については、次の記事をご覧ください。
ドライバー開発者向けの Windows セキュリティ モデル
セキュリティ識別子 (SID) リスク階層
次のセクションでは、ドライバー コードで使用される一般的な SID のリスク階層について説明します。 SDDL の一般的な情報については、「デバイス オブジェクトを表す SDDL」、「SID 文字列」、「SDDL 文字列構文」を参照してください。
カーネルへのアクセスが、より低い特権の呼び出し元に許可されている場合、コード リスクが高くなることを理解しておくことが重要です。 この概要図で、ドライバー機能へのアクセスを特権のより低い SID に許可すると、リスクが増大します。
SY (System)
\/
BA (Built-in Administrators)
\/
LS (Local Service)
\/
BU (Built-in User)
\/
AC (Application Container)
一般的な最小特権セキュリティ原則に従って、ドライバーが機能するために最低限必要なレベルのアクセスのみを構成してください。
WDM の粒度の細かい IOCTL セキュリティ制御
ユーザーモードの呼び出し元によって IOCTL が送信されたときのセキュリティをさらに細かく管理するために、ドライバー コードには IoValidateDeviceIoControlAccess 関数を含めることができます。 この関数を使用すると、ドライバーはアクセス権をチェックできます。 IOCTL を受信すると、ドライバーは、FILE_READ_ACCESS、FILE_WRITE_ACCESS、またはその両方を指定して、IoValidateDeviceIoControlAccess を呼び出すことができます。
粒度の細かい IOCTL セキュリティ制御を実装することは、上で説明した手法を使用してドライバー アクセスを管理する必要性に代わるものではありません。
詳細については、次の記事をご覧ください。
HVCI 互換コードを実装する
セキュリティ チェックリスト項目 8: ドライバーが HVCI 対応となるようにメモリを使用していることを確認する。
メモリ使用と HVCI 対応
HVCI は、ハードウェア テクノロジと仮想化を使用して、コード整合性 (CI) の意思決定機能をオペレーティング システムの残りの部分から分離します。 HVCI では、仮想化ベースのセキュリティを使って、CI を分離します。カーネル メモリが実行できるようになる唯一の方法は、CI の検証にパスすることです。 つまり、カーネル メモリ ページを書き込み可能にすることはできず、実行可能ファイル (W+ X) と実行可能コードを直接変更することはできません。
HVCI 対応のコードを実装するには、ドライバー コードが次の条件を満たしていることを確認します。
- 既定で NX にオプトインする
- メモリ割り当て (NonPagedPoolNx) に NX API/フラグを使用する
- 書き込み可能かつ実行可能なセクションを使用しない
- 実行可能ファイルのシステム メモリを直接変更しない
- カーネルで動的コードを使用しない
- データ ファイルを実行可能ファイルとして読み込まない
- セクションのアラインメントは、0x1000 (PAGE_SIZE) の倍数とする。 たとえば、DRIVER_ALIGNMENT=0x1000
ツールの使用と互換性のないメモリ呼び出しの一覧の詳細については、「HVCI 対応コードを実装する」を参照してください。
関連するシステム基礎セキュリティ テストの詳細については、「ハイパーバイザー コード整合性準備テスト」および「ハイパーバイザーで保護されたコード整合性 (HVCI)」を参照してください。
テクノロジ固有のコードのベスト プラクティスに従う
セキュリティ チェックリスト項目 9: ドライバーに関する次のテクノロジ固有のガイダンスを確認する。
ファイル システム
ファイル システム ドライバーのセキュリティに関する詳細については、次の記事をご覧ください。
NDIS - ネットワーク
NDIS ドライバーのセキュリティについては、「ネットワーク ドライバーのセキュリティの問題」を参照してください。
ディスプレイ
ディスプレイ ドライバーのセキュリティについては、「<コンテンツの保留中>」を参照してください。
プリンター
プリンター ドライバーのセキュリティに関する詳細については、「V4 プリンター ドライバー セキュリティ考慮事項」を参照してください。
Windows Image Acquisition (WIA) ドライバーのセキュリティに関する問題
WIA セキュリティについては、「Windows Image Acquisition (WIA) ドライバーに関するセキュリティの問題」を参照してください。
デバイス インストールのセキュリティを強化する
セキュリティ チェックリスト項目 10: ドライバーの inf の作成とインストールのガイダンスを確認して、ベスト プラクティスに従っていることを確認する。
ドライバーをインストールするコードを作成するときは、デバイスのインストールが常に安全な方法で実行されるようにする必要があります。 デバイスのインストールが安全であるとは、次のことを指します。
- デバイスとそのデバイス インターフェイス クラスへのアクセスを制限する
- デバイス用に作成されたドライバー サービスへのアクセスを制限する
- 変更または削除からドライバー ファイルを保護する
- デバイスのレジストリ エントリへのアクセスを制限する
- デバイスの WMI クラスへのアクセスを制限する
- SetupAPI 関数を正しく使用する
詳細については、次の記事をご覧ください。
ピア コード レビューを実行する
セキュリティ チェックリスト項目 11: ピア コード レビューを実行して、他のツールやプロセスでは顕在化しない問題を探す
知識豊富なコード レビュー担当者を当たって、自分が見逃した可能性のある問題を探します。 第三者の目で見ると、見過ごした可能性のある問題が見つかることはよくあります。
この目的に関して、コードのレビューに適したスタッフが社内にいない場合は、外部に協力を求めることを検討してください。
リリース ドライバーの署名を適切に実施する
セキュリティ チェックリスト項目 12: Windows パートナー ポータルを使用して、配布用のドライバーに適切に署名する。
ドライバー パッケージを一般にリリースする前に、パッケージを認証のために提出することをお勧めします。 詳細については、「パフォーマンスと互換性のテスト」、「ハードウェア申請プロセスの概要」、「ハードウェア ダッシュボード サービス」、「一般公開向けにカーネル ドライバーに構成証明署名を行う」を参照してください。
CodeQL を使用してドライバー コードを確認する
セキュリティ チェックリスト項目 13: CodeQL を使用してドライバー コードの脆弱性を確認する。
GitHub の CodeQL は、セマンティック コード分析エンジンで、広範なセキュリティ クエリのスイートと堅牢なプラットフォームの組み合わせにより、ドライバー コードをセキュリティで保護するための非常に貴重なツールです。 詳しくは、「CodeQL と静的ツール ロゴ テスト」をご覧ください。
ドライバー コードに SAL 注釈を追加する
セキュリティ チェックリスト項目 14: SAL 注釈をドライバー コード内に追加する。
ソース コード注釈言語 (SAL) は、関数が自身のパラメーターをどのように使用するかを記述するのに使用できる注釈のセットを提供します。これらの注釈は、関数がそのパラメーターについて何を前提としているか、または関数が終了時に何を保証するかを示します。 注釈はヘッダー ファイル sal.h
で定義されています。 C++ の Visual Studio コード分析では、SAL 注釈を使用して関数の分析を変更します。 Windows ドライバー開発用の SAL 2.0 の詳細については、「Windows ドライバーの SAL 2.0 注釈」と「C/C++ コードの欠陥を減らすための SAL 注釈の使用」を参照してください。
SAL の一般的な情報については、OSR から入手できるこの記事を参照してください。 https://www.osr.com/blog/2015/02/23/sal-annotations-dont-hate-im-beautiful/
ドライバー検証ツールを使用して脆弱性をチェックする
セキュリティ チェックリスト項目 15: ドライバー検証ツールを使用してドライバー コードの脆弱性を確認する。
ドライバー検証ツールでは、一連のインターフェイス規則とオペレーティング システムのモデルを使用して、ドライバーが Windows オペレーティング システムと正しく対話するかどうかを判断します。 DV は、ドライバーの潜在的なバグを示す可能性があるドライバー コードの欠陥を検出します。
ドライバーの検証ツールでは、ドライバーのライブ テストが可能です。 ドライバーの検証ツールは、Windows カーネルモード ドライバーとグラフィックス ドライバーを監視して、無効な関数呼び出しやシステムを破損する可能性があるアクションを検出します。 ドライバーの検証ツールを使用すると、Windows ドライバーに対してさまざまな負荷やテストを実施し、不適切な動作を見つけることができます。 詳細については、「ドライバーの検証ツール」を参照してください。
DV では、特定の種類のドライバーのみがサポートされることに注意してください。 DV で検証できるドライバーの詳細については、「サポートされているドライバー」を参照してください。 ご使用のドライバーの種類で使用できる DV テストについては、次のページを参照してください。
DV について理解を深めるために、いずれかのサンプル ドライバー (たとえば、おすすめのトースター サンプル: https://github.com/Microsoft/Windows-driver-samples/tree/main/general/toaster/toastDrv/kmdf/func/featured) を使用できます。
BinSkim バイナリ アナライザーを使用してコードを確認する
セキュリティ チェックリスト項目 16: 以下の手順に従い、既知のセキュリティの問題を最小限に抑えるようにコンパイルとビルドのオプションが構成されていることを、BinSkim を使用してダブル チェックする。
BinSkim を使用してバイナリ ファイルを調査し、バイナリの脆弱性につながるコーディングとビルドの慣例を特定します。
BinSkim でチェックされる内容:
- 古いコンパイラ ツール セットの使用 - バイナリは、可能な限り最新のコンパイラ ツール セットでコンパイルして、現在のコンパイラレベルと OS が提供するセキュリティ軽減策を最大限に活用する必要があります。
- セキュリティで保護されていないコンパイル設定 - バイナリは、OS で提供されるセキュリティ軽減策を有効にし、とりわけコンパイラ エラーやアクションにつながる警告の報告を最大化するために、可能な限り最も安全な設定でコンパイルする必要があります。
- 署名の問題 - 署名されたバイナリは、暗号強度の高いアルゴリズムで署名する必要があります。
BinSkim はオープンソース ツールであり、静的分析結果交換形式 (SARIF) 形式を使用する出力ファイルを生成します。 BinSkim は、以前の BinScope ツールを置き換えます。
BinSkim の詳細については、BinSkim ユーザー ガイドを参照してください。
次の手順に従って、出荷するコードでセキュリティ コンパイル オプションが正しく構成されていることを確認します。
クロス プラットフォームの .NET Core SDK をダウンロードしてインストールします。
Visual Studio がインストールされていることを確認します。 Visual Studio のダウンロードとインストールについては、「Visual Studio のインストール」を参照してください。
NuGet パッケージなど、BinSkim をダウンロードするオプションは多数あります。 この例では、git clone オプションを使用してこちら (https://github.com/microsoft/binskim) からダウンロードし、64 ビット Windows PC にインストールします。
Visual Studio 開発者コマンド プロンプト ウィンドウを開き、ディレクトリ (例:
C:\binskim-master
) を作成します。C:\> Md \binskim-master
先ほど作成したディレクトリに移動します。
C:\> Cd \binskim-master
git clone コマンドを使用して、必要なすべてのファイルをダウンロードします。
C:\binskim-master> git clone --recurse-submodules https://github.com/microsoft/binskim.git
clone コマンドで作成した新しい
binskim
ディレクトリに移動します。C:\> Cd \binskim-master\binskim
BuildAndTest.cmd を実行して、リリース ビルドが成功し、すべてのテストが成功することを確認します。
C:\binskim-master\binskim> BuildAndTest.cmd Welcome to .NET Core 3.1! --------------------- SDK Version: 3.1.101 ... C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64\BinSkim.Sdk.dll 1 File(s) copied C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\linux-x64\BinSkim.Sdk.dll 1 File(s) copied ...
ビルド プロセスにより、BinSkim 実行可能ファイルを含む一連のディレクトリが作成されます。 win-x64 ビルド出力ディレクトリに移動します。
C:\binskim-master\binskim> Cd \binskim-master\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64>
分析オプションのヘルプを表示します。
C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64> BinSkim help analyze
BinSkim PE/MSIL Analysis Driver 1.6.0.0
--sympath Symbols path value, e.g., SRV*http://msdl.microsoft.com/download/symbols or Cache*d:\symbols;Srv*http://symweb. See
https://learn.microsoft.com/windows-hardware/drivers/debugger/advanced-symsrv-use for syntax information. Note that BinSkim will clear the
_NT_SYMBOL_PATH environment variable at runtime. Use this argument for symbol information instead.
--local-symbol-directories A set of semicolon-delimited local directory paths that will be examined when attempting to locate PDBs.
-o, --output File path to which analysis output will be written.
--verbose Emit verbose output. The resulting comprehensive report is designed to provide appropriate evidence for compliance scenarios.
...
シンボル パスを設定する
BinSkim を実行している同じコンピューターで分析しているすべてのコードをビルドする場合、通常、シンボル パスを設定する必要はありません。 これは、コンパイルしたローカル ボックスでシンボル ファイルを使用できるためです。 より複雑なビルド システムを使用している場合、またはシンボルを (コンパイルされたバイナリと一緒にではなく) 別の場所にリダイレクトする場合は、--local-symbol-directories
を使用して、シンボル ファイルの検索にこれらの場所を追加します。
自分のコードには含まれないコンパイル済みバイナリを参照している場合、これらのコードの依存関係のセキュリティを確認するために、Window デバッガーの sympath を使用してシンボルを取得できます。 これらの依存関係で見つかった問題は修正できない可能性があります。 ただし、これらの依存関係を採用することで負うことになるセキュリティ リスクを認識できるというメリットはあります。
ヒント
(ネットワーク シンボル サーバーを参照する) シンボル パスを追加する場合は、ローカル キャッシュの場所を追加して、シンボルをキャッシュするローカル パスを指定します。 これを行わないと、BinSkim のパフォーマンスが大幅に低下する可能性があります。 次の例では、d:\symbols にローカル キャッシュを指定します。
--sympath Cache*d:\symbols;Srv*http://symweb
sympath の詳細については、「Windows デバッガーのシンボル パス」を参照してください。
次のコマンドを実行して、コンパイル済みのドライバー バイナリを分析します。 コンパイル済みのドライバー .sys ファイルを指すターゲット パスを更新します。
C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64> BinSkim analyze "C:\Samples\KMDF_Echo_Driver\echo.sys"
詳しい情報が必要な場合は、次のように verbose オプションを追加します。
C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64> BinSkim analyze "C:\Samples\KMDF_Echo_Driver\osrusbfx2.sys" --verbose
Note
--verbose オプションは、すべてのチェックに対して明示的に成功/失敗の結果を生成します。 verbose を指定しない場合は、BinSkim によって検出された欠陥のみが表示されます。 --verbose オプションは、通常、ログ ファイルのサイズが大きくなり、大量の "成功" の結果に埋もれて、発生した個々の失敗の識別が困難になるため、実際のオートメーション システムでは推奨されません。
コマンド出力を確認して、考えられる問題を探します。 この出力例は、合格した 3 つのテストを示しています。 BA2002 などの規則に関する追加情報については、BinSkim のユーザー ガイドを参照してください。
Analyzing... Analyzing 'osrusbfx2.sys'... ... C:\Samples\KMDF_Echo_Driver\osrusbfx2.sys\Debug\osrusbfx2.sys: pass BA2002: 'osrusbfx2.sys' does not incorporate any known vulnerable dependencies, as configured by current policy. C:\Samples\KMDF_Echo_Driver\Debug\osrusbfx2.sys: pass BA2005: 'osrusbfx2.sys' is not known to be an obsolete binary that is vulnerable to one or more security problems. C:\Samples\KMDF_Echo_Driver\osrusbfx2.sys: pass BA2006: All linked modules of 'osrusbfx2.sys' generated by the Microsoft front-end satisfy configured policy (compiler minimum version 17.0.65501.17013).
この出力は、ドライバーが ELF バイナリではなく、テスト BA3001 が実行されていないことを示しています。
... C:\Samples\KMDF_Echo_Driver\Debug\osrusbfx2.sys: notapplicable BA3001: 'osrusbfx2.sys' was not evaluated for check 'EnablePositionIndependentExecutable' as the analysis is not relevant based on observed metadata: image is not an ELF binary.
この出力は、テスト BA2007 のエラーを示しています。
... C:\Samples\KMDF_Echo_Driver\Debug\osrusbfx2.sys: error BA2007: 'osrusbfx2.sys' disables compiler warning(s) which are required by policy. A compiler warning is typically required if it has a high likelihood of flagging memory corruption, information disclosure, or double-free vulnerabilities. To resolve this issue, enable the indicated warning(s) by removing /Wxxxx switches (where xxxx is a warning id indicated here) from your command line, and resolve any warnings subsequently raised during compilation.
Visual Studio でこれらの警告を有効にするには、プロジェクトのプロパティ ページの C/C++ で、除外したくない値を [指定の警告を無効にする] から削除します。
Visual Studio のドライバー プロジェクトの既定のコンパイル オプションでは、次のような警告を無効にすることができます。 これらの警告は BinSkim によって報告されます。
コンパイラの警告の詳細については、「コンパイラのバージョン別のコンパイラの警告」を参照してください。
ハードウェア互換性プログラムのテストでコードを確認する
セキュリティ チェックリスト項目 17: セキュリティ関連のハードウェア互換性プログラム テストを使用して、セキュリティの問題を確認する。
ハードウェア互換性プログラムには、コードの脆弱性を探すために使用できるセキュリティ関連のテストが含まれています。 Windows ハードウェア互換性プログラムは、Windows ハードウェア ラボ キット (HLK) のテストを利用します。 HLK Device Fundamentals テストをコマンド ラインで使用して、ドライバー コードを実行し、弱点を調査できます。 デバイスの基礎テストとハードウェア互換性プログラムに関する一般的な情報については、「Windows Hardware Lab Kit」を参照してください。
以下に示したのは、コードの脆弱性に関連するいくつかの動作についてドライバー コードをチェックするのに役立つ可能性のあるテストの例です。
DF - ランダム IOCTL のファジー テスト (信頼性)
DF - 0 長バッファー FSCTL のファジー テスト (信頼性)
DF - ランダム FSCTL のファジー テスト (信頼性)
ドライバー検証ツールに含まれているカーネル同期遅延ファジーを使用することもできます。
CHAOS (Concurrent Hardware and Operating System) テストでは、さまざまな PnP ドライバー テスト、デバイス ドライバーのファジー テスト、および電源システム テストを同時に実行します。 詳細については、「CHAOS テスト (Device Fundamental)」を参照してください。
デバイス基礎侵入テストでは、セキュリティ テストの重要な要素である入力攻撃をさまざまな形で実行します。 攻撃と侵入のテストは、ソフトウェア インターフェイスの脆弱性を特定するのに役立ちます。 詳細については、「侵入テスト (Device Fundamental)」を参照してください。
「Device Guard - コンプライアンス テスト」を、この記事で説明されている他のツールと共に使用して、ドライバーが HVCI 対応であることを確認します。
ドメイン固有のカスタム テスト ツール
ドメイン固有のカスタム セキュリティ テストの開発を検討します。 追加のテストを開発するには、ソフトウェアの元の設計者からのインプットや、開発中の特定の種類のドライバーに精通している無関係の開発リソース、そして、セキュリティ侵入分析と防止に精通している人物からのインプットを収集します。
デバッガーの手法と拡張機能を確認する
セキュリティ チェックリスト項目 18: これらのデバッガー ツールを確認し、開発デバッグ ワークフローでの使用を検討する。
セキュリティ関連のデバッガー コマンド
!acl 拡張機能は、アクセス制御リスト (ACL) の内容を書式設定して表示します。 詳細については、「オブジェクトの ACL の判別」と「!acl」を参照してください。
!token 拡張機能は、セキュリティ トークン オブジェクトを書式設定された形式で表示します。 詳細については、「!token」を参照してください。
!tokenfields 拡張機能は、アクセス トークン オブジェクト (TOKEN 構造体) 内のフィールドの名前とオフセットを表示します。 詳細については、「!tokenfields」を参照してください。
!sid 拡張機能は、指定されたアドレスのセキュリティ識別子 (SID) を表示します。 詳細については、「!sid」を参照してください。
!sd 拡張機能は、指定されたアドレスのセキュリティ記述子を表示します。 詳細については、「!sd」を参照してください。
Microsoft Vulnerable and Malicious Driver Reporting Center
疑わしいドライバーは、だれでも Microsoft Vulnerable and Malicious Driver Reporting Center を使用して報告することができます。 分析対象のドライバーを提出する方法については、新しい Microsoft Vulnerable and Malicious Driver Reporting Center を使用してカーネルのセキュリティを強化する方法についてのブログ記事をご覧ください。
Reporting Center では、x86 および x64 アーキテクチャ用にビルドされた Windows ドライバーをスキャンして分析できます。 脆弱性や悪意のあるスキャンされたドライバーには、Microsoft’s Vulnerable Driver チームによる分析と調査の対象としてフラグが設定されます。 脆弱性のあるドライバーが確認されると、適切な通知と共に、脆弱性のあるドライバーのブロックリストに追加されます。 詳細については、「Microsoft が推奨するドライバー ブロックの規則」を参照してください。 Hypervisor-Protected Code Integrity (HVCI) 対応デバイスと S モードの Windows 10 には、これらの規則が既定で適用されます。
セキュア コーディング リソースを確認する
セキュリティ チェックリスト項目 19: これらのリソースを確認して、ドライバー開発者に適用できる、安全なコーディングのベスト プラクティスについて理解を深める。
ドライバーのセキュリティの詳細を紹介するリソース
安全なカーネル モード ドライバーのコーディング ガイドライン
安全なコーディングに関係する組織
Carnegie Mellon University SEI CERT
Carnegie Mellon University SEI CERT C Coding Standard: Rules for Developing Safe, Reliable, and Secure Systems (2016 年版)
MITRE - Weaknesses Addressed by the CERT C Secure Coding Standard
Building Security In Maturity Model (BSIMM) - https://www.bsimm.com/
SAFECode - https://safecode.org/
OSR
OSR は、ドライバー開発のトレーニングとコンサルティング サービスを提供しています。 OSR ニュースレターのこれらの記事では、ドライバーのセキュリティの問題を大きく取り上げています。
Names, Security Descriptors and Device Classes - Making Device Objects Accessible… and SAFE
You've Gotta Use Protection -- Inside Driver & Device Security
Locking Down Drivers - A Survey of Techniques
Meltdown and Spectre: What about drivers?
ケース スタディ
書籍
24 deadly sins of software security : programming flaws and how to fix them (Michael Howard、David LeBlanc、John Viega 共著)
The art of software security assessment : identifying and preventing software vulnerabilities (Mark Dowd、John McDonald、Justin Schuh 共著)
Writing Secure Software Second Edition (Michael Howard および David LeBlanc 共著)
The Art of Software Security Assessment: Identifying and Preventing Software Vulnerabilities (Mark Dowd および John McDonald 共著)
Secure Coding in C and C++ (SEI Series in Software Engineering) 2nd Edition (Robert C. Seacord 著)
Programming the Microsoft Windows Driver Model (2nd Edition) (Walter Oney 著)
Developing Drivers with the Windows Driver Foundation (Developer Reference) (Penny Orwick および Guy Smith 共著)
トレーニング
Windows ドライバーのクラスルーム トレーニングが、次のようなベンダーから提供されています。
安全なコーディングのオンライン トレーニングは、さまざまなソースから入手できます。 たとえば、Coursera からは次のコースが提供されています。
SAFECode からは、無料のトレーニングも提供されています。
プロフェッショナル認定資格
CERT からは、安全なコーディングのプロフェッショナル認定資格が提供されています。
重要なポイントのまとめ
ドライバーのセキュリティは、多くの要素を含む複雑な作業ですが、考慮すべきいくつかの重要なポイントを次に示します。
ドライバーが Windows カーネルに存在し、カーネルでの実行時に問題が発生すると、オペレーティング システム全体が危険にさらされます。 このため、ドライバーのセキュリティとそれを念頭に置いた設計に細心の注意を払う必要があります。
最小特権の原則を適用します。
a. 厳密な SDDL 文字列を使用してドライバーへのアクセスを制限する
b. 個々の IOCTL をさらに制限する
脅威モデルを作成して攻撃ベクトルを特定し、さらに何かを制限できるかどうかを検討します。
ユーザーモードから渡される埋め込みポインターに注意します。 それらをプローブし、try except 内でアクセスする必要があります。また、ToCToU (Time of Check Time of Use) の問題に発展しやすいため、バッファーの値をキャプチャして比較する必要があります。
確信がない場合は、IOCTL バッファリング メソッドとして METHOD_BUFFERED を使用します。
コード スキャン ユーティリティを使用して、既知のコードの脆弱性を探し、特定された問題を修復します。
知識豊富なコード レビュー担当者を当たって、自分が見逃した可能性のある問題を探します。
ドライバー検証ツールを使用し、めったに発生しないケースを含む複数の入力でドライバーをテストします。