Master/TLS Canary Overwrite¶
Bir stack overflow, thread'in TLS'te yerleşik "master" canary'sine ulaşacak kadar uzun olduğunda, saldırgan hem on-stack kopyayı hem de referans değeri overwrite edebilir, böylece forge edilmiş canary yine de
__stack_chk_fail'i geçer.
Mechanism¶
Note
Glibc'nin stack protector'ı bir equality invariant ile çalışır: local
buffer'ların üstüne yazılan değer (on-stack canary), Thread-Local Storage'da
tutulan per-thread bir referansla eşleşmelidir. x86-64'te bu referans,
tcbhead_t'nin stack_guard field'ıdır ve %fs:0x28 üzerinden okunur
(x86-32: %gs:0x14). Epilogue, stack kopyasını %fs:0x28 ile karşılaştırır;
bir mismatch __stack_chk_fail'i çağırır ve abort eder. Defender'ın kör
noktası: bir thread için TLS block, o thread'in stack'ine bitişik olarak
allocate edilir. Bu yüzden yeterince büyük bir overflow, TLS'e ulaşıp
referans canary'yi de overwrite edebilir. Saldırgan, stack kopyasını ve TLS
stack_guard'ını aynı seçili değere ayarlarsa, karşılaştırma başarılı
olur ve kontrol sessizce yenilir — gerçek canary'nin leak edilmesine gerek
kalmaz.
Master canary, _dl_setup_stack_chk_guard() / THREAD_SET_STACK_GUARD
aracılığıyla kernel'in sağladığı rastgelelikten (_dl_random) bir kez
initialize edilir ve düşük byte'ı 0x00'dır, böylece string işlemleri onun
üzerinde truncate olur (bu da onu string read'leri ile leak etmeyi
zorlaştırır).
Walkthrough¶
Değerlerin yaşadığı yer (x86-64):
__stack_chk_guard -> process master value (non-threaded path)
%fs:0x28 -> tcbhead_t.stack_guard (per-thread reference)
[ ... buffer ... ][ saved canary ] <- on-stack copy compared in epilogue
Function epilogue (kavramsal, GCC'nin emit ettiği şekliyle):
mov rax, [rbp-0x8]
xor rax, fs:0x28 ; compare on-stack copy to TLS reference
je .ok ; equal -> canary intact
call __stack_chk_fail ; mismatch -> abort
.ok: ; ... normal return ...
Warning
Her ikisini de overwrite etme tekniği yalnızca overflow primitive'i,
vulnerable buffer'dan saved canary üzerinden thread'in TLS stack_guard'ına
kadar uzanacak kadar uzun ve bitişik olduğunda geçerlidir. Bu, spawn edilen
thread'ler için thread-stack/TLS bitişikliğinin bir özelliğidir; burada
kavramsaldır, hazır bir exploit değil. Daha basit, daha yaygın bypass bir
stack canary leak'tir — referansı yeniden yazmak
yerine değeri okumak.
İki ayrı bypass ailesi
1. Leak then reuse: info-leak the canary, write it back verbatim.
(see stack-canary-leak, format-string-canary-leak)
2. Overwrite master: overflow far enough to clobber TLS stack_guard,
set on-stack copy == TLS copy == attacker value.
3. Brute force: fork-without-re-randomize servers -> guess byte by
byte (see byte-by-byte-canary-brute-force).
"master/TLS canary overwrite"in adlandırdığı şey aile (2)'dir: sadece kopya değil, referans da attacker-controlled'dır.
Mitigation¶
Canary referansını bir overflow'un ulaşabileceği bir şey olarak gör:
stack-canaries / stack-protector
(-fstack-protector-strong) etkin kalsın ki buffer en azından guard edilsin,
ama bunun TLS'in bitişik bir overwrite'ını durdurmadığını kabul et. Layout
hardening (stack ve TLS arasına guard page'ler) ve bounds-checked kopyalar
(FORTIFY) pencereyi daraltır. Canary, blind overflow'lara karşı savunur;
bir leak (ya da ulaşılabilir bir master) onu çökerttiğinden, leak'e dirençli
tasarımla birlikte kullan.