Story of two pointers attack on LFH

This time post will be based on talk by Steven Seeley (Ghost in the allocator) and talk by Chris Valasek & Tarjei Mandt (Windows 8 Heap Internals).

I would like to focus on win8 _HEAP_USERDATA_HEADER structure, and its main perfomance feature – missing validation check per allocation from its block.

Highlights :

    • randomization
      •   calculation formula : 
    • page guard
      •   introduced guard pages between user data blocks to avoid easy rewriting its _HEAP_USERDATA_HEADER
      •   if guard page should be used is decided in RtlpLowFragHeapAllocFromContext() :
    • header replacement

Additional findings :

  • randomization and its limitation
    • analysing piece of code responsible for randomization alloc from user block lead to interesting limitation of pseudo random :
    • in other words, psuedo-randomization in allocs is limited only on sizeof(pointer) base {first not -1 member is your man } :
  • user data block and different size
    • user data block size per chunk size, and per allocation of new user data block can vary

  • header is our playground
    • Due missed validation check on _HEADER_USERDATA_BLOCK, we can play with this structure and find a way how to alter it, obviously no-one (even heap manager) to care ,  in the shape that will look cool for managing heap manager in our own way

    • When we look closer at calculation formula we observe that important member of this header are :
      • FirstAllocationOffset
      • BlockStride
      • BusyBitmap.SizeOfBitMap
      • BusyBitmap.Buffer
    • other members are not even checked, so goal would be to rewrite this members in reasonable way


  • header targeting
    • There are at least two options :
      • Force heap to allocate UserDataBlock in that way, that heap uses free chunk resides right after memory chunk which we can overflow
      • Defeat randomization and overwrite _HEAP_USERDATA_HEADER directly from memory chunk from LFH
        • Be sure that you dont alter Guard Page – f.e. as steven mentioned in his talk alloc 3 userblocks for given size (last two blocks will reside side by side)

    • .. and overflow from every memory chunk placed at second user block!
      • overflow from each chunk from second userdata block (maybe unreal condition ?) will defeat randomization, and ensure overwriting following block’s _HEAP_USERDATA_HEADER structure. And dont bother about rewriting other memory chunk headers – nobody will check validity of overwritten headers  – unless you dont want to free them!
    • Properly craft your header I.
      • Two Pointers are good enough Just take a look how is member of _HEAP_USERDATA_HEADER aligned
      • First two pointers (SubSegment, Reserved) are not interesting, last ‘pointer’ BitmapData and third ‘pointer’ (SizeIndexAndPadding, Signature) are also not interesting , BusyBitmap here you have to place valid ptr to Buffer, and reasonable SizeOfBitmap, and also crucial for calculation formula are members of fourth pointer FirstAllocationOffset and BlockStride.
      • question is : Are we able to cover BusyBitmapBuffer with same pointer that covers FirstAllocationOffset and BlockStride ? .
  • target memory
    • Due to sizeof(pointer) based randomization, it is no problem to allocate memory (relative to your userdata block) you want! Just calculate (depending on FirstAllocationOffset, BlockStride, and position of  _HEAP_USERDATA_HEADER) how many members of bitmapdata of size sizeof(pointer) you need to reserve (value == -1), in particular bitmapdata member set just one bit as 0, and next member of bitmapdata set as free (value == 0). And null randomization is performed
    • Properly craft your header II.
      • For preparing calculation prior to setuping the BitMapData, is necessary to know BlockStride.
      • As i mentioned, i like two pointer approach so BlockStride, and FirstAllocationOffset as well, is deeply connected with BitMapData buffer address.
      • It can seem like a little problem, but when you are able to do small HeapSpray, it should no be problem to calc 1+1 .

  • But keep in mind, that memory that should be targeted, have at its fake _HEAP_ENTRY.UnusedBytes fullfill condition !(UnusedBytes & 0x3F) for preventing RtlpLogHeapFailure()

Rewriten header :


Consequences :

  • You are able to target (malloc will return address you can specified) every address which you can cover with FirstAllocationOffset, BlockStride, and position of  _HEAP_USERDATA_HEADER
  • More simplier, you can force malloc to return 2times the same adress. First time for app where can be place some vtable – or sensitive data, and second time for your writeable buffer .

Conclusions : This post was only playing with the heap, and showing what can be done with current state of heap manager logic. A lot of improvements was done in win8 from winXP, but validation checks are realy essential to keep proper state of heap (or at least detect corruption). Problem with missing validation checks on LFH – UsetData block was present here from win7, and not fixed in current win8, i hope in the next version of windows will be added also validation check on _HEAP_USERDATA_HEADER header per allocation (or maybe something better ) instead of just renaming members and another calculation formula.

I also add .cpp project with collection of public heap specific exploiting techniques,
hope it can help to someone with future research :)
Leave a comment


Leave a Reply

[ Ctrl + Enter ]

Go To Top

Get every new post delivered to your Inbox

Join other followers: