Description
A Windows executable announces the flag but first takes a very long sleep. Use Frida to hook and bypass the Sleep call so the binary wakes up immediately and prints the flag.
Unzip with the password picoctf to obtain bininst1.exe. The challenge needs a Windows machine (or VM) with Python and Frida installed.
Install Frida tools: pip install frida-tools. You may also need the Visual C++ Redistributable from Microsoft.
Run the binary once to see what it says. It will announce the flag but then sleep for a very long time.
Create a JavaScript Frida script that intercepts the Sleep function in kernel32.dll and returns immediately instead of waiting.
pip install frida-tools# Run the binary to see the sleep behavior:bininst1.exe# The binary says it will print the flag but needs a nap firstSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1
Hook Sleep with a Frida scriptObservationI noticed the binary explicitly announces it will print the flag but then sleeps for an extended period, which suggested that bypassing the Sleep call in kernel32.dll via Frida's Interceptor.replace would skip the delay and let execution reach the flag output immediately.Write a Frida JavaScript file that locates Sleep in kernel32.dll and replaces it with a function that just returns immediately. Run the binary under frida to apply the hook.js// kill_sleep.js var sleep = Module.getExportByName("kernel32.dll", "Sleep"); Interceptor.replace(sleep, new NativeCallback(function(ms) { return; }, "void", ["uint32"]));bashfrida bininst1.exe -l kill_sleep.jsWhat didn't work first
Tried: Using Interceptor.attach instead of Interceptor.replace to hook Sleep.
Interceptor.attach lets you run code on entry and exit but does not prevent the original function from executing, so the binary still sleeps. Interceptor.replace swaps the entire function pointer with your NativeCallback, ensuring Sleep never actually waits.
Tried: Attaching Frida to the already-running bininst1.exe process with frida -n bininst1.exe -l kill_sleep.js after the binary has started.
Sleep is called very early in execution. By the time you attach by name the thread is already deep inside the Sleep wait, so the hook arrives too late. Spawning the binary through Frida with frida bininst1.exe -l kill_sleep.js guarantees the hook is in place before any code in the binary runs.
Learn more
Frida is a dynamic binary instrumentation toolkit that lets you inject JavaScript into running processes. It works by attaching to a process (or spawning one) and loading a JavaScript engine into it, giving you full access to the process memory, function hooks, and API interception. Frida is widely used for security research, reverse engineering, and CTF challenges on Windows, macOS, Linux, iOS, and Android.
Module.getExportByNamelooks up a named export from a loaded DLL by name.Interceptor.replacereplaces the function at that address with a customNativeCallbackwritten in JavaScript. The callback here accepts the millisecond argument Sleep normally takes and simply returns without doing anything - effectively making Sleep a no-op and letting the binary continue immediately.The Windows
Sleepfunction lives inkernel32.dlland pauses the current thread for the specified number of milliseconds. Malware and anti-analysis binaries commonly call Sleep with enormous values (hours or even days) to frustrate dynamic analysis tools that time out after a few seconds. Hooking Sleep is one of the first things analysts do when running a suspicious sample in a sandbox.Step 2
Decode the Base64 outputObservationI noticed the binary printed a string starting with 'cGljb0NUR', the recognizable Base64 prefix of 'picoCTF', which indicated the flag was Base64-encoded and a single base64 -d decode command would recover the plaintext flag.With Sleep bypassed, the binary wakes up and prints the flag as a Base64-encoded string. Decode it to recover the picoCTF flag.bash# The binary prints something like:bash# Ok, I'm Up! The flag is: cGljb0NURnt3NGtlX20zX3VwX3cxdGhfZnIxZGFfZjI3YS4uLn0=bash# Decode with:bashecho 'cGljb0NURnt3NGtlX20zX3VwX3cxdGhfZnIxZGFfZjI3YS4uLn0=' | base64 -dExpected output
picoCTF{w4ke_m3_up_w1th_fr1da_...}What didn't work first
Tried: Running strings on bininst1.exe to find the flag without using Frida.
The flag is Base64-encoded inside the binary's logic, not stored as a plain string in the read-only data section. strings will show the encoded form cGljb0NURn... only if the encoder is a literal, and even then Base64 text is easy to miss without context. The binary must execute past the Sleep hook to emit the encoded output at runtime.
Tried: Piping the binary's output through CyberChef using 'From Base64' with the URL-safe alphabet.
The binary's Base64 output uses the standard alphabet (A-Z, a-z, 0-9, +, /). CyberChef defaults to the standard alphabet as well, so this works, but choosing the URL-safe alphabet (- and _ replace + and /) would produce corrupted bytes. The command-line echo ... | base64 -d always uses the standard alphabet and is unambiguous.
Learn more
Base64 is a reversible encoding that represents binary data using 64 printable ASCII characters. The prefix
cGljb0NURis always the Base64 encoding ofpicoCTF, so any encoded picoCTF flag starts with it. Spotting this pattern is the fastest way to identify encoded flags in binary output or network captures.The binary encodes its output as Base64 to avoid the flag appearing as a plain string in the executable's read-only data section. A naive
stringsscan of the binary would not immediately reveal the flag, making the Frida instrumentation approach necessary rather than optional.
Flag
Reveal flag
picoCTF{w4ke_m3_up_w1th_fr1da_...}
Install Frida, write a one-function kill_sleep.js that replaces Sleep with a no-op, and run the binary under frida to get the Base64 flag output.