Skip to content

Segment Heap exploitation

Windows Segment Heap Variable Size (VS) allocator'ının encode'lu _HEAP_VS_CHUNK_HEADER metadata'sını corrupt et ki free/coalesce mantığı attacker'ın kontrol ettiği bir chunk'ı geri versin.

Mechanism

Bu not vs. sibling

Bu sayfa, allocator yapısını değil, VS allocator'ının _HEAP_VS_CHUNK_HEADER coalesce mantığına karşı somut bir corruption primitive'ini belgeler. Allocator'ın internals'ı (LFH/VS/backend dispatch, page-range tree'leri) için önce bkz. Segment Heap.

Invariant

Segment Heap, Windows 10 için tanıtılan native heap'tir (modern app'ler/Edge content process'leri tarafından kullanılır; Windows 8.1+ ilgili backend çalışmasına sahiptir), Mark Vincent Yason'ın Windows 10 Segment Heap Internals (Black Hat USA 2016) çalışmasında belgelenmiştir. Front/back bileşenlerine bölünür: küçük aynı boyutlu bucket'lar için Low Fragmentation Heap (LFH), 16-byte granularity'de 1 byte'tan 0x20000 (131072) byte'a kadar allocation'lar için Variable Size (VS) allocator ve daha büyük istekleri servis edip segment'leri page-range descriptor'larıyla yöneten Backend.

Her VS block bir _HEAP_VS_CHUNK_HEADER ile başlar. Onun size/status word'ü (Sizes; UnsafeSize, UnsafePrevSize, MemoryCost ve Allocated byte'ını taşır) encode'lu saklanır: iki değerle XOR'lanır — chunk header'ın kendi adresi (VS allocator'da header chunk'ın başında oturur, yani header adresi = chunk adresidir; ikisi ayrı iki faktör değil, tek ve aynı adrestir) ve RtlpHpHeapGlobals.HeapKey (8-byte rastgele bir değer). Free sırasında allocator Allocated'ı okur, sonra sonucu VS free tree'sine eklemeden önce komşu free chunk'ları birleştirmek için RtlpHpVsChunkCoalesce()'i çağırır. Attacker'ın suistimal ettiği invariant: komşu bir VS header'ı overwrite edebilirsen (örneğin bir heap overflow ile), encoding konuma bağlı ve header başınadır — ama coalescing ve free-tree yerleşimi attacker'ın etkilediği size field'ları tarafından sürülür. Coalesce mantığı yanlış bir size/prev-size'ı kabul edecek şekilde bir header forge etmek, bir free chunk'ı canlı bir allocation üzerine uzatmanı sağlar; bu da sonraki bir allocation tarafından geri verilecek overlapping / controlled-size bir chunk üretir — arbitrary write primitive'in kapısı. Özellikle, Allocated bir byte olduğundan, körü körüne overwrite edilmiş bir header ~255/256 olasılıkla allocated olarak okunur, dolayısıyla o byte kontrol edilmedikçe bir overflow genellikle istenmeyen merge'den kaçınır.

Walkthrough

Bu bir Windows kernel/userland WinDbg workflow'udur; amaç, VS header layout'unu ve onun encoding'ini gözlemleyerek bir heap overflow sonrası bir header forge planlayabilmektir.

:: Identify Segment Heap usage for a process heap in WinDbg
0:000> !heap -s
        Heap Address   Type
        00000226`...   Segment

:: Dump a VS chunk header (encoded Sizes word is not human-readable in memory)
0:000> dt ntdll!_HEAP_VS_CHUNK_HEADER 0x00000226`xxxxxxxx
   +0x000 Sizes            : _HEAP_VS_CHUNK_HEADER_SIZE
   +0x008 EncodedSegmentPageOffset : Bitfield
   +0x008 UnusedBytes      : Bitfield
   +0x008 SkipDuringWalk   : Bitfield
   +0x008 Spare            : Bitfield

Size alt-yapısı, coalescing'in güvendiği field'ları taşır:

0:000> dt ntdll!_HEAP_VS_CHUNK_HEADER_SIZE
   +0x000 MemoryCost       : Pos 0,  16 Bits    ; pages beyond the header page
   +0x000 UnsafeSize       : Pos 16, 16 Bits    ; this chunk size (>>4)
   +0x004 UnsafePrevSize   : Pos 0,  16 Bits    ; previous chunk size (>>4)
   +0x004 Allocated        : Pos 16, 8  Bits    ; 1 = allocated

Plaintext Sizes'ı kurtarmak/forge etmek için iki parçalı key (header addr = chunk addr, ve HeapKey) ile XOR'laman gerekir:

0:000> dq ntdll!RtlpHpHeapGlobals L2          ; locate the 8-byte HeapKey
00007ff8`xxxx0000  <HeapKey>  ...

:: Plaintext Sizes  =  *(header+0) XOR header_addr XOR HeapKey   ; header_addr == chunk start
0:000> ? poi(0x...header) ^ 0x...header ^ poi(ntdll!RtlpHpHeapGlobals)
Evaluate expression: <decoded size/status word>
Exploitation taslağı (VS allocator overflow)

1. Groom the VS subsegment so a controllable buffer sits adjacent to a
   target VS chunk  (see heap-grooming-feng-shui).
2. Trigger a linear heap-buffer-overflow that overwrites the neighbor's
   _HEAP_VS_CHUNK_HEADER. To survive the coalesce path you must produce a
   header whose decoded Allocated/UnsafeSize/UnsafePrevSize are consistent
   with the position-dependent XOR key (header addr ^ HeapKey; the header
   address is the chunk start).
3. On the next free()/coalesce, RtlpHpVsChunkCoalesce() merges using your
   forged sizes, yielding a free chunk that overlaps a live object.
4. Reclaim that controlled-size region (controlled-size-allocation) to get
   overlapping objects -> arbitrary read/write or type confusion.
RtlpHpHeapGlobals.HeapKey leak'i (bir address/secret leak) olmadan encoding, kesin bir forge'u olasılıksal kılar; 1-byte'lık Allocated field'ı, key'siz bir overwrite'ın yine de allocated olarak okunması için ~255/256 olasılık verir.

Detection

  • Encode'lu header (header addr = chunk start ve HeapKey'in XOR'u) kendisi bir integrity check'tir: tutarlılığı sağlayamayan corrupt/forge edilmiş bir header bir __fastfail / RtlpHpHeapHandleError corruption stop'u tetikler.
  • Application Verifier / PageHeap ve subsegment'lerin etrafındaki guard page'leri, lineer overflow'ları anında access violation'lara çevirir.

Mitigation

  • Segment Heap'in kendi hardening'i: rastgele bir HeapKey ile header başına XOR encoding, heap address randomization, guard page'leri ve metadata tutarsızlığında fast-fail, blind header forgery için çıtayı yükseltir.
  • Üstüne katmanlanan genel exploit mitigation'ları: ASLR (HeapKey/base leak'ini engellemek için), DEP/NX ve bir write primitive'i takip eden control-flow aşaması için Intel CET shadow stack'leri.

References