Uninitialized stack variable infoleak¶
Bir kernel fonksiyonu, stack üzerindeki bir local'i (ya da struct padding'ini) initialize etmeden userspace'e kopyalar ve önceki bir call frame'in bıraktığı artık byte'ları — çoğu zaman kernel pointer'larını ya da bir stack canary'sini — açığa çıkarır.
Mechanism¶
Kernel stack'i çağrılar arasında yeniden kullanılır. Bir syscall handler bir local değişken ya da struct tanımlayıp onu her byte'ı yazmayan bir yol boyunca copy_to_user()/put_user() ile userspace'e kopyaladığında, yazılmamış byte'lar hâlâ önceki fonksiyonun orada bıraktığı her ne ise onu tutar.
Note
Aynı invariant'ın iki çeşidi — "tam olarak initialize etmediğin belleği asla dışarı kopyalama":
- Yazılmamış local'ler / error path'leri: bir alan yalnızca başarı durumunda doldurulur, ama struct koşulsuz olarak (ya da erken bir
return/goto'da) dışarı kopyalanır. - Struct padding boşlukları: compiler alanlar arasına alignment padding ekler.
memcpy/alan-alan kopyalar boşluğa hiç dokunmaz, dolayısıyla her isimli alan set edilse bile boşluk eski stack byte'larını leak'ler.
Açığa çıkan veri "bedava"dır, ama değeri önceki frame'in ne bıraktığına bağlıdır. WOOT'20 sonucu (Cho et al.) şudur: bu düşük riskli değildir: targeted stack-spraying ile saldırgan önce derin call chain'i tam olarak vulnerable fonksiyonun daha sonra initialize edilmemiş okuduğu stack slot'una bir kernel function pointer'ı ya da kernel-stack pointer'ı bırakan bir syscall çağırır — bir "çöp byte" leak'ini güvenilir bir KASLR-bozan kernel-pointer leak'ine çevirir.
Kanonik örnekler: VMware DRM vmw_gb_surface_define_ioctl (initialize edilmemiş backup_handle), Xen blkif make_response padding'i (XSA-216) ve birçok compat-ioctl handler'ı (bkz. cciss_ioctl32_passthru, atyfb ioctl padding).
Walkthrough¶
Minimal bir vulnerable handler ve saldırgan iş akışı:
struct leak { u32 a; /* 4 bytes padding here */ u64 b; };
long vuln_ioctl(unsigned long uarg) {
struct leak out; // NOT zeroed
out.a = 0x1337; // 'a' set; padding + 'b' may stay stale
return copy_to_user((void __user *)uarg, &out, sizeof(out));
}
-
Stack'i hazırla (targeted spray). Call chain'i aynı stack bölgesiyle örtüşecek kadar derin olan ve oraya bir kernel pointer bırakan ilgisiz bir syscall çağır. "Priming" syscall'unun seçimi, leak'i rastgele yerine deterministik yapan şeydir.
-
Leak'i tetikle. Aynı thread'den derhal vulnerable handler'ı çağır (slot'u yeniden ezecek araya giren derin bir çağrı olmadan):
struct leak out;
ioctl(fd, VULN_CMD, &out);
printf("padding/uninit = %#llx\n", out.b); // stale kernel pointer
- KASLR'ı boz. Leak'lenen değeri bir kernel sembolünün bilinen low bit'lerine maskele ve KASLR slide'ını geri elde etmek için sembolün statik offset'ini çıkar:
Expected output
Aynı desen, leak'lenen slot bir saved-canary konumuyla alias olduğunda bir stack canary verir ve downstream'de stack-smash exploitation'ı mümkün kılar.
Detection¶
- Compile-time:
-Wuninitialized,-Wmaybe-uninitializedve kernel'in stack-init hardening'i (CONFIG_INIT_STACK_ALL_ZERO,CONFIG_INIT_STACK_ALL_PATTERN) local'leri zero/poison yapar, böylece leak'ler secret yerine zero/poison olarak yüzeye çıkar. - Dynamic: KMSAN (
CONFIG_KMSAN) initialize edilmemiş belleği izler ve poison'lanmış byte'ların userspace'e kopyalarını raporlar; syzbot onu sürekli çalıştırır. - Multi-variant execution (kMVX) ve stack-leak dedektörleri, yazılmamış boşluklarla dışarı kopyalanan struct'ları işaretler.
Mitigation¶
- Doldurmadan önce
memset(&out, 0, sizeof(out))yap, ya da designated-initializer= {0}/= {}kullan. CONFIG_INIT_STACK_ALL_ZERO=y(modern hardened build'lerde default-on) stack local'lerini otomatik sıfırlar ve padding dahil tüm sınıfı etkisizleştirir.CONFIG_GCC_PLUGIN_STRUCTLEAK/__user-copy padding-temizleyen helper'lar;kptr_restrictve KASLR, kalan herhangi bir leak'in değerini artırır.
References¶
- Exploiting Uses of Uninitialized Stack Variables in Linux Kernels to Leak Kernel Pointers — Cho et al., USENIX WOOT '20
- WOOT '20 paper PDF (sefcom.asu.edu)
- leak-kptr exploitation toolkit (sefcom)
- XSA-216 / CVE-2017-10911 — Xen blkif responses leak backend stack data
- drm/vmwgfx: Make sure backup_handle is always valid (uninitialized
backup_handleinvmw_gb_surface_define_ioctl)