1 / 59

Windows Heap Exploitation (Win2KSP0 through WinXPSP2)

Original CanSecWest 04 Presentation: Matt Conover & Oded Horovitz XP SP2 Additions added/presented, Matt Conover @ SyScan 2004. Windows Heap Exploitation (Win2KSP0 through WinXPSP2). Agenda. “Practical” Windows heap internals How to exploit Win2K – WinXP SP1 heap overflows

Download Presentation

Windows Heap Exploitation (Win2KSP0 through WinXPSP2)

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Original CanSecWest 04 Presentation: Matt Conover & Oded Horovitz XP SP2 Additions added/presented, Matt Conover @ SyScan 2004 Windows Heap Exploitation(Win2KSP0 through WinXPSP2)

  2. Agenda • “Practical” Windows heap internals • How to exploit Win2K – WinXP SP1 heap overflows • 3rd party (me ) assessment of WinXP SP2 improvements • How to exploit WinXP SP2 heap overflows • Summary

  3. Windows Heap Internals • Many heaps can coexist in one process (normally 2-3) PEB Default Heap 2nd Heap

  4. Windows Heap Internals • Important heap structures Segments Segment List Virtual Allocation list Free Lists Lookaside List

  5. Windows Heap Internals • Introduction to Free Lists • 128 doubly-linked list of free chunks (from 8 bytes to 1024 bytes) • Chunk size is table row index * 8 bytes • Entry [0] is a variable sized free lists contains buffers of 1KB <= size < 512KB, sorted in ascending order 1400 2000 2000 2408 16 16 48 48

  6. Windows Heap Internals • Lookaside Table • Used for “fast” allocates and deallocates when available • Starts empty • 128 singly-linked lists of busy chunks (free but left marked as busy) 16 48 48

  7. Windows Heap Internals • Why have lookasides at all? Speed! • Singly-linked • Used to quickly allocate or deallocate • No coalescing (leads to fragmentation) • So the lookaside lists “fill up” quickly (4 entries)

  8. Windows Heap Internals 01 – Busy 02 – Extra present 04 – Fill pattern 08 – Virtual Alloc 10 – Last entry 20 – FFU1 40 – FFU2 80 – No coalesce Self Size Previous chunk size Segment Index Flags Unused bytes Tag index (Debug) 0 1 2 3 4 5 6 7 8 • Basic chunk structure – 8 Bytes Overflow direction

  9. Windows Heap Internals 0 1 2 3 4 5 6 7 8 • Free chunk structure – 16 Bytes Self Size Previous chunk size Segment Index Flags Unused bytes Tag index (Debug) Next chunk Previous chunk

  10. Windows Heap Internals • Allocation algorithm (high level) • If size >= 512K, virtual memory is used (not on heap) • If < 1K, first check the Lookaside lists. If there is no free entries on the Lookaside, check the matching free list • If >= 1K or no matching entry was found, use the heap cache (not discussed in this presentation). • If >= 1K and no free entry in the heap cache, use FreeLists[0] (the variable sized free list) • If still can’t find any free entry, extend heap as needed

  11. Windows Heap Internals • Allocate algorithm – FreeLists[0] • This is usually what happens for chunk sizes > 1K • FreeLists[0] is sorted from smallest to biggest • Check if FreeLists[0]->Blink to see if it is big enough (the biggest block) • Then return the smallest free entry from free list[0] to fulfill the request, like this: • While (Entry->Size < NeededSize) • Entry = Entry->Flink

  12. Windows Heap Internals • Allocate algorithm – Virtual Allocate • Used when ChunkSize > VirtualAlloc threshold (508K) • Virtual allocate header is placed on the beginning of the buffer • Buffer is added to busy list of virtually allocated buffers (this is what Halvar’s VirtualAlloc overwrite is faking)

  13. Windows Heap Internals • Free Algorithm (high level) • If the chunk < 512K, it is returned to a lookaside or free list • If the chunk < 1K, put it on the lookaside (can only hold 4 entries) • If the chunk < 1K and the lookaside is full, put it on the free list • If the chunk > 1K put it on heap cache (if present) or FreeLists[0]

  14. Windows Heap Internals • Free Algorithm – Free to Lookaside • Free buffer to Lookaside list only if: • The lookaside is available (e.g., present and unlocked) • Requested size is < 1K (to fit the table) • Lookaside is not “full” yet (no more than 3 entries already) • To add an entry to the Lookaside: • Put to the head of Lookaside • Point to former head of Lookaside • Keep the buffer flags set to busy (to prevent coalescing)

  15. Windows Heap Internals A C A C A B • Free Algorithm – Coalesce Step 1: Buffer free Step 2: Buffer removed from free list Step 3: Buffer removed from free list A + B Coalesced A + B + C Coalesced Step 4: Buffer placed back on the free list

  16. Windows Heap Internals • Free Algorithm – Coalesce • Where coalesce cannot happen: • Chunk to be freed is virtually allocated • Chunk to be freed will be put on Lookaside • Chunk to be coalesced with is busy • Highest bit in chunk flags is set • …

  17. Windows Heap Internals • Free Algorithm – Coalesce (cont) • Where coalesce cannot happen: • Chunk to be freed is first  no backward coalesce • Chunk to be freed is last  no forward coalesce • The size of the coalesced chunk would be >= 508K

  18. Windows Heap Internals • Summary – Questions? • Just remember: • Lookasides are allocated from and freed to before free lists • FreeLists[0] is mainly used for 1K <= ChunkSize < 512K • Coalescing only happens for entries going onto FreeList, not lookaside list • Entries on a certain lookaside will stay there until they are allocated from

  19. Heap Exploitation: Basic Terms • 4-byte Overwrite • Able to overwrite any arbitrary 32-bit address (WhereTo) with an arbitrary 32-bit value (WithWhat) • 4-to-n-byte Overwrite • Using a 4-byte overwrite to indirectly cause an overwrite of an arbitrary-n bytes

  20. Arbitrary Memory Overwrite Explained Index < 64 Flags != 1 Fake Flink (WithWhat) Fake Blink (WhereTo) • Coalesce-On-Free 4-byte Overwrite • Utilize coalescing algorithms of the heap • This is the method first discussed by Oded and I at CSW04 – it is our preferred method for reliable heap exploitation on all versions < XPSP2 • Just make sure to fill the Lookaside[ChunkSize] (put 4 entries on heap) before freeing a chunk of ChunkSize to ensure coalescing • Arbitrary overwrite happens when the overflowed buffer gets freed Overflow start

  21. Arbitrary Memory Overwrite • Lookaside List Head Overwrite: • 4-to-n-byte overwrite • What we want to do is overwrite a Lookaside list head and then allocate from it • We must be the first one to allocate that size • We will get a chunk back pointing to whatever location in memory we want • Use this to overwrite a function pointer or put the shellcode at a known writable location

  22. Arbitrary Memory Overwrite • Lookaside List Head Overwrite: How To • Use the Coalesce-on-Free Overwrite, with these values: • FakeChunk.Blink = &Lookaside[ChunkSize] where ChunkSize is a pretty infrequently allocated size • FakeChunk.Flink = what we want a pointer to • To calculate the FakeChunk.Blink value: • LookasideTable = HeapBase + 0x688 • Index = (ChunkSize/8)+1 • FakeChunk.Blink = LookasideTable + Index * EntrySize (0x30) • Set FakeChunk.Flags = 0x20, FakeChunk.Index = 1-63, FakeChunk.PreviousSize = 1, FakeChunk.Size = 1

  23. Exploition Made Simple Overwrite PEB lock routine to point to PEB space Put shellcode into PEB space Then cause the PEB lock routine to execute PEB Header PEB lock/unlock function pointers 0x7ffdf020, 0x7ffdf024 0x7ffdf130 ~1k of payload

  24. Exploitation Made Simple • Win2K through WinXP SP1 in a single attempt: • First 4-byte overwrite: • Blink = 0x7ffdf020, • Flink = 0x7ffdf154 • 4-to-n-byte overwrite: • Blink = &Lookaside[(n/8)+1] • Flink = 0x7ffdf154 • Be the first to allocate n bytes (cause HeapAlloc(n)): • Put your shellcode into the returned buffer • All done! Either wait, or cause a crash immediately: • For example, do 4-byte overwrite with Blink = 0xABABABAB

  25. Exploitation Made Simple • Forcing Shellcode To Run • Most applications (read: everyone but MSSQL) don’t specially handle access violations • An access violation results in ExitProcess() being called • Once the process attempts to exit, ExitProcess() is called • The first thing ExitProcess() does is call the PEB lock routine • Thus, causing crash = instant shellcode execution • Nice 

  26. Exploitation Made Simple • Demo

  27. Heap Exploitation • Questions? • This technique we just covered is very reliably, providing success almost every time on all Win2K (all service packs) and WinXP (up to SP2) • On to XP SP2….

  28. XP Service Pack 2 • Effects on Heap Exploitation • New low fragmentation heap for chunks >= 16K • PEB “shuffling” (aka randomization) • New security cookie in each heap chunk • Safe unlinking: (usually) stops 4-byte overwrites

  29. XP Service Pack 2 • PEB Randomization • In theory, it could have a big impact on heap exploitation – though not in reality • Prior to XP SP2, it used to always be at the highest page available (0x7ffdf000) • The first (and ONLY the first) TEB is also randomized • They seem to never be below 0x7ffd4000

  30. XP Service Pack 2 • PEB Randomization – Does it make any difference? • Not much, randomization is definitely a misnomer • If 2 threads are present: • We can write to 0x7ffdf000-0x7ffdffff, and • 2 other pages between 0x7ffd4000-0x7ffdefff • If 3 threads are present: • 0x7ffde000-0x7ffdffff • 2 other pages between 0x7ffd4000-0x7ffdefff • … • If 11 threads are present: • 100% success, no empty pages

  31. XP Service Pack 2 • PEB Randomization – Summary • Provides little protection for… • Any application that have m workers per n connections (IIS? Exchange?) • Any service in dllhost/services/svchost or any other “active” surrogate process

  32. XP Service Pack 2 Self Size Self Size Previous chunk size Previous chunk size Segment Index New Cookie Flags Flags Unused bytes Unused bytes Segment Index Tag index (Debug) 0 1 2 3 4 5 6 7 8 • Heap header cookie *reminder: overflow direction XP SP2 Header Current Header

  33. XP Service Pack 2 • Heap header cookie calculation • If ((AddressOfChunkHeader / 8) XOR Chunk->Cookie XOR Heap->Cookie != 0) CORRUPT • Since the cookie has only 8-bits, it has 2^8 = 256 possible keys • We’ll randomly guess the security cookie, on average, 1 of every 256 attempts

  34. XP Service Pack 2 • On the normal WinXP SP2 system, corrupting a chunk will do nothing • Since we only overwrite the Flink/Blink of the chunk, we corrupt no other chunks • Thus we can keep trying until we run out of memory

  35. XP Service Pack 2 • Summary so far… • At this point, we see that we can with enough time trivially defeat all the other protection mechanisms. • On to “safe” unlinking…

  36. XP Service Pack 2 A B C • Safe Unlinking • Safe unlinking means that RemoveListEntry(B) will make this check: • (B->Flink)->Blink == B && (B->Blink)->Flink == B • In other words: • C->Blink == B && A->Flink == B • Can it be evaded? Yes, in one particular case. Header to free

  37. XP Service Pack 2 • UnSafe-Unlinking FreeList Overwrite Technique p = HeapAlloc(n); FillLookaside(n); HeapFree(p); EmptyLookaside(n); Overwrite p[0] (somewhere on the heap) with: p->Flags = Busy (to prevent accidental coalescing) p ->Flink = (BYTE *)&ListHead[(n/8)+1] - 4 p ->Blink = (BYTE *)&ListHead[(n/8)+1] + 4 HeapAlloc(n); // defeats safe unlinking (ignore result) p = HeapAlloc(n); // defeats safe unlinking // p now points to &ListHead[(n/8)].Blink

  38. XP Service Pack 2 • Defeating Safe Unlinking (before overwrite) [4] Blink ListHead[n-1] [0] Flink [0] Flink FreeChunk ListHead[n] [4] Blink [4] Blink [0] Flink ListHead[n+1]

  39. XP Service Pack 2 • Defeating Safe Unlinking: Step 1 (Overwrite) [4] Blink ListHead[n-1] [0] Flink [0] Flink FreeChunk ListHead[n] [4] Blink [4] Blink [0] Flink ListHead[n+1] Now call HeapAlloc(n) to unlink FreeChunk from ListHead FreeChunk->Blink->Flink == *(*(FreeChunk+4)+0) FreeChunk->Flink->Blink) == *(*(FreeChunk+0)+4) Both point to FreeChunk, unlink proceeds!

  40. XP Service Pack 2 • Defeating Safe Unlinking: Step 2 (1st alloc) [4] Blink ListHead[n-1] [0] Flink ListHead[n] [4] Blink [0] Flink ListHead[n+1] FreeChunk->Blink->Flink = FreeChunk->Flink FreeChunk->Flink->Blink = FreeChunk->Blink Returns pointer to previous FreeChunk

  41. XP Service Pack 2 • Defeating Safe Unlinking: Step 3 (2nd alloc) [4] Blink ListHead[n-1] [0] Flink ListHead[n] [4] Blink [0] Flink ListHead[n+1] Returns pointer to &ListHead[n-1].Blink Now the FreeLists point to whatever data the user puts in it

  42. XP Service Pack 2 • Questions?

  43. XP Service Pack 2 • Unsafe-Unlinking FreeList Overwrite Technique • For vulnerabilities where you can control the allocation size, safe unlinking can be evadable. • But is this reliable? Hardly. • …

  44. XP Service Pack 2 • Unsafe-Unlinking FreeList Overwrite Technique (cont) • We have to flood the heap with this repeating 8 byte sequence: • [FreeListHead-4][FreeListHead+4] • And hope the Chunk’s Flink/Blink pair is within the range we can overflow • But there is an even easier method…

  45. XP Service Pack 2 • Chunk-on-Lookaside Overwrite Technique • In fact on XP SP2, there is an even easier method • Lookasides lists take precedence over free lists • This is quite convenient because… • Lookaside lists (singly linked) are easier to exploit than the free lists (doubly linked)

  46. XP Service Pack 2 • Chunk-on-Lookaside Overwrites • HeapAlloc checks the lookaside before the free list • There is no check to see if the cookie was overwritten since it was freed • It is a singly-linked list, thus the safe unlinking check doesn’t apply • Result: a clean exploitation technique (albeit with brute-forcing required)

  47. XP Service Pack 2 • Chunk-on-Lookaside Overwrites (Technique Summary) // We need at least 2 entries on lookaside a_n[0] = HeapAlloc(n) a_n[1] = HeapAlloc(n) HeapFree(a_n[1]) HeapFree(a_n[0]) Overwrite a_n[0] (somewhere on the heap) with: a_n[0].Flags = Busy (to prevent accidental coalescing) a_n[0].Flink = AddressWeWant HeapAlloc(n) // discard, this returns a_n[0] p = HeapAlloc(n) p now points to AddressWeWant

  48. XP Service Pack 2 • Chunk-on-Lookaside Overwrite - Success rate? • Reqiures overwriting a chunk already freed to the lookaside • If an attacker overflows a buffer repeatedly, how often will he/she need to before succeeding?

  49. XP Service Pack 2 • Chunk-on-Lookaside Overwrite – Empirical results • 64K heap with 1 segment • All chunk sizes sizes between 8-1024 bytes • Max overflow size = 1016 bytes • Random number of allocs between 10-1000 • Free probability of 50% • Took an average of 84 allocations to be within overflow range • It will take at least 2 overwrites (one to overwrite a function pointer, one to place shellcode)

  50. XP Service Pack 2 • Chunk-on-Lookaside Overwrite – Empirical results • Application specific function pointer and writable location for shellcode: • 84*2 = 168 attempts to execute shellcode • Using PEB lock routine + PEB space (application generic): • 84*2*12=2,016 attempts to execute shellcode • The 12 is for the 12 possible locations of the PEB due to PEB randomization

More Related