Bagikan melalui


Masalah Keamanan untuk Driver Jaringan

Untuk diskusi umum tentang menulis driver yang aman, lihat Membuat Driver Andal Kernel-Mode.

Selain mengikuti praktik pengkodean yang aman dan panduan driver perangkat umum, driver jaringan harus melakukan hal berikut untuk meningkatkan keamanan:

  • Semua driver jaringan harus memvalidasi nilai yang dibaca dari registri. Secara khusus, pemanggil NdisReadConfiguration atau NdisReadNetworkAddress tidak boleh membuat asumsi tentang nilai yang dibaca dari registri dan harus memvalidasi setiap nilai registri yang dibacanya. Jika pemanggil NdisReadConfiguration menentukan bahwa nilai berada di luar batas, nilai tersebut harus menggunakan nilai default sebagai gantinya. Jika pemanggil NdisReadNetworkAddress menentukan bahwa nilai berada di luar batas, itu harus menggunakan alamat kontrol akses menengah (MAC) permanen atau alamat default sebagai gantinya.

Masalah khusus OID

Pedoman Keamanan untuk Kueri OID

Sebagian besar OID Kueri dapat dikeluarkan oleh aplikasi usermode apa pun pada sistem. Ikuti panduan khusus berikut untuk Kueri OID.

  1. Selalu validasi ukuran buffer yang cukup besar untuk output. Setiap handler OID kueri yang tidak memiliki pemeriksaan ukuran buffer output memiliki bug keamanan.

    if (oid->DATA.QUERY_INFORMATION.InformationBufferLength < sizeof(ULONG)) {
        oid->DATA.QUERY_INFORMATION.BytesNeeded = sizeof(ULONG);
        return NDIS_STATUS_INVALID_LENGTH;
    }
    
  2. Selalu tulis nilai yang benar dan minimal ke BytesWritten. Ini adalah tanda bahaya untuk menugaskan oid->BytesWritten = oid->InformationBufferLength seperti yang dilakukan contoh berikut.

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

    OS akan menyalin byte BytesWritten kembali ke aplikasi usermode. Jika BytesWritten lebih besar dari jumlah byte yang benar-benar ditulis driver, maka OS mungkin akhirnya menyalin kembali memori kernel yang tidak diinisialisasi ke usermode, yang akan menjadi kerentanan pengungkapan informasi. Sebagai gantinya, gunakan kode yang mirip dengan ini:

    oid->DATA.QUERY_INFORMATION.BytesWritten = sizeof(ULONG);
    
  3. Jangan pernah membaca kembali nilai dari buffer. Dalam beberapa kasus, buffer output OID langsung dipetakan ke dalam proses mode pengguna yang berbahaya. Proses jahat dapat mengubah buffer output Anda setelah Anda menulis ke buffer tersebut. Misalnya, kode di bawah ini dapat diserang, karena penyerang dapat mengubah NumElements setelah ditulis:

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

    Untuk menghindari pembacaan kembali dari buffer, simpan salinan lokal. Misalnya, untuk memperbaiki contoh di atas, perkenalkan variabel tumpukan baru:

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

    Dengan pendekatan ini, for loop membaca ulang dari variabel num tumpukan driver dan tidak dari buffer keluarannya. Driver juga harus menandai buffer output dengan volatile kata kunci, untuk mencegah pengkompilasi membatalkan perbaikan ini secara diam-diam.

Menetapkan pedoman keamanan OID

Sebagian besar OID Set dapat dikeluarkan oleh aplikasi usermode yang berjalan di Administrator atau Grup keamanan sistem. Meskipun ini umumnya adalah aplikasi tepercaya, driver miniport masih tidak boleh mengizinkan kerusakan memori atau injeksi kode kernel. Ikuti aturan khusus ini untuk Mengatur OID:

  1. Selalu pastikan input cukup besar. Setiap handler set OID tanpa pemeriksaan ukuran buffer input memiliki kerentanan keamanan.

    if (oid->DATA.SET_INFORMATION.InformationBufferLength < sizeof(ULONG)) {
        return NDIS_STATUS_INVALID_LENGTH;
    }
    
  2. Setiap kali memvalidasi OID dengan offset yang disematkan, Anda harus memvalidasi bahwa buffer yang disematkan berada dalam payload OID. Ini membutuhkan beberapa pemeriksaan. Misalnya, OID_PM_ADD_WOL_PATTERN dapat memberikan pola yang disematkan, yang perlu diperiksa. Validasi yang benar memerlukan pemeriksaan:

    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 tidak melampaui batas

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

      Kedua pemeriksaan ini dapat digabungkan menggunakan kode seperti contoh berikut:

      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 tidak meluap

      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;
      }
      

Panduan keamanan metode OID

OID metode dapat dikeluarkan oleh aplikasi moda pengguna yang berjalan di kelompok Administrator atau kelompok keamanan sistem. Mereka adalah kombinasi dari Set dan Kueri, maka kedua daftar panduan yang disebutkan sebelumnya juga berlaku untuk Metode OID.

Masalah keamanan driver jaringan lainnya

  • Banyak driver miniport NDIS mengekspos perangkat kontrol dengan menggunakan NdisRegisterDeviceEx. Mereka yang melakukan ini harus mengaudit handler IOCTL mereka, dengan semua aturan keamanan yang sama dengan driver WDM. Untuk informasi selengkapnya, lihat Masalah Keamanan untuk Kode Kontrol I/O.

  • Driver miniport NDIS yang dirancang dengan baik tidak boleh tergantung pada pemanggilan dalam konteks proses tertentu, atau berinteraksi terlalu dekat dengan mode pengguna (dengan IOCTL dan OID yang menjadi pengecualian). Ini akan menjadi tanda peringatan untuk melihat miniport yang membuka handle mode pengguna, menunggu di mode pengguna, atau mengalokasikan memori terhadap kuota mode pengguna. Kode itu harus diselidiki.

  • Sebagian besar driver miniport NDIS seharusnya tidak terlibat dalam memilah payload paket. Namun, dalam beberapa kasus, mungkin perlu. Jika demikian, kode ini harus diaudit dengan sangat hati-hati, karena driver mengurai data dari sumber yang tidak tepercaya.

  • Seperti standar saat mengalokasikan memori mode kernel, driver NDIS harus menggunakan mekanisme NX Pool Opt-In yang sesuai. Di WDK 8 dan yang lebih baru, keluarga fungsi NdisAllocate* diaktifkan dengan benar.