Kot
Kot
Platform: Alfactf | Category: Web | Type: Challenge | Difficulty: Easy | OS: NA | Author: D3v0o0Nu11 | Date: 2026-04-25 | Status: Solved Techniques: client_side_source_inspection, js_deobfuscation, xor_decryption
Summary
Task: a browser runner game hides the reward behind a 1000-point requirement in obfuscated client-side JavaScript. Solution: inspect the bundled code, recover the XOR-based string decoder and decrypt the embedded flag directly without playing.
Recon
Port scan
bash read-only • copy allowednmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
| Port | Service | Version | Notes |
|---|---|---|---|
| <PORT> | <SVC> | <VER> | <notes> |
Enumeration highlights
- Event:
alfactf| ID:20260425_alfactf_kot - Tags: game_hacking, javascript, source_code_analysis, client_side, xor, obfuscation
- Indicators: single-page app with all game logic in a bundled JS file, unrealistic score gate at 1000 points, frontend helper that XOR-decodes arrays into strings, flag reveal triggered entirely on the client side
- Source:
20260425_alfactf_kot.md
Foothold
Vulnerability / Misconfiguration
- client_side_source_inspection
- js_deobfuscation
- xor_decryption
bash read-only • copy allowed<command>
Exploitation
- See original writeup content for detailed exploitation.
Privilege Escalation
Enumeration
bash read-only • copy allowedsudo -l find / -perm -4000 2>/dev/null getcap -r / 2>/dev/null cat /etc/crontab ps aux
Exploitation
- N/A for challenge-type writeup; see exploitation above.
- Flag obtained via challenge solve.
bash read-only • copy allowed<command>
Flags
| Flag | Location | Value |
|---|---|---|
| flag | REDACTED |
Key Takeaways / Lessons
- client_side_source_inspection
- js_deobfuscation
- xor_decryption
- Tags: game_hacking, javascript, source_code_analysis, client_side, xor, obfuscation
Original Writeup
<details><summary>Click to expand original content</summary>Kot — AlfaCTF
Description
Низкополигональный раннер с котом, рюкзаком и очень длинной дорогой.
English summary: the target serves a small browser runner game. The obvious objective is to survive long enough to reach the score threshold and reveal the flag.
URL: https://cat-k4sl0sey.alfactf.ru/
Analysis
Recon
Initial enumeration showed a static single-page application:
GET /returned an HTML page that loaded/assets/index-yflPIC15.jsand/assets/index-BREGg8EB.cssrobots.txtonly containedUser-agent: *andDisallow: /- requests to common leftovers such as
.gitor.envreturned the same app shell instead of useful files
That strongly suggested the interesting logic lived in the client bundle.
Client-side game logic
After inspecting and deobfuscating the JavaScript bundle, the important constants and checks became clear:
javascript read-only • copy allowedns = 0x3e8; function cs(arr, key) { return arr.map((v, i) => String.fromCharCode(v ^ (key + 3 * i))).join(''); } flag: () => [ cs([0x57,0x55,0x5a,0x5e], 0x36), cs([0x42,0x51,0x4a,0x31,0x2c,0x2b,0x14,0x2d,0x30,0x20,0x8,0x33,0x33,0x3f,0x2,0x39,0xb,0xd,0xc,0x19,0x5,0x19,0x18,0x15,0xfc], 0x39) ].join('')
And the score check used that generator directly:
javascript read-only • copy allowedif (state.score >= ns && !state.f0) { state.f0 = true; setBannerText(us.flag()); }
So the challenge was not about winning the game legitimately. The flag was already present in the bundle, only lightly hidden behind a custom XOR-based decoder and a score gate.
Extra observations
The bundle also contained cheat mechanics:
catcatcat→ FASTERpowerup→ GODMODE- the cheat panel unlocks after score
500
These were interesting hints that the game was meant to be inspected rather than played honestly, but direct flag recovery was still faster.
We also checked a similar CTFBase writeup, 20260109_duckerz_task78_polet_normalny, which reinforced the same pattern: when a web game keeps flag logic on the frontend, inspect and decode the bundle instead of grinding points.
Solution
The helper function applies XOR to each array element with a position-dependent key:
text read-only • copy alloweddecoded_char = value ^ (key + 3 * index)
Decoding the two arrays from us.flag() reconstructs the full flag.
python read-only • copy allowed#!/usr/bin/env python3 def cs(arr, key): return ''.join(chr(v ^ (key + 3 * i)) for i, v in enumerate(arr)) flag = ''.join([ cs([0x57, 0x55, 0x5a, 0x5e], 0x36), cs([ 0x42, 0x51, 0x4a, 0x31, 0x2c, 0x2b, 0x14, 0x2d, 0x30, 0x20, 0x08, 0x33, 0x33, 0x3f, 0x02, 0x39, 0x0b, 0x0d, 0x0c, 0x19, 0x05, 0x19, 0x18, 0x15, 0xfc ], 0x39), ]) print(flag)
Output:
text read-only • copy allowedalfa{REDACTED}
This is enough to solve the challenge without modifying score values or using the in-game cheats.
Lessons Learned
- Client-side gates are not security boundaries.
- Obfuscation only slows reading; it does not protect embedded secrets.
- For browser games, inspect the bundle first before spending time playing.
signed by XESXOR