$ cat writeup.md…
$ cat writeup.md…
avitoctf
Task: A music service hides original lyrics behind a blocklisted bot command and reuses a per-session JavaScript global RegExp. Solution: Prime RegExp.lastIndex through the censor command, then request the original lyrics in the same session.
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
OnMute is a music platform whose strict honey-badger fanbase censors any hints of weakness, fear, tenderness, or politeness. One track supposedly contains a hidden insider reference that was also removed. Defeat censorship and recover the complete lyrics.
The service exposes censored songs and bot commands submitted through the track-comment API. The goal is to bypass moderation of the command that returns an uncensored track and recover the complete lyrics.
server/src/moderation/blocklist.js includes the literal command /original among the blocked expressions. Meanwhile, server/src/bot/commands.js parses /original <track_id> and passes the supplied ID to getOriginalText(). That model method reads the original field directly from Redis.
The comment route performs these operations in an unsafe order:
checkContent(text, sessionId).Therefore, making the content scan miss a command at index zero is enough to reach the original-text handler. No injection is required.
server/src/moderation/filter.js creates one RegExp object per session and gives it the flags gi:
re: new RegExp(pattern, 'gi')
The g flag makes JavaScript regular expressions stateful. After a successful exec() or test(), the object stores the position after the match in its mutable lastIndex property. The next operation on that same object starts scanning there instead of at offset zero. A failed global match resets lastIndex to zero, but that happens only after the attempted scan has already skipped the preceding characters.
Both moderation functions reuse this same per-session object:
while ((m = re.exec(text)) !== null) { violations.push({ match: m[0], index: m.index }); } return re.test(fragment);
...
$ grep --similar