# CryptX CTF 2026 - Full Writeup
**Platform:** https://ctf.cryptx.lk
**Event:** CryptX CTF by ICTS, University of Sri Jayewardenepura
---
## Warmups
### 1. Obfusckated Cells
| Field | Value |
|-------|-------|
| Category | Warmups |
| Flag | `cryptx{br41nfu9k_m4st3r_of_m3m0ry_c3lls}` |
**Summary:** This was in Brainfuck language.
---
### 2. Magic Peeler
| Field | Value |
|-------|-------|
| Category | Warmups |
| Flag | `cryptx{y0u_kn0w_y0ur_f1l3_s1gn4tur3s}` |
**Description:** A file with nested layers of compression.
**Solution:**
1. Start with a file called `Archive` that has no extension.
2. Use `file` to identify the type — it cycles through multiple archive formats (zip, tar, gzip, bzip2, etc.).
3. Keep peeling: identify the format, extract, and repeat.
4. After peeling through all layers, a `flag.txt` is revealed containing the flag.
**Key Technique:** Recursive file format identification using magic bytes / file signatures.
---
### 3. Brute... Brute...
| Field | Value |
|-------|-------|
| Category | Warmups |
| Flag | `cryptx{darkc1cle_tw1ce_2st3p7_h1dden_lay3r}` |
**Description:** "Not all data is where it appears to be. Sometimes, the real content lies beneath the pixels."
A password-protected ZIP archive requiring two layers of brute-forcing — one for the ZIP password, and another for a steghide passphrase hidden inside the extracted image.
**Solution:**
#### Layer 1 — Crack the ZIP password
1. Extract the password hash from the archive:
```bash
zip2john protected.zip > hash.txt
```
2. Crack with John the Ripper using `rockyou.txt`:
```bash
john hash.txt --wordlist=/usr/share/wordlists/rockyou.txt
```
3. **ZIP password:** `tsu132009`
4. Extract the archive:
```bash
unzip -P "tsu132009" protected.zip
```
This reveals `unknown.jpg`.
#### Layer 2 — Crack the steghide passphrase
5. The challenge description ("beneath the pixels") hints at image steganography. Use `stegseek` (a fast steghide cracker) with `rockyou.txt`:
```bash
stegseek unknown.jpg /usr/share/wordlists/rockyou.txt
```
6. **Steghide passphrase:** `jassy1373949068`
7. `stegseek` automatically extracts the embedded file `flag.txt`, which contains the flag.
**Flag:** `cryptx{darkc1cle_tw1ce_2st3p7_h1dden_lay3r}` — *"dark circle twice, two-step hidden layer"*
---
### 4. Lactose & Lies
| Field | Value |
|-------|-------|
| Category | Warmups |
| Flag | `cryptx{r51t_mi1ro_qr_d51od5}` |
**Description:** A suspicious JPEG file `Milk_Powder.jpeg`.
**Solution:**
1. There was a RMQR code
---
### 5. Shards of the Signal
| Field | Value |
|-------|-------|
| Category | Warmups |
| Flag | `CryptX{p13c35_pu7_b4ck_1n_0rd3r}` |
**Description:** A `Message.txt` file containing an encoded string. The signal was "split into small parts."
**Solution:**
1. `Message.txt` contains a Base32-encoded string.
2. Base32-decode it to get space-separated ASCII codes: `121 114 99 120 116 112 49 112 123 51 99 51 ...`
3. Convert ASCII codes to characters: `yrcxtp1p{3c3p_5_7uc4b1_k0_n3dr}r`
4. Split into **blocks of 3 characters** and **reverse each block**:
| Block | Reversed |
|-------|----------|
| yrc | cry |
| xtp | ptx |
| 1p{ | {p1 |
| 3c3 | 3c3 |
| p_5 | 5_p |
| _7u | u7_ |
| c4b | b4c |
| 1_k | k_1 |
| 0_n | n_0 |
| 3dr | rd3 |
| }r | r} |
5. Concatenated: `CryptX{p13c35_pu7_b4ck_1n_0rd3r}` — *"pieces put back in order"*
**Key Technique:** Multi-stage encoding: Base32 → ASCII codes → block cipher (reverse blocks of 3).
---
## Phase 1
### 6. The Chimera Vault
| Field | Value |
|-------|-------|
| Category | Phase 1 |
| Type | Sub Question Challenge (6 questions) |
| Flag | `cryptx{secure_vault_pass_v4ult_0v3rr1d3_x7}`|
**Description:** An OSINT-heavy multi-part challenge. A Reddit link led to a Google Drive file containing `Secret_Vault.zip` (password-protected). Players needed to find the developer's identity and GitHub to answer 6 sub-questions, ultimately unlocking the vault.
**Solution:**
**Questions 1-3** (answered by the user via OSINT):
- Identified the developer as **Kenji Tanaka** with GitHub handle `Guardian-Archivist`.
- Found the related repo: [Guardian-Archivist/Vault-Protocols](https://github.com/Guardian-Archivist/Vault-Protocols).
**Question 4 — "What cipher was used?"**
From the [commit 827fe10d](https://github.com/Guardian-Archivist/Vault-Protocols/commit/827fe10de4e2ffcdea3deba4605dc846189975bf), the `README.md` was modified to include:
> *"Fallback cipher initialized using standard galactic mapping."*
**Answer:** `standard galactic alphabet`
**Question 5 — "Decode the cipher text"**
A deleted file `key_directive.txt` in the same commit contained Standard Galactic Alphabet (Minecraft enchanting table) encoded text:
```
ᓵ∷||!¡ℸ ̣ ̇/{ᓭᒷᓵ⚍∷ᒷ_⍊ᔑ⚍ꖎℸ ̣ _!¡ᔑᓭᓭ_⍊4⚍ꖎℸ ̣ _0⍊3∷∷1↸3_ ̇/7}
```
Decoded using the SGA mapping:
**Answer:** `cryptx{secure_vault_pass_v4ult_0v3rr1d3_x7}`
**Question 6 — "Unlock the vault"**
The password for `Secret_Vault.zip` was derived from the flag. Testing 17-character substrings, the password `v4ult_0v3rr1d3_x7` successfully extracted the vault, revealing three Phase 2 sub-challenges:
- `The Breach/Axiom.apk`
- `The Network Fragments/traffic.pcap`
- `The Rogue Architect/Signal_Part1.wav`
---
## Phase 2
### 7. The Network Fragments
| Field | Value |
|-------|-------|
| Category | Phase 2 |
| Flag | `cryptx{wh3n_dns_m33ts_1cmp}` |
**Description:** Analyze `traffic.pcap` to reconstruct hidden data from DNS queries and ICMP messages.
**Solution:**
1. Open `traffic.pcap` with Scapy.
2. **DNS Exfiltration:** Extract Base64-encoded subdomains from DNS queries matching `*.googleapis.com`, `*.cloudflare.com`, `*.amazonaws.com`:
- `Y3J5cHR4` → `cryptx`
- `e3doM25f` → `{wh3n_`
- `ZG5zXw==` → `dns_`
3. **ICMP Data Extraction:** Extract data from ICMP packets (Type 11 — Time Exceeded / Type 8 — Echo Request). XOR the Echo Request and Echo Reply payloads to reveal additional flag fragments.
4. Combine DNS and ICMP fragments in chronological order:
**Flag:** `cryptx{wh3n_dns_m33ts_1cmp}` — *"when DNS meets ICMP"*
**Bonus:** The PCAP also contained `Core_Override_Notes.txt` with the master key `r3v_ch1m3r4_c0r3_99aZ` needed for The Breach challenge.
**Key Technique:** Network forensics — DNS exfiltration analysis + ICMP data extraction with XOR decoding.
---
### 8. The Breach
| Field | Value |
|-------|-------|
| Category | Phase 2 |
| Flag | `cryptx{m0b1l3_byp4ss3d_y0ur_w3b_k3y_1s_K9$eR2!vM7@pX4#c}` |
**Description:** Reverse engineer `Axiom.apk` to find the true encrypted payload using the Master Initialization Key from `Core_Override_Notes.txt`.
**Solution:**
1. Decompile `Axiom.apk` using `jadx`.
2. Locate the crypto package `com.axiom.ctf.crypto.A12.a` — the correct decryption function among several decoys.
3. The correct algorithm implements:
```
int v = x - (k % 5);
r[i] = (char) (v ^ k);
```
where `x` is the encrypted byte, `k` is the key byte (cycling through the master key).
4. Use the master key `r3v_ch1m3r4_c0r3_99aZ` (from `Core_Override_Notes.txt` extracted from the PCAP) to decrypt the hardcoded payload.
5. Run the decryption script:
```python
def decrypt(encrypted_hex, key):
enc = bytes.fromhex(encrypted_hex)
result = []
for i, x in enumerate(enc):
k = ord(key[i % len(key)])
v = x - (k % 5)
result.append(chr(v ^ k))
return ''.join(result)
```
**Flag:** `cryptx{m0b1l3_byp4ss3d_y0ur_w3b_k3y_1s_K9$eR2!vM7@pX4#c}` — *"mobile bypassed your web key is..."*
**Key Technique:** Android APK reverse engineering (jadx), identifying the correct decryption algorithm among decoys, cross-challenge dependency (master key from PCAP).
---
### 9. The Rogue Architect
| Field | Value |
|-------|-------|
| Category | Phase 2 |
| Flag | `cryptx{m4rg4d_ch7nn9ls_d04snt_l14}` |
**Description:** Track Kenji Tanaka's digital footprint, find his hidden portfolio, recover `Signal_Part2.wav`, and apply "perfect inversion" to reveal the flag.
**Hint:** *"Internet also has its own history. Find its time machine to go back through the time and find."*
**Solution:**
#### Step 1 — OSINT: Find the Portfolio
1. `@kenjitanaka512` on GitHub has one repo with README:
> *"All central servers are compromised. Wiped my old deployment notes. The Vault is secure. Everything is fully decentralized now, hidden in the mist."*
2. The hint references using the internet's "time machine" (Wayback Machine / web archives) to find deleted content.
3. There was a hidden **Gist** at [https://gist.github.com/kenjitanaka512](https://gist.github.com/kenjitanaka512) — recovered via wayback machine
#### Step 2 — Audio: Perfect Inversion
1. Both WAV files have identical properties: mono, 16-bit, 44100 Hz, 60 seconds, 2,646,000 samples.
2. **"Perfect inversion"** = phase cancellation / noise subtraction:
```python
result = Signal_Part1 - Signal_Part2 # subtract = invert + add
```
3. The subtraction cancels the shared noise (std drops from 6574 → 151), revealing a clean signal.
4. Generate a spectrogram of the result:
```python
import wave, numpy as np
import matplotlib.pyplot as plt
# Load both files
with wave.open('Signal_Part1.wav', 'rb') as w:
p1 = np.frombuffer(w.readframes(w.getnframes()), dtype=np.int16).astype(np.float64)
with wave.open('EVIDENCE_RECORDING_404.wav', 'rb') as w:
p2 = np.frombuffer(w.readframes(w.getnframes()), dtype=np.int16).astype(np.float64)
result = np.clip(p1 - p2, -32768, 32767).astype(np.int16)
fig, ax = plt.subplots(figsize=(30, 8))
ax.specgram(result, Fs=44100, NFFT=4096, noverlap=3600, cmap='inferno', scale='dB')
plt.savefig('flag_spectrogram.png', dpi=200)
```
5. The spectrogram clearly reveals the flag text in the 2000–6000 Hz band.
**Flag:** `cryptx{m4rg4d_ch7nn9ls_d04snt_l14}` — *"merged channels doesn't lie"*
**Key Technique:** OSINT (GitHub, Notion, Wayback Machine), audio signal processing (phase cancellation / noise subtraction), spectrogram analysis.
---
## Phase 3
### 10. The Ronin's Citadel
| Field | Value |
|-------|-------|
| Category | Phase 3 |
| Type | dynamic_iac |
| Flag | `cryptx{4dm1n_35c4l4t10n_k3y_ch1m3r4}` |
**Description:** The final challenge. Kenji Tanaka stripped his own admin privileges before disappearing. The Chimera Vault web portal is online — find the remaining access point, reclaim admin control, and unlock the vault.
**Solution:**
#### Step 1 — Login as Kenji Tanaka
Using credentials gathered from previous challenges:
- **Email:** `kenji.tanaka@chimera.local` (from GitHub commit history, using `@chimera.local` domain found in the app's chat system)
- **Password:** `K9$eR2!vM7@pX4#c` (the "web key" extracted from The Breach flag)
Login via the `/api/auth/sign-in/email` endpoint succeeds. Kenji's role is `security_engineer` (admin privileges stripped, as the challenge description states).
#### Step 2 — IDOR on User Profile Endpoint
The security dashboard contains an "IDOR Detection Pattern" alert, and `profile.js` reveals a vulnerable endpoint:
```
GET /api/users/profile?email=<any_email>
```
Any authenticated user can query any other user's profile by changing the `email` parameter. Querying `admin@chimera.local` reveals the admin profile:
```json
{
"email": "admin@chimera.local",
"fullName": "System Administrator",
"accessLevel": "Executive / Root",
"securityNotes": "Temporary hotfix deployed for the auth module. If a rollback is needed, reference incident bundle: /api/4f8c2a91d7e3b6f0a1c9e5d2b7f4a8c1"
}
```
The `securityNotes` field leaks a hidden API path.
#### Step 3 — Download the Auth Module Incident Bundle
Accessing `/api/4f8c2a91d7e3b6f0a1c9e5d2b7f4a8c1/` reveals a directory listing containing `auth-module.zip`. The archive contains:
| File | Purpose |
|------|---------|
| `src/security/passwordCrypto.ts` | AES-256-CBC encryption (reversible, not hashed!) |
| `src/config/env.ts` | Config loading — `APP_SECRET` env var used as encryption key |
| `data/chimera.db` | SQLite database with user credentials |
| `data/server.log` | 100K-line access log with embedded diagnostic data |
#### Step 4 — Extract the APP_SECRET from Server Logs
`passwordCrypto.ts` shows passwords are encrypted with AES-256-CBC using `APP_SECRET` as the key (passed through SHA-256):
```typescript
function getKey(): Buffer {
const secret = authConfig.APP_SECRET || "dev_secret_123456";
return crypto.createHash("sha256").update(secret).digest();
}
```
The server log contains diagnostic requests to `/api/internal/diag?seq=N&val=<base64>`. Most are noise from external IPs, but **4 requests from the internal Kubernetes pod IP `10.42.0.15`** (identifiable by `curl/7.81.0` user-agent) carry the real secret fragments:
| seq | Base64 Value | Decoded Hex |
|-----|-------------|-------------|
| 1 | `M2Y2YjJhOGQ=` | `3f6b2a8d` |
| 2 | `OWMxZTRmN2E=` | `9c1e4f7a` |
| 3 | `NWI4YzJkNmU=` | `5b8c2d6e` |
| 4 | `OWYxYTRiN2M=` | `9f1a4b7c` |
Concatenated: **`APP_SECRET = 3f6b2a8d9c1e4f7a5b8c2d6e9f1a4b7c`**
#### Step 5 — Decrypt the Admin Password
From `chimera.db`, the admin account (`admin@chimera.local`, role `admin`) has encrypted password:
```
Gc7/uKtLjYMC/rUvyO+TOg==:BT8cgBDdoDlOk881UdxkHQBkpPqt55ejYNQbIMdgrzQ=
```
Format is `base64(IV):base64(ciphertext)`. Decrypting with the recovered APP_SECRET:
```python
from Crypto.Cipher import AES
import hashlib, base64
secret = "3f6b2a8d9c1e4f7a5b8c2d6e9f1a4b7c"
key = hashlib.sha256(secret.encode()).digest()
iv = base64.b64decode("Gc7/uKtLjYMC/rUvyO+TOg==")
data = base64.b64decode("BT8cgBDdoDlOk881UdxkHQBkpPqt55ejYNQbIMdgrzQ=")
cipher = AES.new(key, AES.MODE_CBC, iv)
password = cipher.decrypt(data) # Remove PKCS7 padding
```
**Admin password:** `A9v!Q2m#L7x@R4p$T8k`
#### Step 6 — Login as Admin and Retrieve the Flag
Login with `admin@chimera.local` / `A9v!Q2m#L7x@R4p$T8k` grants admin access. The `chat.js` file reveals an admin-only system message loaded from `/api/flags/`:
```javascript
async function loadSystemMessages() {
const res = await fetch('/api/flags/', { credentials: 'include' });
const data = await res.json();
const sysData = data.flags?.[0];
// Renders in admin-internal channel:
// "Admin access verified. Your escalation key proof is: <flag>"
}
```
Fetching `/api/flags/` as admin returns the flag.
**Flag:** `cryptx{4dm1n_35c4l4t10n_k3y_ch1m3r4}` — *"admin escalation key chimera"*
**Vulnerabilities Exploited (matching the in-app Security Audit PDF):**
1. **IDOR (Insecure Direct Object Reference)** — Profile endpoint leaks any user's data including admin security notes
2. **Weak Credential Storage** — Passwords encrypted with AES (reversible) instead of hashed with bcrypt/Argon2
3. **Secret Leakage in Logs** — APP_SECRET fragments leaked via diagnostic endpoints in server logs
4. **Flat Permission Model** — No server-side RBAC enforcement on sensitive endpoints like `/api/flags/`
**Key Technique:** Web application exploitation chain — IDOR → secret extraction from logs → AES password decryption → admin privilege escalation.
---
*CryptX CTF 2026*