Skip to content

Stack clash

Bir process'in stack'ini komşu bir memory region ile çarpışana kadar büyütüp, sonra guard page'in üzerinden "atlayarak" o region ile overlap edip onu corrupt etmek.

Mechanism

Bir process stack'i talep üzerine büyür: stack pointer eşlenmemiş belleğe dokunduğunda, kernel'in page-fault handler'ı stack'i aşağı doğru genişletir. Qualys'in 2017 tarihli "The Stack Clash" advisory'si, bu büyümenin komşu mapping'lere karşı güvenli şekilde sınırlanmadığını gösterdi.

Note

Tutması gereken invariant, stack'in yalnızca boş alana doğru büyüyebilmesi ve bir fault tarafından yakalanmasıdır. Advisory bunu kırar: "if the stack-pointer of a process can move from the stack into another memory region (which ends exactly where the stack starts) without raising a page-fault, then the process uses this other memory region as if it were an extension of the stack." Kernel'in savunması bir guard page'dir — stack'in altındaki eşlenmemiş bir PROT_NONE page — ancak yeterince büyük tek bir allocation onu atlar: "if the stack-pointer 'jumps' over the guard-page ... then no page-fault exception is raised and the stack extends into the other memory region." O teorik guard-page atlamasına CVE-2017-1000364 atandı. stack ile bir komşu (heap, mmap veya kütüphane) aynı page'leri overlap ettiğinde, birinden yapılan write'lar diğerini ezer.

Advisory, exploitation'ı bir sekans olarak çerçeveler:

  1. Clashstack başka bir region'a dayanana kadar stack memory ayır.
  2. Jump — guard page'e dokunmadan stack pointer'ı guard page üzerinden o region'a taşı.
  3. Smashstack'i diğer region ile overwrite et, ya da tersi.

Walkthrough

Clash, bir kerede büyük miktarda stack tüketen herhangi bir şeyle tetiklenir: derin recursion, büyük alloca()/VLA'lar veya devasa argv/environ. Qualys, kontrollü allocation'ı sağlayan birkaç gerçek bug için PoC'ler yayınladı; bunlara CVE-2017-1000366 (glibc ld.so etkileşimi), CVE-2017-1000367 (Sudo tty parsing) ve CVE-2017-1000369 (Exim) dahildir (tam liste için aşağıdaki Qualys advisory'ye bak).

/* conceptual: a single large alloca jumps the stack pointer past the guard page */
void deep(size_t n) {
    char gap[n];          /* attacker-influenced size = jump distance */
    gap[0] = 0;           /* first touch may land inside the heap/mmap region */
}
Çarpışmayı /proc//maps içinde gözlemleme

$ cat /proc/$(pidof victim)/maps
...
7ffff7a00000-7ffff7dd0000 r-xp  ...  libc.so      <-- mmap region
7ffffffde000-7ffffffff000 rw-p  ...  [stack]      <-- grows downward
# under a stack-clash the [stack] low bound descends toward the mapping above;
# a single oversized frame lets %rsp land inside the libc/mmap mapping,
# so subsequent stack stores overwrite that region's data.
Normalde [stack] altında duran 4 KiB'lık guard page'e dokunulmak yerine üzerinden atlanır, dolayısıyla atlama anında hiçbir SIGSEGV oluşmaz.

Detection

stack'in bir mapping'e taşması, /proc/<pid>/maps içinde anormal [stack] sınırları olarak ya da beklenmedik bir region içindeki fault'lar olarak ortaya çıkar. Dağıtımlar, guard region'ı büyüten ve (bazıları için) bir stack-probing ABI ekleyen kernel patch'leri yayınladı; uyumsuz probing, derleme zamanında tespit edilebilir.

Mitigation

Koordineli düzeltme, kernel stack guard gap'ini büyüttü (Linux'ta 1 MiB'lık bir guard region) ve compiler stack-probing (-fstack-clash-protection) ekledi; böylece her büyük frame aşağı inerken her guard page'e dokunur ve bir atlama yerine bir fault'u garanti eder. Bkz. guard page'ler ve ASLR (advisory ayrıca offset2lib varyantlarıyla ASLR'yi de yendi).

References