ネットワーク ドライバーのセキュリティの問題
セキュリティ保護されたドライバーの記述に関する一般的な説明については、「信頼できるカーネル モード ドライバーの作成」を参照してください。
安全なコーディングプラクティスと一般的なデバイス ドライバーガイダンスに従う以外に、ネットワーク ドライバーはセキュリティを強化するために次のことを行う必要があります。
- すべてのネットワーク ドライバーは、レジストリから読み取った値を検証する必要があります。 具体的には、NdisReadConfiguration または NdisReadNetworkAddress の呼び出し元は、レジストリから読み取られた値に関して憶測をせず、読み取った各レジストリ値を検証する必要があります。 NdisReadConfiguration の呼び出し元が値が範囲外であると判断した場合は、代わりに既定値を使用する必要があります。 NdisReadNetworkAddress の呼び出し元が値が範囲外であると判断した場合は、代わりに永続的なメディア アクセス制御 (MAC) アドレスまたは既定のアドレスを使用する必要があります。
OID 固有の問題
ミニポート ドライバーは、その MiniportOidRequest または MiniportCoOidRequest 関数で、ドライバーが設定するよう要求されている任意のオブジェクト識別子 (OID) 値を検証する必要があります。 ドライバーが、設定する値が範囲外であると判断した場合は、セット要求を失敗させる必要があります。 オブジェクト識別子の詳細については、「ミニポート ドライバー情報の取得と設定」および「WMI の NDIS サポート」を参照してください。
中間ドライバーの MiniportOidRequest 関数が下位ミニポート ドライバーに設定操作を渡さない場合、関数は OID 値を検証する必要があります。 詳細については、「中間ドライバー クエリと設定操作」を参照してください。
クエリ OID セキュリティ ガイドライン
ほとんどのクエリ OID は、システム上の任意のユーザー モード アプリケーションによって発行できます。 クエリ OID については、次の特定のガイドラインに従ってください。
バッファーのサイズが出力に十分な大きさであることを必ず検証します。 出力バッファー サイズがチェックされていないクエリ OID ハンドラーには、セキュリティのバグがあります。
if (oid->DATA.QUERY_INFORMATION.InformationBufferLength < sizeof(ULONG)) { oid->DATA.QUERY_INFORMATION.BytesNeeded = sizeof(ULONG); return NDIS_STATUS_INVALID_LENGTH; }
BytesWritten には、常に正しい最小値を書き込みます。 次の例のように
oid->BytesWritten = oid->InformationBufferLength
を割り当てることは危険信号になります。// ALWAYS WRONG oid->DATA.QUERY_INFORMATION.BytesWritten = DATA.QUERY_INFORMATION.InformationBufferLength;
OS は BytesWritten バイトをユーザー モード アプリケーションにコピーします。 BytesWritten がドライバーが実際に書き込んだバイト数より大きい場合、OS は初期化されていないカーネル メモリをユーザーモードにコピーする可能性があり、これは情報漏えいの脆弱性になります。 代わりに、次のようなコードを使用します。
oid->DATA.QUERY_INFORMATION.BytesWritten = sizeof(ULONG);
決してバッファーから値を読み戻さないでください。 場合によっては、OID の出力バッファーが、悪意のあるユーザー モード プロセスに直接マッピングされます。 悪意のあるプロセスは、出力バッファーに書き込んだ後で変更できます。 たとえば、次のコードは、記述後に攻撃者が NumElements を変更できるため、攻撃される可能性があります。
output->NumElements = 4; for (i = 0 ; i < output->NumElements ; i++) { output->Element[i] = . . .; }
バッファーからの読み戻しを回避するには、ローカル コピーを保持します。 たとえば、上記の例を修正するには、新しいスタック変数を導入します。
ULONG num = 4; output->NumElements = num; for (i = 0 ; i < num; i++) { output->Element[i] = . . .; }
この方法では、for ループは、出力バッファーからではなく、ドライバーのスタック変数
num
から読み戻します。 また、コンパイラがこの修正プログラムをサイレントに元に戻さないように、ドライバーは出力バッファーをvolatile
キーワードでマークする必要があります。
セット OID セキュリティ ガイドライン
ほとんどのセット OID は、管理者またはシステム セキュリティ グループで実行されているユーザーモード アプリケーションによって発行できます。 これらは一般的に信頼されたアプリケーションですが、ミニポート ドライバーはメモリの破損やカーネル コードのインジェクションを許可してはなりません。 セット OID の以下の特定の規則に従います。
入力が十分な大きさであることを必ず検証します。 入力バッファー サイズチェックを持たない OID セット ハンドラーには、セキュリティの脆弱性があります。
if (oid->DATA.SET_INFORMATION.InformationBufferLength < sizeof(ULONG)) { return NDIS_STATUS_INVALID_LENGTH; }
埋め込みオフセットを使用して OID を検証するときは常に、埋め込みバッファーが OID ペイロード内にあることを検証する必要があります。 これには、いくつかのチェックが必要です。 たとえば、OID_PM_ADD_WOL_PATTERNは、チェックする必要がある埋め込みパターンを提供できます。 正しい検証には以下のチェックが必要です。
InformationBufferSize >= sizeof(NDIS_PM_PACKET_PATTERN)
PmPattern = (PNDIS_PM_PACKET_PATTERN) InformationBuffer; if (InformationBufferLength < sizeof(NDIS_PM_PACKET_PATTERN)) { Status = NDIS_STATUS_BUFFER_TOO_SHORT; *BytesNeeded = sizeof(NDIS_PM_PACKET_PATTERN); break; }
Pattern->PatternOffset + Pattern->PatternSize がオーバーフローしない
ULONG TotalSize = 0; if (!NT_SUCCESS(RtlUlongAdd(Pattern->PatternOffset, Pattern->PatternSize, &TotalSize) || TotalSize > InformationBufferLength) { return NDIS_STATUS_INVALID_LENGTH; }
これら 2 つのチェックは、次の例のようなコードを使用して組み合わせることができます。
ULONG TotalSize = 0; if (InformationBufferLength < sizeof(NDIS_PM_PACKET_PATTERN) || !NT_SUCCESS(RtlUlongAdd(Pattern->PatternSize, Pattern->PatternOffset, &TotalSize) || TotalSize > InformationBufferLength) { return NDIS_STATUS_INVALID_LENGTH; }
InformationBuffer + Pattern->PatternOffset + Pattern->PatternLength がオーバーフローしない
ULONG TotalSize = 0; if (!NT_SUCCESS(RtlUlongAdd(Pattern->PatternOffset, Pattern->PatternLength, &TotalSize) || (!NT_SUCCESS(RtlUlongAdd(TotalSize, InformationBuffer, &TotalSize) || TotalSize > InformationBufferLength) { return NDIS_STATUS_INVALID_LENGTH; }
Pattern->PatternOffset + Pattern->PatternLength <= InformationBufferSize
ULONG TotalSize = 0; if(!NT_SUCCESS(RtlUlongAdd(Pattern->PatternOffset, Pattern->PatternLength, &TotalSize) || TotalSize > InformationBufferLength)) { return NDIS_STATUS_INVALID_LENGTH; }
メソッド OID セキュリティ ガイドライン
ほとんどのメソッド OID は、管理者またはシステム セキュリティ グループで実行されているユーザーモード アプリケーションによって発行できます。 これらはセットとクエリの組み合わせであるため、前述のガイダンスの両方がメソッド OID にも適用されます。
ネットワーク ドライバーのセキュリティに関するその他の問題
NDIS ミニポート ドライバーの多くは、NdisRegisterDeviceEx を使用してコントロール デバイスを公開します。 これを行う場合は、WDM ドライバーと同じセキュリティ規則をすべて使用して、IOCTL ハンドラーを監査する必要があります。 詳細については、「I/O コントロール コードのセキュリティの問題」を参照してください。
適切に設計された NDIS ミニポート ドライバーは、特定のプロセス コンテキストで呼び出されることに依存したり、ユーザー モードと非常に密接に対話したりしてはいけません (IOCTL & OID は例外です)。 ユーザー モード ハンドルを開いた、ユーザー モード待機を実行した、またはユーザー モード クォータに対してメモリを割り当てたミニポートの表示は危険信号になります。 そのコードを調査する必要があります。
ほとんどの NDIS ミニポート ドライバーは、パケット ペイロードの解析に関与してはいけません。 ただし、これが必要な場合もあります。 その場合は、ドライバーが信頼されていないソースからデータを解析するため、このコードは非常に慎重に監査する必要があります。
カーネル モード メモリを割り当てる場合の標準と同様に、NDIS ドライバーは、適切な NX プール オプトイン メカニズムを使用する必要があります。 WDK 8 以降では、
NdisAllocate*
ファミリーの関数が適切にオプトインされます。