Skip to content

cciss_ioctl32_passthru stack infoleak (CVE-2013-2147)

cciss driver'ındaki bir compat ioctl handler'ı initialize edilmemiş bir on-stack struct döndürüyor ve bir padding hole üzerinden stale kernel-stack byte'larını leak ediyor.

Mechanism

drivers/block/cciss.c içinde (HP/Compaq Smart Array "cciss" driver), CCISS_PASSTHRU32 için olan compat handler cciss_ioctl32_passthru(), kernel-stack üzerinde lokal bir IOCTL_Command_struct arg64; tanımlıyor. arg64copy_from_user() ile alan alan dolduruyor ama hiçbir zaman sıfırlamıyor.

Note

IOCTL_Command_struct, ->buf_size'ın hemen ardından hiç yazılmayan bir padding hole'a sahip (~2 byte); bu yüzden stale kernel-stack byte'ları hayatta kalıyor ve userspace'e geri kopyalanıyor — bir kernel stack infoleak. İkincil bir leak: eğer herhangi bir copy_from_user() kısmen başarısız olursa, kopyalanmayan o alanlar da leak olur. cpqarray kardeşi (drivers/block/cpqarray.c, ida_locked_ioctl()) aynı CVE kapsamındaydı.

Kavramsal olarak arg64'ün on-stack layout'u (alan isimleri gerçek, offset'ler/boyutlar derleyici ve arch'a göre değişir — burada yalnızca padding hole'un neden doğduğunu göstermek için):

  stack frame: IOCTL_Command_struct arg64
  +---------------------------------------------------+
  | LUN_info        |  <- copy_from_user ile dolduruldu
  | Request         |  <- copy_from_user ile dolduruldu
  | error_info      |  <- copy_from_user ile dolduruldu
  | buf_size        |  <- copy_from_user ile dolduruldu
  | [padding hole]  |  <- HİÇ yazılmadı  ===> stale stack byte'ları
  | buf (pointer)   |  <- ayrıca set edildi
  +---------------------------------------------------+
            |
            |  copy_to_user(arg64) tüm struct'ı geri yazar
            v
  userspace  <=== padding hole içindeki eski kernel-stack içeriği sızar

Hole, derleyicinin buf_size'tan sonra bir sonraki alanı doğal hizalamaya (alignment) getirmek için bıraktığı boşluktan kaynaklanır; hiçbir alan-dolduran satır ona dokunmadığı için önceki frame'lerden kalan byte'lar hayatta kalır.

Fix (commit 58f09e00ae09, "cciss: info leak in cciss_ioctl32_passthru()", Dan Carpenter) basitçe bir memset ekliyor:

@@ static int cciss_ioctl32_passthru(struct block_device *bdev, fmode_t mode, ...
        int err;
        u32 cp;

+       memset(&arg64, 0, sizeof(arg64));
        err = 0;
        err |=
            copy_from_user(&arg64.LUN_info, &arg32->LUN_info,

Walkthrough

Bu CVE için public bir PoC yok; reprodüksiyon kavramsal, yalnızca advisory'deki gerçek device path'leri ve ioctl isimlerini kullanıyor.

  1. 32-bit (compat) bir process, /dev/cciss/* altındaki bir cciss device node'unu açar.
  2. cciss_ioctl32_passthru()'ya yönlendirilen compat ioctl CCISS_PASSTHRU32'yi issue eder.
  3. arg64 initialize edilmeden allocate edilir; buf_size sonrasındaki padding hiç yazılmaz.
  4. Native struct userspace'e geri döndürülür ve stale kernel-stack byte'larını ifşa eder. (cpqarray kardeşi: device /dev/ida/*, komut IDAGETPCIINFO.)

Commit mesajı (verbatim): "The arg64 struct has a hole after ->buf_size which isn't cleared. Or if any of the calls to copy_from_user() fail then that would cause an information leak as well. This was assigned CVE-2013-2147."

Detection

Bu, klasik uninitialized-stack / padding-hole leak sınıfı; kernel stack info leak'lerini tespit eden static analyzer'lar ve akademik araçlar tam olarak bu pattern'i flag eder (yazılmamış hole'larla birlikte userspace'e kopyalanan struct).

Mitigation

Commit 58f09e00...'ı uygula (stable 3.2.52 vb. içinde). Etkilenen: RHEL 5 ve MRG 2; RHEL 6 etkilenmiyor. /dev/cciss/* veya /dev/ida/* üzerinde lokal read/ioctl erişimi gerekiyor, dolayısıyla node permission'larını kısıtlamak bunu mitigate eder. Etki yalnızca info-disclosure.

References