Segment Heap exploitation¶
Windows Segment Heap Variable Size (VS) allocator'ının encode'lu
_HEAP_VS_CHUNK_HEADERmetadata'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/RtlpHpHeapHandleErrorcorruption 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
HeapKeyile 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¶
- Mark Vincent Yason, Windows 10 Segment Heap Internals — white paper (Black Hat USA 2016, PDF)
- Mark Vincent Yason, Windows 10 Segment Heap Internals — slides (Black Hat USA 2016, PDF)
- Blue Frost Security — Windows Segment Heap: Attacking the VS Allocator (Varnavas Papaioannou, 2022) (kaynak URL'i artık erişilemiyor — host DNS-dead, Wayback snapshot yok)