Black Helicopter Vibes CTF track - HackFest & CyberChess 2025

By 0xhamy 01:33 PM - October 31st 2025
Black Helicopter Vibes CTF track - HackFest & CyberChess 2025

Description

This challenge was part of HackFest 2025 (Canada) and CyberChess 2025 (Latvia) events, I contributed a track of challenges named "Black Helicopter Vibes" with 4 challenges:

Challenge Purpose Category
Bravo 6 Exploiting an internal file read vulnerability in vvveb 1.0.5 CMS Web
State Secrets A sound-based cipher where each distinct sound maps to an ASCII letter Cipher, Steganography
Arasaka A challenge focused on reverse engineering a binary to understand how it writes the flag reverse
Package 101 Exploiting IDOR & file upload filter (insecure design) to read internal files Web

In this blog, I am going to publish solutions for all challenges.


Bravo 6

Vvveb’s drag-and-drop website editor hides an internal file read vulnerability that can be triggered by tinkering with URL parameters. If you manage to pull it off, you’ll be able to peek at files you shouldn’t normally see, and one of them contains the flag.

The challenge runs on Vvveb version 1.0.5, which has plenty of other holes, but to keep things fair you’ll start with a low-privileged account. The vulnerability is tracked as CVE-2025-8519 and was discovered by 0xHamy.

Challenge description given to players

Login into the backend and read internal NPM config files to get the flag.

Solution

  1. Attacker must read all posts on site, the last post is by John Smith, clicking the author name will show all of his posts and the URl will reveal his username: http://127.0.0.1:8084/author/jsmith
  2. Attacker must use that username and xato-net-10-million-passwords-1000.txt wordlist to bruteforce password; password is 1q2w3e4r5t
  3. Login and click on "Edit website" on sidebar.
  4. Modify the URL to something like this: http://127.0.0.1:8084/admin/index.php?module=editor/editor&url=index.html
  5. And now you can browse internal files. Flag is inside, package.json

Flag

HackFest:
HF-L8CDCHOGRJ83M5FXXFGTGI8SC5LDAZYB

CyberChess: 
HF-G32F229XLX4G2TE8RAY9SPB8OIK3BEV9

Package 101

This web application contains a vulnerable user settings functionality that can be exploited via an Insecure Direct Object Reference (IDOR).

When a user attempts to change their password, the request includes:

  • A secret hash
  • Their username
  • Their password

The "secret hash" is derived directly from the user’s ID (via MD5), making it trivial to predict other users’ hashes.

An attacker can exploit this by resetting the admin’s password, then logging in as admin. Once logged in, the attacker can use the admin panel to approve their own account, which was initially unapproved.

The application is pre-seeded with three posts from the user blackbird, hinting that posting functionality exists but is restricted. By brute-forcing page names such as /post or /upload, an attacker can discover hidden endpoints—but these require approval to use.

Therefore, the attack path is:

  1. Exploit the IDOR in the settings functionality to reset the admin password.
  2. Log in as admin and approve the attacker’s own account.
  3. Log out of admin, then log back in as the approved attacker account.
  4. Use the upload feature to upload a malicious .html file. This file will be rendered, enabling further exploitation.
  5. Abuse the custom Jinja2 filter that reads files from the packages/ directory to leak sensitive files.

One such file is package.json, which contains the challenge flag.

Challenge description given to players

Explore the application and its documentation to uncover the vulnerabilities and read contents of one of the NPM config files to retrieve the flag.

Solution

  1. Register a new user account.
  2. Intercept a password change request from the settings page.
  3. Modify the secret parameter to use the MD5 hash of the admin’s user ID (1).
  4. Reset the admin’s password and log in as the admin user.
  5. From the admin panel, approve your original user account.
  6. Enumerate files and directories to discover the /DOCUMENTATION file, then read it to learn how the Jinja2 filter works.
  7. Log out of the admin account and log back in as your newly approved user.
  8. Go to /upload and upload a crafted .html file with this content {{ "package.json" | read_package_file }}
  9. Render the uploaded file and leverage the Jinja2 filter to read files in the packages/ directory.
  10. Access the contents of package.json to obtain the flag.

Flag

HackFest: 
HF-B58SKFZS99561RZG82YKO6Y4N9LT07K

CyberChess: 
HF-R342R4E2SFAHV8Q5HWGJT717NUUO38IU

State Secrets

This challenge is on a cipher that encodes text into sound by mapping digits to distinct audio clips. Each character is first turned into its ASCII code, then the digits of that code are replaced with corresponding sounds. Finally, these sounds are stitched together to create one continuous audio file.

Each digit 0–9 corresponds to a unique sound file:

separator = ding.mp3
1 = meow.mp3
2 = bark.mp3
3 = blaster.mp3
4 = roar.mp3
5 = chirping.mp3
6 = laughter.mp3
7 = crying.mp3
8 = clapping.mp3
9 = yawn.mp3
0 = engine.mp3

