네트워크 드라이버의 보안 문제
보안 드라이버 작성에 대한 일반적인 논의는 신뢰할 수 있는 Kernel-Mode 드라이버 만들기를 참조하세요.
안전한 코딩 방법 및 일반 디바이스 드라이버 지침을 따르는 것 외에도 네트워크 드라이버는 보안을 강화하기 위해 다음을 수행해야 합니다.
- 모든 네트워크 드라이버는 레지스트리에서 읽은 값의 유효성을 검사해야 합니다. 특히 NdisReadConfiguration 또는 NdisReadNetworkAddress 호출자는 레지스트리에서 읽은 값에 대한 가정을 해서는 안 되며 읽는 각 레지스트리 값의 유효성을 검사해야 합니다. NdisReadConfiguration의 호출자가 값이 범위를 벗어나는 것을 결정하는 경우 대신 기본값을 사용해야 합니다. NdisReadNetworkAddress 호출자가 값이 범위를 벗어나는 것으로 판단되면 MAC(영구 중간 액세스 제어) 주소 또는 기본 주소를 대신 사용해야 합니다.
OID 관련 문제
MiniportOidRequest 또는 MiniportCoOidRequest 함수의 미니포트 드라이버는 드라이버가 설정하도록 요청된 OID(개체 식별자) 값의 유효성을 검사해야 합니다. 드라이버가 설정할 값이 범위를 벗어나는 것으로 판단되면 설정 요청에 실패해야 합니다. 개체 식별자에 대한 자세한 내용은 Miniport 드라이버 정보 가져오기 및 설정 및 WMI에 대한 NDIS 지원을 참조하세요.
중간 드라이버의 MiniportOidRequest 함수가 기본 미니포트 드라이버에 설정된 작업을 전달하지 않는 경우 함수는 OID 값의 유효성을 검사해야 합니다. 자세한 내용은 중간 드라이버 쿼리 및 작업 설정을 참조하세요.
OID 보안 지침 쿼리
대부분의 쿼리 OID는 시스템의 모든 usermode 애플리케이션에서 발급할 수 있습니다. 쿼리 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의 출력 버퍼가 적대적인 usermode 프로세스에 직접 매핑됩니다. 적대적 프로세스는 출력 버퍼를 작성한 후 변경할 수 있습니다. 예를 들어 공격자가 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
를 키워드(keyword) 표시하여 컴파일러가 이 수정 사항을 자동으로 실행 취소하지 않도록 해야 합니다.
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를 사용하여 제어 디바이스를 노출합니다. 이렇게 하는 사용자는 WDM 드라이버와 동일한 모든 보안 규칙을 사용하여 IOCTL 처리기를 감사해야 합니다. 자세한 내용은 I/O 제어 코드에 대한 보안 문제를 참조하세요.
잘 설계된 NDIS 미니포트 드라이버는 특정 프로세스 컨텍스트에서 호출되는 것에 의존하거나 usermode와 매우 긴밀하게 상호 작용해서는 안 됩니다(IOCTL & OID는 예외임). usermode 핸들을 열거나, usermode 대기를 수행하거나, usermode 할당량에 대해 메모리를 할당하는 미니포트를 보는 것은 빨간색 플래그입니다. 해당 코드를 조사해야 합니다.
대부분의 NDIS 미니포트 드라이버는 패킷 페이로드 구문 분석에 관여해서는 안 됩니다. 그러나 경우에 따라 필요할 수 있습니다. 그렇다면 드라이버가 신뢰할 수 없는 원본의 데이터를 구문 분석하므로 이 코드를 매우 신중하게 감사해야 합니다.
커널 모드 메모리를 할당할 때 표준과 마찬가지로 NDIS 드라이버는 적절한 NX 풀 Opt-In 메커니즘을 사용해야 합니다. WDK 8 이상
NdisAllocate*
에서는 함수 제품군이 올바르게 옵트인됩니다.