セキュリティで保護されたドライバーの記述に関する一般的な説明については、「 Reliable Kernel-Mode ドライバーの作成」を参照してください。
安全なコーディングプラクティスと一般的なデバイス ドライバーガイダンスに従う以外に、ネットワーク ドライバーはセキュリティを強化するために次のことを行う必要があります。
- すべてのネットワーク ドライバーは、レジストリから読み取った値を検証する必要があります。 具体的には、 NdisReadConfiguration または NdisReadNetworkAddress の呼び出し元は、レジストリから読み取られた値に関する仮定を行う必要はなく、読み取る各レジストリ値を検証する必要があります。 NdisReadConfiguration の呼び出し元が値が範囲外であると判断した場合は、代わりに既定値を使用する必要があります。 NdisReadNetworkAddress の呼び出し元が値が範囲外であると判断した場合は、代わりに永続的なメディア アクセス制御 (MAC) アドレスまたは既定のアドレスを使用する必要があります。
OID 固有の問題
ミニポート ドライバーは、 そのミニポートOidRequest または ミニポートCoOidRequest 関数で、設定するドライバーが要求されている任意のオブジェクト識別子 (OID) 値を検証する必要があります。 ドライバーが設定する値が範囲外であると判断した場合は、set 要求を失敗させる必要があります。 オブジェクト識別子の詳細については、 取得と設定ミニポート ドライバー情報と WMI の NDIS サポートを参照してください。
中間ドライバーの ミニポートOidRequest 関数が基になるミニポート ドライバーに設定された操作を渡さない場合、関数は 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 は初期化されていないカーネル メモリを usermode にコピーする可能性があります。これは情報漏えいの脆弱性になります。 代わりに、次のようなコードを使用します。
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 セキュリティ ガイドラインを設定する
ほとんどの Set OID は、Administrators または System セキュリティ グループで実行されているユーザーモード アプリケーションによって発行できます。 これらは一般的に信頼されたアプリケーションですが、ミニポート ドライバーはメモリの破損やカーネル コードの挿入を許可してはなりません。 セット 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->パターンオフセット + Pattern->パターン長さ <= 情報バッファサイズ
ULONG TotalSize = 0; if(!NT_SUCCESS(RtlUlongAdd(Pattern->PatternOffset, Pattern->PatternLength, &TotalSize) || TotalSize > InformationBufferLength)) { return NDIS_STATUS_INVALID_LENGTH; }
メソッド OID のセキュリティ ガイドライン
メソッド OID は、Administrators または System セキュリティ グループで実行されているユーザーモード アプリケーションによって発行できます。 これらはセットとクエリの組み合わせであるため、前述のガイダンスの両方がメソッド OID にも適用されます。
ネットワーク ドライバーのセキュリティに関するその他の問題
NDIS ミニポート ドライバーの多くは、NdisRegisterDeviceEx を使用してコントロール デバイスを公開します。 これを行う場合は、WDM ドライバーと同じセキュリティ規則をすべて使用して、IOCTL ハンドラーを監査する必要があります。 詳細については、「 I/O コントロール コードのセキュリティの問題」を参照してください。
適切に設計された NDIS ミニポート ドライバーは、特定のプロセス コンテキストで呼び出されることに依存したり、ユーザー モードと非常に密接に対話したりしないでください (IOCTL と OID は例外です)。 ユーザーモードのハンドルを開いたり、ユーザーモード待機を行ったり、ユーザーモードのクォータに対してメモリを割り当てたりするミニポートは、警告のサインになります。 そのコードを調査する必要があります。
ほとんどの NDIS ミニポート ドライバーは、パケット ペイロードの解析に関与しないでください。 ただし、必要な場合もあります。 その場合は、ドライバーが信頼されていないソースからデータを解析するため、このコードは非常に慎重に監査する必要があります。
カーネル モード メモリを割り当てる場合の標準と同様に、NDIS ドライバーは適切な NX プール Opt-In メカニズムを使用する必要があります。 WDK 8 以降では、
NdisAllocate*機能ファミリが適切にオプトインされます。