Challenge description given to players

A suspect has been hiding **audio-based ciphers** inside pirated movies. These ciphers usually appear during the intro or credits, disguised as background sounds. By uploading the altered files to torrent sites, the suspect communicates secretly with his handlers.

Investigators discovered that each **distinct sound represents a digit**. A single **ding** separates digits within the same set of numbers (e.g. 161), while **two consecutive dings** separate different numbers (e.g 321,412). Together, the sequence of sounds encodes text, and possibly the key to the case.

Your mission: **analyze the audio, decode the hidden cipher, and uncover the message.**

Solution

Each combination of sounds means one ASCII character or number, for example 110 would be the letter n.

  • For each character in the message, find its ASCII code.

  • Example:

    • "nation"110,97,116,105,111,110
  • Digits within the same ASCII code are separated by one ding sound.

  • Different characters (different ASCII codes) are separated by two ding sounds.

  • This ensures you can distinguish individual digits vs. new characters during decoding.

Flag

HackFest:
The pass code for bunker is my_cat_is_green

CyberChess:
Welcome to United Nations of Hackers!

Both the flags and digit-to-sounds were modified for these two events so even if you knew the previous solution you wouldn't have been able to solve it.


Arasaka

There is a C program here, it uses inline assembly and direct system calls to check certain conditions and then write the flag to /tmp/flag if all conditions pass.

Here are the conditions that need to be met for flag to be written on /tmp/flag:

  • it must check hostname of the current device and if it's arasaka then proceed
  • then check current date and time, if it's 17/06/2029 past 2 PM then proceed
  • check if user ghostsec exists
  • and last, after all checks pass, write flag to /tmp/ as user ghostsec

Challenge description given to players

Reverse engineer the following program to understand its logic flow, if the program runs successfully, it will write a flag to `/tmp/flag` and you will be able to read it.

Solution

To solve the challenge, players must reverse-engineer the obfuscated binary to uncover the hidden checks. Key steps include:

  1. Static Analysis: Disassemble the binary (e.g., using objdump -d obfuscated or Ghidra/IDA Free) to identify inline assembly syscalls (e.g., syscall instructions with numbers like 63 for uname, 96 for gettimeofday).
  2. String Decryption: Locate XOR-encrypted byte arrays (key=42) and decrypt them to reveal strings like "arasaka", "/etc/passwd", "ghostsec:", and "/tmp/flag". For example, decrypt the first array {75,88,75,89,75,65,75,0}: each byte XOR 42 yields "arasaka".
  3. Logic Flow: Trace the main() function's conditional branches (early return 1 on failures). Identify:
    • Hostname check: Compares decrypted "arasaka" with utsname.nodename from uname(63).
    • Time check: Uses gettimeofday(96) + localtime() to verify date > 2029-06-17 14:00.
    • User check: Reads /etc/passwd via open(2)/read(0), searches for "ghostsec:", parses UID.
    • Privilege switch: setreuid(113, -1, uid) to become "ghostsec".
    • Flag write: open(2) /tmp/flag (O_WRONLY|O_CREAT|O_TRUNC), write(1) decrypted flag.
  4. Environment Setup: Run as root (sudo), set hostname arasaka, add user ghostsec (e.g., sudo useradd -m ghostsec), ensure system date is post-2029-06-17 14:00.
  5. Run and Extract: Compile/run the binary; on success, cat /tmp/flag reveals the flag.

No dynamic analysis needed beyond running in the correct env; fails silently otherwise.

Flag

HackFest: 
HF-53Z65I5EUG0MO29HTJHVXJEFR0JM7C0H

CyberChess:
HF-33V0K4IR1E3KES7SIW1MRB4X7Q469TS0

Summary

All of my challenges were based on real-world vulnerabilities and practices, for example Bravo 6 was focused on exploiting a CVE found by myself, State Secrets was focused on using AI/LLMs for pattern/sound recognition and package 101 was focused on IDOR & template sandbox escape vulnerability.

Most CTFs focus on creating small application that you won't see in the real world at all, Bravo 6 on the other hand used a real CMS that's used by thousands of developers out there. Rest assured, everything in this CTF track is based on reality so if you completed these challenges, you now have the skills to hunt for vulnerabilities in real-world software.

For a CTF designer like myself, it was a really good CTF experience working with HackFest's team on-site in Quebec City and with CyberChess team from Latvia remotely. Together we were able to deliver both the infrastructure and challenges.

My contribution to HackFest and CyberChess also counts towards progress of one of our new missions: Delta Knowledge Transfer Program (DKTP). Through this mission, Delta Obscura's team members will work either as volunteers or contractors to contribute to CTF events across 10 countries.