Final Round
Welcome back to my writeup! With Trend Micro's uCTF 2025 now complete, I’m sharing my writeup for the finals. Please note that I’ll only cover two challenges, the ones I personally solved, since some of the others were handled by my team members. These two challenges were key to helping our team secure 3rd place.

C2 An Obfuscated Script
For this challenge, we’re given an obfuscated JavaScript code. Essentially, it’s a cryptography challenge, but with a few twists to make it more difficult.
C2_an_obfuscated_script.js
This JavaScript code is an obfuscated self-decrypting script that uses cryptography to hide its true functionality until runtime. It first derives a 256-bit AES key from a password (_0xb3fde3) and a Base64-encoded salt (_0x28f6c4) using PBKDF2 (Password-Based Key Derivation Function 2) with SHA-512 as the hashing function and 100,000 iterations. PBKDF2 essentially stretches a password into a fixed-length cryptographic key by repeatedly hashing it with the salt, mathematically defined as:
where is the password, is the salt, is the iteration count, is the desired key length, and is the hash function (SHA-512). Using this derived key, the script sets up an AES-256-CBC cipher with the provided initialization vector (_0x466dee) to decrypt the Base64-encoded ciphertext (_0x1b50eb). AES-CBC divides the plaintext into blocks and encrypts each block while chaining it with the previous ciphertext block; mathematically, each ciphertext block is with . After decryption, the result is dynamically executed using new Function("require", ...), meaning the hidden payload is run as JavaScript code. Essentially, this script hides its real functionality behind strong cryptography and executes it only when the correct key and IV are used.
To solve this challenge, we first identify the ciphertext, password, salt, and IV from the script. Using the password and salt, we derive a key and decrypt the ciphertext with AES-256-CBC to reveal the flag.
solve.py
Here's the output

C10 Invisible Challenge
For this challenge, we were also given a straightforward JavaScript code.
C10_invisible_challenge.js
The code defines a secret.message string full of unusual characters and prints "Find the invisible flag!". To most editors and on normal screens, these characters appear as blank spaces because they are full-width spaces and Hangul filler characters, which render almost invisibly. That’s why the string seems empty, even though it actually contains data.
The hidden message is encoded in these invisible characters: typically represents 0 and ㅤ1. By mapping each character to a bit and converting the resulting binary string to ASCII, the real content which is the flag can be revealed. Essentially, the flag is right in front of your eyes, but standard text rendering hides it, requiring careful decoding to uncover.
To decode the invisible string, you need to map each character to a binary value—commonly, as 0 and ㅤ1 then concatenate these bits into a binary string. Next, split the binary string into bytes (8 bits each) and convert each byte to its ASCII character. Doing this for the entire string will reveal the hidden message or flag that’s encoded within the seemingly invisible characters.
solve.py
Here's the output

And that’s a wrap! Participating in such a large-scale CTF competition has been both incredibly enjoyable and a significant personal achievement. The challenges were tough, but they offered a lot of learning opportunities. Securing 3rd place with our team feels like a major accomplishment and is something I’m truly proud of.
Last updated