Condividi tramite


Problemi di sicurezza per i driver di rete

Per una discussione generale sulla scrittura di driver sicuri, vedere Creazione di driver reliable Kernel-Mode.

Oltre alle procedure di codifica sicura e alle indicazioni generali sul driver di dispositivo, i driver di rete devono eseguire le operazioni seguenti per migliorare la sicurezza:

  • Tutti i driver di rete devono convalidare i valori letti dal Registro di sistema. In particolare, il chiamante di NdisReadConfiguration o NdisReadNetworkAddress non deve effettuare ipotesi sui valori letti dal Registro di sistema e deve convalidare ogni valore del Registro di sistema letto. Se il chiamante di NdisReadConfiguration determina che un valore non è limitato, deve usare invece un valore predefinito. Se il chiamante NdisReadNetworkAddress determina che un valore non è limitato, deve usare l'indirizzo MAC (Permanent Medium Access Control) o un indirizzo predefinito.

Problemi specifici dell'OID

Linee guida per la sicurezza di Query OID

La maggior parte degli OID query può essere rilasciata da qualsiasi applicazione usermode nel sistema. Seguire queste linee guida specifiche per gli ID query.

  1. Convalidare sempre le dimensioni del buffer è sufficiente per l'output. Qualsiasi gestore OID di query senza un controllo delle dimensioni del buffer di output ha un bug di sicurezza.

    if (oid->DATA.QUERY_INFORMATION.InformationBufferLength < sizeof(ULONG)) {
        oid->DATA.QUERY_INFORMATION.BytesNeeded = sizeof(ULONG);
        return NDIS_STATUS_INVALID_LENGTH;
    }
    
  2. Scrivere sempre un valore corretto e minimo in Byte Scritti. È un flag rosso da assegnare oid->BytesWritten = oid->InformationBufferLength come l'esempio seguente.

    // ALWAYS WRONG
    oid->DATA.QUERY_INFORMATION.BytesWritten = DATA.QUERY_INFORMATION.InformationBufferLength; 
    

    Il sistema operativo copia byte scritti in un'applicazione usermode. Se BytesScritto è maggiore del numero di byte effettivamente scritto dal driver, il sistema operativo potrebbe terminare la copia della memoria kernel non inizializzata in usermode, che sarebbe una vulnerabilità di divulgazione delle informazioni. Usare invece codice simile al seguente:

    oid->DATA.QUERY_INFORMATION.BytesWritten = sizeof(ULONG);
    
  3. Non leggere mai i valori dal buffer. In alcuni casi, il buffer di output di un OID viene mappato direttamente in un processo usermode ostile. Il processo ostile può modificare il buffer di output dopo averlo scritto. Ad esempio, il codice seguente può essere attaccato, perché un utente malintenzionato può modificare NumElements dopo la scrittura:

    output->NumElements = 4;
    for (i = 0 ; i < output->NumElements ; i++) {
        output->Element[i] = . . .;
    }
    

    Per evitare la lettura dal buffer, mantenere una copia locale. Ad esempio, per correggere l'esempio precedente, introdurre una nuova variabile stack:

    ULONG num = 4;
    output->NumElements = num;
    for (i = 0 ; i < num; i++) {
        output->Element[i] = . . .;
    }
    

    Con questo approccio, il ciclo for legge nuovamente dalla variabile num stack del driver e non dal relativo buffer di output. Il driver deve anche contrassegnare il buffer di output con la volatile parola chiave, per impedire al compilatore di annullare automaticamente questa correzione.

Impostare le linee guida per la sicurezza OID

La maggior parte degli OID set può essere rilasciata da un'applicazione usermode in esecuzione nei gruppi di sicurezza Administrators o System. Anche se queste applicazioni sono generalmente attendibili, il driver miniport non deve comunque consentire il danneggiamento della memoria o l'inserimento del codice kernel. Seguire queste regole specifiche per Set OIDs:

  1. Convalidare sempre l'input è abbastanza grande. Qualsiasi gestore del set OID senza un controllo delle dimensioni del buffer di input presenta una vulnerabilità di sicurezza.

    if (oid->DATA.SET_INFORMATION.InformationBufferLength < sizeof(ULONG)) {
        return NDIS_STATUS_INVALID_LENGTH;
    }
    
  2. Ogni volta che si convalida un OID con un offset incorporato, è necessario verificare che il buffer incorporato si trovi all'interno del payload OID. Ciò richiede diversi controlli. Ad esempio, OID_PM_ADD_WOL_PATTERN può fornire un modello incorporato, che deve essere controllato. La convalida corretta richiede il controllo:

    1. 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;
      }
      
    2. Pattern-PatternOffset + Pattern-PatternSize>> non sovraflow

      ULONG TotalSize = 0;
      if (!NT_SUCCESS(RtlUlongAdd(Pattern->PatternOffset, Pattern->PatternSize, &TotalSize) ||
          TotalSize > InformationBufferLength) 
      {
          return NDIS_STATUS_INVALID_LENGTH;
      }
      

      Questi due controlli possono essere combinati usando codice come l'esempio seguente:

      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;
      }
      
    3. InformationBuffer + Pattern-PatternOffset + Pattern-PatternLength>> non esegue il overflow

      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;
      }
      
    4. Pattern-PatternOffset + Pattern-PatternLength>>< = InformationBufferSize

      ULONG TotalSize = 0;
      if(!NT_SUCCESS(RtlUlongAdd(Pattern->PatternOffset, Pattern->PatternLength, &TotalSize) ||
          TotalSize > InformationBufferLength)) 
      {
          return NDIS_STATUS_INVALID_LENGTH;
      }
      

Linee guida per la sicurezza del metodo OID

Gli OID del metodo possono essere rilasciati da un'applicazione usermode in esecuzione nei gruppi di sicurezza Administrators o System. Sono una combinazione di set e query, quindi entrambi gli elenchi precedenti di linee guida si applicano anche agli ID metodo.

Altri problemi di sicurezza dei driver di rete

  • Molti driver miniport NDIS espongono un dispositivo di controllo usando NdisRegisterDeviceEx. Queste operazioni devono controllare i gestori IOCTL, con tutte le stesse regole di sicurezza di un driver WDM. Per altre informazioni, vedere Problemi di sicurezza per i codici di controllo I/O.

  • I driver miniport NDIS ben progettati non devono basarsi sulla chiamata in un particolare contesto di processo, né interagire molto con usermode (con IOCTLs & OID che sono l'eccezione). Si tratta di un flag rosso per visualizzare un miniport che ha aperto handle usermode, eseguito attese utentemode o memoria allocata rispetto alla quota utentemode. Tale codice deve essere analizzato.

  • La maggior parte dei driver miniport NDIS non deve essere coinvolta nell'analisi dei payload dei pacchetti. In alcuni casi, tuttavia, potrebbe essere necessario. In tal caso, questo codice deve essere controllato con attenzione, poiché il driver analizza i dati da un'origine non attendibile.

  • Come è standard quando si alloca la memoria in modalità kernel, i driver NDIS devono usare i meccanismi di pool NX appropriati Opt-In. In WDK 8 e versioni successive, la NdisAllocate* famiglia di funzioni viene esplicitata correttamente.