網路驅動程式的安全性問題
如需撰寫安全驅動程式的一般討論,請參閱 建立可靠 Kernel-Mode 驅動程式。
除了遵循安全編碼做法和一般裝置驅動程式指引之外,網路驅動程式應執行下列動作來增強安全性:
- 所有網路驅動程式都應該驗證它們從登錄讀取的值。 具體而言, NdisReadConfiguration 或 NdisReadNetworkAddress 的呼叫端不得對從登錄讀取的值進行任何假設,而且必須驗證它所讀取的每個登錄值。 如果 NdisReadConfiguration 的呼叫端判斷某個值超出界限,則應該改用預設值。 如果 NdisReadNetworkAddress 的呼叫端判斷值超出界限,它應該改用永久的媒體訪問控制 (MAC) 位址或默認位址。
OID 特定問題
迷你埠驅動程式在其 MiniportOidRequest 或 MiniportCoOidRequest 函式中,應該驗證驅動程式要求設定的任何對象識別碼 (OID) 值。 如果驅動程式判斷要設定的值超出範圍,它應該會失敗設定要求。 如需物件標識碼的詳細資訊,請參閱 取得和設定Miniport驅動程式資訊與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 位元組複製到 usermode 應用程式。 如果 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 都可以由系統管理員或系統安全組中執行的 usermode 應用程式發出。 雖然這些通常是受信任的應用程式,但迷你埠驅動程式仍然不得允許記憶體損毀或插入核心程序代碼。 請遵循設定 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; }
這兩個檢查可以使用下列範例的程式代碼來結合:
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 可由系統管理員或系統安全組中執行的 usermode 應用程式發出。 它們是集合和查詢的組合,因此上述兩個指引清單也適用於方法 OID。
其他網路驅動程式安全性問題
許多 NDIS 迷你埠驅動程式都會使用 NdisRegisterDeviceEx 公開控制裝置。 這樣做的處理程式必須稽核其 IOCTL 處理程式,且所有的安全性規則都與 WDM 驅動程式相同。 如需詳細資訊,請參閱 I/O 控制程式碼的安全性問題。
設計良好的 NDIS 迷你埠驅動程式不應該依賴在特定進程內容中呼叫,也不會與使用者模式 (非常緊密地互動,& OID 是例外狀況) 。 這會是紅色旗標,以查看開啟 usermode 句柄、執行使用者模式等候或針對 usermode 配額配置記憶體的迷你埠。 應該調查該程序代碼。
大部分的 NDIS 迷你埠驅動程式不應涉及剖析封包承載。 不過,在某些情況下,可能需要。 如果是,應該非常仔細地稽核此程式代碼,因為驅動程式正在剖析來自不受信任來源的數據。
如同配置內核模式記憶體時的標準,NDIS 驅動程式應該使用適當的 NX 集區 Opt-In 機制。 在 WDK 8 和更新版本中,函
NdisAllocate*
式系列已正確選擇加入。