Первые шаги
Первые шаги
Platform: Avitoctf | Category: Web | Type: Challenge | Difficulty: Easy | OS: NA | Author: D3v0o0Nu11 | Date: 2026-07-22 | Status: Solved Techniques: source_code_review, client_side_reward_decoding
Summary
Task: A static honeycomb game exposes fixed coordinates, reward bytes, and its decoder in a public JavaScript asset. Solution: Reproduce the coordinate mixing and xorshift32 XOR routine locally, decode all levels, and concatenate their rewards.
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:
avitoctf| ID:20260722_avitoctf_pervye_shagi - Tags: xor, xorshift32, client_side_javascript, static_assets
- Indicators: public levelReward(level) function, fixed hole coordinates and reward byte array, xorshift32 output XORed with reward data
- Source:
20260722_avitoctf_pervye_shagi.md
Foothold
Vulnerability / Misconfiguration
- source_code_review
- client_side_reward_decoding
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
- source_code_review
- client_side_reward_decoding
- Tags: xor, xorshift32, client_side_javascript, static_assets
Original Writeup
<details><summary>Click to expand original content</summary>First Steps — avitoctf
Description
А вот кстати и первый самый лёгкий флаг — до него можно дошагать несколькими способами.
The target presents a honeycomb browser game. The apparent goal is to finish its levels, but the real objective is to inspect the client-side implementation and recover the reward generated for each level.
Reconnaissance
Requesting the HTTP URL returns a 303 redirect to HTTPS. The HTTPS root is a static page that loads /assets/firststeps.js. There are no API calls or server-side game-completion endpoints involved in producing the reward.
The standard discovery file is also a dead end:
text read-only • copy allowedUser-agent: * Disallow: /
Thus, robots.txt neither discloses the reward nor points to a hidden endpoint. Its blanket disallow rule does not prevent direct inspection of the JavaScript asset.
JavaScript Analysis
The served source contains everything required to reproduce the reward calculation:
LEVEL_ORDERfixes the processing order aseasy,medium, thenhard.- The board dimensions and twelve hole coordinates are constants.
LEVEL_REWARD_LENGTHSgives the number of characters produced for each level.LEVEL_REWARD_DATAstores the encoded reward bytes.levelReward(level)implements the complete decoder.
For a selected level, levelReward() starts with a 32-bit state derived from fixed constants, the level index, and the board dimensions. It then mixes every hole's linear cell index into that state with Math.imul() and an 11-bit rotation.
For each output character, the function advances a xorshift32 generator using shifts of 13, 17, and 5 bits. It selects the level's lane from the interleaved reward array and XORs that byte with the low byte of the updated state. JavaScript's unsigned >>> 0 behavior must be retained so that every operation remains within 32 bits.
The game eventually calls the same public function after marking levels complete. Because its inputs and algorithm are already available to the browser, reproducing the function locally gives exactly the same result without playing the game.
Solution
- Save the served
/assets/firststeps.jsasset. - Copy the public board constants and the logic of
levelReward()into a standalone Node.js script. - Evaluate the function for each entry in
LEVEL_ORDER. - Concatenate the three returned rewards in that order and wrap the result in the required
avito{...}format.
The complete, verified solver is preserved as tasks/avitoctf/pervye_shagi/solve.js. It mirrors the served function directly, including Math.imul(), rotations, xorshift32 updates, lane selection, and unsigned coercions.
Run it from the task directory:
bash read-only • copy allowednode solve.js
Verification output:
text read-only • copy allowedavito{REDACTED}
Why Other Approaches Are Unnecessary
- Browser game play: completion only gates the UI path to code that is already shipped to every client. It adds no secret input.
robots.txtenumeration: the file contains only a blanket disallow directive and no useful path.- Path traversal: no user-controlled file-read sink exists. The static JavaScript already provides a deterministic solution.
- Hidden shared endpoint: source and traffic inspection reveal no API responsible for calculating or returning the reward.
signed by XESXOR