← Back to Writeups
CUSTOMMediumReversing

algorave

XESXOR9/2/20262 min read
#reversing#custom#medium#base64_decoding#memoization_fix#recursive_function_analysis#visual_flag_extraction

algorave

Platform: CUSTOM | Category: Reversing | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: base64_decoding, memoization_fix, recursive_function_analysis, visual_flag_extraction

Summary

Walkthrough of the CUSTOM Reversing algorave. Task: extract a flag from a Strudel.cc live coding music challenge with browser-crashing recursive JavaScript. Solution: add memoization and depth limiting to the recursive signal function, run the fi

Description

The challenge provided a link to an HTML file that redirected to strudel.cc (a live coding music platform). The HTML contained base64-encoded JavaScript code that generated music using the Strudel library.

Challenge URL: https://2025.ctf.link/assets/files/algorave-c649c74aa5785a46.html

Analysis

Initial Investigation

The HTML file contained a meta refresh redirect to strudel.cc with base64-encoded JavaScript in the URL fragment:

html
<meta http-equiv="refresh" content="0; url=https://strudel.cc/#base64_encoded_code_here">

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Code Structure

After decoding the base64, the JavaScript code revealed:

  1. A large notes array containing musical note patterns like note("[c4,f#4]"), note("[c4,c#4,d4,d#4,e4,f4,f#4]"), etc.
  2. A recursive function f(k) inside a signal() call that computed indices into the notes array
  3. A pianoroll visualization component

The Problem

The original recursive code caused browser hangs due to infinite/deep recursion:

javascript
rand = signal(t => {
  const f = k => {
    if (silence._steps.gte(k)) return k;
    const a = cycleToSeconds(f(id(k-silence._steps))+scramble(notes[Math.floor(t)]).length, ...);
    return _mod(a + ... + f(constant(k - parseFractional(notes[4]._steps.n), getFrequency)), notes.length);
  };
  return f(Math.floor(t));
});

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

The function f(k) called itself recursively without proper base cases, leading to stack overflow or infinite loops when executed in the browser.

Solution

Step 1: Fix the Recursive Function

Added memoization and depth limiting to prevent browser hangs:

javascript
const memo = {};
const maxDepth = 500;

rand = signal(t => {
  const floorT = Math.floor(t);
  
  const f = (k, depth) => {
    if (depth > maxDepth) return 0;
    if (k <= 0) return 0;
    
    const memoKey = k + '_' + depth;
    if (memo[memoKey] !== undefined) return memo[memoKey];
    
    if (silence._steps.gte(k)) {
      memo[memoKey] = k;
      return k;
    }
    
    const a = cycleToSeconds(
      f(id(k-silence._steps), depth+1) + scramble(notes[Math.floor(t)]).length,
      ...
    );
    const result = _mod(
      a + ... + f(constant(k - parseFractional(notes[4]._steps.n), getFrequency), depth+1),
      notes.length
    );
    
    memo[memoKey] = result;
    return result;
  };
  
  return f(floorT, 0);
});

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 2: Run the Fixed Code

With the memoization fix applied:

  1. Opened strudel.cc in browser
  2. Pasted the modified code
  3. Started playback

Step 3: Extract the Flag

When the code ran correctly, the pianoroll visualization displayed letters forming the flag. The letters appeared in the bottom-right corner of the visualization as the music played.

By examining the pianoroll visualization over time, the flag characters were revealed sequentially through the musical note patterns.

References

  • Strudel.cc - Live coding music platform
  • Algorave - Algorithmic rave music movement ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key Takeaways / Lessons

  • Mastering base64_decoding, memoization_fix, recursive_function_analysis, visual_flag_extraction was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2025-12-29 • CUSTOM • algorave • Difficulty: Medium