Skip to content

Stack canaries (kernel stack protector)

Local buffer'lar ile saved return address arasına yerleştirilen ve return'de kontrol edilen rastgele bir guard değeri; userland ve kernel'de linear stack buffer overflow'ları tespit etmek için.

Mechanism

Invariant: return address'in önünde, herhangi bir linear overflow'un mecburen ezeceği gizli bir değer durur

Bir stack canary (namıdiğer stack cookie), compiler tarafından stack'e, local buffer'lar ile saved return address (ve saved frame pointer) arasına yerleştirilen rastgele bir integer'dır. LWN'nin tarif ettiği gibi canary "function return etmeden önce kontrol edilir; değişmişse program abort eder."

Invariant şu: saved return address'e contiguous (linear) bir stack write üzerinden ulaşıp onu overwrite etmek için — klasik stack buffer overflow — saldırgan önce canary'yi overwrite etmek zorundadır. "Canary'nin değeri saldırgan tarafından bilinmediği için yerine konamaz," dolayısıyla corruption function epilogue'unda tespit edilir ve poisoned return address asla kullanılmadan program abort edilir (kernel durumunda bir kernel panic).

Teknik, StackGuard / ProPolice'ten türeyen SSP (Stack Smashing Protector) olarak da bilinir. Bazı implementation'lar saf random yerine 0x00 (NUL), 0x0a (LF), 0xff gibi byte'lar içeren bir terminator canary kullanır; böylece strcpy/gets gibi string-tabanlı overflow primitive'leri canary boyunca temiz kopyalayamaz.

Bu, linear overflow'lar için bir tespit-et-ve-abort-et savunmasıdır; bir control-flow hijack'ini bir denial-of-service'e (crash) çevirir. Non-contiguous write'ları, canary'yi geçmeyen write'ları ya da canary değerinin kendisinin info-leak'lerini durdurmaz.

Walkthrough

Compiler prologue/epilogue instrumentation'ı emit eder:

// conceptual prologue
canary = __stack_chk_guard;   // load secret (per-boot/per-process)
*(frame_canary_slot) = canary;

// ... function body, including the vulnerable local buffer ...

// conceptual epilogue
if (*(frame_canary_slot) != __stack_chk_guard)
    __stack_chk_fail();        // abort: SIGABRT / kernel panic

x86-64 glibc'de master canary thread pointer'dan (%fs:0x28) okunur; emit edilen prologue/epilogue'da bu somut olarak görünür:

mov    %fs:0x28,%rax        ; load master canary
mov    %rax,-0x8(%rbp)      ; store into frame, just below ret addr
...
mov    -0x8(%rbp),%rax      ; reload frame canary
xor    %fs:0x28,%rax        ; compare to master (0 if unchanged)
je     <ok>
call   __stack_chk_fail     ; mismatch -> abort

Coverage seçilebilir — GCC/Clang flag'leri üzerinden (ve kernel bunları CONFIG'de aynalar):

  • -fstack-protector (CONFIG_CC_STACKPROTECTOR_REGULAR): 8-byte-veya-daha-büyük bir character array içeren function'ları korur. Bir x86 kernel build'inde, function'ların ~%3'ü, ~%0.3 boyut artışı.
  • -fstack-protector-strong (CONFIG_CC_STACKPROTECTOR_STRONG): daha geniş — herhangi bir local array (struct/union içindeki array'ler dahil her tür/uzunluk), bir local'in adresini alan ya da local register variable kullanan function'lar. Kernel function'larının ~%20'si, ~%2 boyut artışı — tipik hardened default.
  • -fstack-protector-all: her function; yüksek overhead, nadiren kullanılır.

Windows/MSVC'de eşdeğeri /GS'tir (bkz. gs-stack-cookie). Linux kernel'i CONFIG_STACKPROTECTOR / CONFIG_STACKPROTECTOR_STRONG'u açar; guard değeri __stack_chk_guard'da yaşar ve başarısızlıklar __stack_chk_fail'a yönlenir.

Bir saldırgan onu nasıl yener (ve canary'lerin neyi kapsamadığı)
  • Canary'yi leak et (stack-canary-leak, format-string-canary-leak): sırrı disclose et, sonra check geçsin diye overflow'a doğru değeri dahil et.
  • Brute force — değerin re-randomization olmadan fork başına yeniden türetildiği durumda (byte-by-byte-canary-brute-force): forking bir server'a karşı bir seferde bir byte tahmin et.
  • Canary'den önce overwrite structure'ları: buffer ile canary arasında duran hassas local'leri ya da bir saved frame pointer'ı boz, veya canary'nin tamamen üstünden atlayan bir write primitive'i kullan (structures-before-canary-overwrite).
  • Master/TLS canary overwrite: per-frame canary'nin karşılaştırıldığı master kopyayı boz (master-tls-canary-overwrite).

Detection

Korumanın derlemeye dahil edildiğini doğrula ve abort sinyaline dikkat et

  • Build doğrulaması (userland): binary'lerin __stack_chk_fail / __stack_chk_guard'a referans verdiğini (örn. symbol/relocation incelemesi) ya da Windows'ta GS instrumentation'ı taşıdığını kontrol et. checksec, hardening-check gibi hardening checker'lar ve distro annobin/abi tooling'i, stack-protector'ın kullanılıp kullanılmadığını ve hangi coverage'da olduğunu raporlar.
  • Kernel doğrulaması: çalışan kernel config'inde (/proc/config.gz veya /boot/config-*) CONFIG_STACKPROTECTOR_STRONG=y olduğunu doğrula.
  • Runtime sinyali: tetiklenen bir canary belirgin bir abort üretir — userland'de stderr'e *** stack smashing detected *** ve SIGABRT; kernel ise bir Kernel stack is corrupted/stack-protector panic'i emit eder. Network'e bakan bir servisten bunları yakalayan EDR/crash telemetry'si, sadece generic bir crash değil, güçlü bir exploitation-girişimi göstergesidir.
  • Core/coredump analizi: aynı function epilogue'unda tekrar eden abort'lar, bir overflow'un prob edildiğine işaret eder.

Mitigation

Etkinleştirme, hardening ve uyarılar

  • Geniş etkinleştir: userland'i -fstack-protector-strong ile build et (çoğu modern distro buna default'lar); kernel'ler için CONFIG_STACKPROTECTOR_STRONG=y ayarla; MSVC build'lerinde /GS'i etkinleştir.
  • Birleştir, yalnızca buna güvenme: canary'ler yalnızca return-address taşıyan frame'lerin linear overflow'larını ve yalnızca function return'ünde tespit eder. Shadow stack'ler / backward-edge CFI, safestack, ASLR, guard page'ler, FORTIFY_SOURCE ve (mevcutsa) corruption'ı epilogue zamanı yerine spatially yakalayan hardware tagging (memory-tagging-extension) ile katmanla. AArch64'te pointer-authenticated return address'ler (arm-pointer-authentication) return-address korumasında canary'yi tamamlar ya da onun yerini alır.
  • Leak/brute-force maruziyetini azalt: guard'ın high-entropy olduğundan ve uygun şekilde re-randomize edildiğinden emin ol (kernel için per-boot, userland için per-exec); info-leak'lere karşı harden et (stack-canary-leak), çünkü leak'lenmiş bir canary check'i tamamen bypass eder.
  • Bilinen bypass uyarıları: Walkthrough'a bak — leak, fork'lar arası brute force, canary'nin ötesine write ve aradaki local'lerin corruption'ı, hepsi canary'leri "enabled" bırakırken yener. Canary'yi sınır değil, bir katman olarak gör.

References