← All blogs Sagnik Ghosh

Running a 24-Hour CTF From Scratch: Lessons from "Hack With Jolu 2026" & AI in CTFs

Introduction

When our college cybersecurity club, Z3R0L0G0N, decided to host our own Capture The Flag event, I knew that leading it as CTF lead would mean a lot more than just writing a few challenges. Hack With Jolu 2026 ran for 24 hours. It was just a small team, but we wanted to build something the community would genuinely enjoy playing and actually learn from. This was also my first time hosting a CTF for my college cybersecurity club.

Here's what it took to pull it off, what went wrong along the way, and what I'd do differently next time.


Setting Up the Infrastructure

The backbone of the event was two separate AWS EC2 servers, split by purpose rather than crammed into one box. One ran a CTFd container, while the other ran the challenge containers. AWS was a perfect fit because a new account comes with free credits (100), which was more than enough for a CTF of this scale.

Main Server

After starting an EC2 instance, I SSH'd into it, pulled the CTFd repo from GitHub, set up Docker, built the image and ran the container. This server handled registration, the scoreboard, the challenge frontend and the whole player-facing platform. Once it was set up, we spent time working through the theming and customization options for the event homepage. That covered everything the platform needed to run smoothly for the full 24 hours.

Challenge Server

I started a second EC2 instance to run the challenge containers. I created a dedicated folder at /opt/challenges, pulled the challenges from a GitHub repo and ran them in Docker containers.

For each challenge, I had to manually configure the security group every time I wanted to expose or hide it. I want to automate this next time I host a CTF.

The First Mistake

The challenges pulled their configuration from a .env file. However, the repo had been published with a generalized .env.example instead of the real .env. After pulling the repo, I forgot to swap in the actual .env file. As a result, one web challenge served the example flag (test{flag}) because of incorrect environment variables.


My Challenges

I designed and built multiple challenges across several categories. For detailed writeups and challenge files, visit the repo. Some notable ones are listed below along with their ideas.

1nsp3ctor

Category Web
Difficulty Easy
Vulnerability Exposed Headers

Inspection can lead to deception, only if you're not paying attention.

Idea: This challenge tests whether players know their HTTP methods beyond GET and POST. A plain GET request to the endpoint returns nothing, so the real solve path starts with recon across methods.

Sending OPTIONS doesn't return the usual Allow header, so players can't just enumerate the supported methods the easy way. This is the "deception" hinted at in the description. Sending a TRACE request instead reveals a custom FTag header containing part of the flag, along with other custom headers. Querying the endpoint with different HTTP methods leads to different endpoints, each holding another piece of the puzzle.

Finally, a normal GET request only returns the flag if it includes the custom headers disclosed earlier in the TRACE response. The player has to chain it together:

TRACE → capture headers → replay them on GET → get the flag

Custom-Auth

Category Web
Difficulty Easy
Vulnerability Broken Access Control, Reversible Token

A developer decided to implement their own custom authentication token mechanism. Everything seems to work perfectly…

Idea: The login page exposes default credentials in the page source. Logging in with them returns an auth-token cookie in a custom-looking format. Feeding that token into CyberChef reveals it's just Base64 with the trailing = padding stripped off. Decoding it (after re-adding the padding) shows the plaintext value guest.

The final step is realizing the "custom" token is really just Base64-encode-and-strip-padding applied to a role name. The player crafts the same encoding for admin, swaps it into the token and sends it, which grants access to the admin area where the flag sits.

Role-number 1

Category Web
Difficulty Hard
Vulnerability IDOR, Weak JWT Signing Secret

A promising startup has just launched its brand-new customer portal after months of development. Everything looks polished, and the team is eager to impress its first users. But can it impress you?

Idea: This challenge tests whether players can look past a polished front end and dig into how the application actually handles authentication and authorization. The portal offers a clean, professional-looking registration and login flow. Players sign up, log in, and land on a personal dashboard served from an endpoint like /dashboard/<id>, showing their own profile details.

The obvious first move is to try IDOR by changing the ID in the URL to 1 and hoping to see someone else's data. This fails, because the server checks that the requesting user's session actually maps to the requested ID. This is the deliberate red herring: the app looks vulnerable to a classic IDOR, but the endpoint is properly access-controlled at first glance.

The real vulnerability lies one layer deeper, in how the session is established. Inspecting the auth token reveals it's a JWT. Running it through a JWT-cracking tool (e.g. jwt_tool, hashcat, or john with a wordlist) shows the signing secret is weak and crackable. Once the secret is recovered, the player can forge a brand-new, validly signed JWT with the user_id (or equivalent claim) changed to 1. That is the "role number 1" hinted at in the challenge name, which turns out to be the admin account. Presenting the forged token grants access to /dashboard/1, where the flag is waiting.


Insights

  • No outage on infrastructure: There was no downtime on either EC2 server for the full 24-hour event, confirming the two-server split (CTFd platform + Docker challenge host) was correctly sized for the load (thousands of unique IPs).
  • AI influence on writeup quality: When we asked for writeups after the event, a significant number of participants had used AI to write them, making it hard for us to separate the genuine ones from AI slop.
  • AI in CTFs: While reviewing the writeups, I noticed that many participants went completely "dumb mode" with AI. It was used everywhere, even in the simplest of places. Here are some examples.

Example 1: Fixing Magic Bytes

This is a very basic challenge that I've seen in many CTFs. You fix the magic bytes of an image, render it, and get the flag from there. I always do it with a hex editor, but this writeup was something else. Total slop, if you ask me.

Example 2: Custom Base62 Decoder

Someone wrote a custom Base62 decoder in Python instead of just using CyberChef.

...and many more. Honestly, reviewing these writeups with my team was the funniest and most amusing part of the whole event.

So, How Much AI Is Acceptable?

It raises a real question: how much AI usage should be acceptable for participants?

My take: AI should only be used for direction when you're stuck, not as the first move. Try it yourself first. If nothing clicks, ask AI for a nudge, but don't lean on it to solve the whole challenge. This event didn't even have prizes on the line, and people still leaned on AI to get through it. That's a risky habit to build early, especially for beginners who need the struggle to actually learn.


Conclusion

Hack With Jolu 2026 wasn't the most technically impressive CTF infrastructure-wise, and it didn't need to be. What it taught me was less about scaling servers and more about the discipline of actually shipping something end-to-end: designing challenges, standing up infrastructure, and owning the mistakes that only show up once real players are hammering on it at 2 a.m.

Running an event with zero budget and a two-server setup held together by patience taught me more about operational security thinking than any single CTF challenge could have.

Next time, the servers will be the same. The .env files, hopefully, will not be forgotten.

I hope you learned a thing or two about hosting a CTF as well.