> For the complete documentation index, see [llms.txt](https://borgor-1.gitbook.io/borgorshop/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://borgor-1.gitbook.io/borgorshop/events/umcs-2026.md).

# UMCS 2026

**2-time reigning champion of UMCS CTF**

How? It wasn't just dumb luck. We spent 2 weeks prior to the CTF preparing for it. Coming from a team of diverse experiences, each of us had something to contribute. And by diversified experiences, I don't just mean different categories. I mean, our real-world experiences differed. From GCC 2025 and GCC 2026, to Rui Yi's participation in ICC 2025 and Eschaton Finals in Kerala, and not to mention the fact that we're no strangers to Attack & Defence-style CTFs, we had different ideas and implementations of what to bring.

Here are some of the tools that brought us to the top of the leaderboard during UMCS 2026:

&#x20;

## Challenge dumping and Tulip (TCP-dump)

### Challenge dumper

The idea is that, in Attack & Defence, there is a set of red-team-focused challenges that is served using Dockers (or just containers). Different points are awarded based on the status of the container; if your container remains active and stays up (SLA), then your points remain; if your container is down, you lose points.

Likewise, when you attack someone else, you would gain points. And to stop someone from attacking you (and therefore, not give them your points/flags), you would patch the vulnerability in your system to close that vulnerability.

The issue arises when you patch your challenge, and the game server is no longer able to check on its status. When that happens, your challenge is considered down because your challenge is failing the game server's SLA checks. The SLA payload varies depending on the technical team and the challenge itself, so there's no one-size-fits-all answer here.

From this, you can figure out that there is a meta-strategy. If the SLA for the challenge is down, we need to find a way to bring it back up. We can either undo the changes or, better yet, restore all configurations to the original settings. And how do we do that?

A challenge dumper. We made and used a challenge dumper. It’s a custom script we built that essentially copies the files from the challenges and uploads onto a private GitHub.

Most Attack & Defence CTFs require you to use a VPN and SSH into their machines, where you will do all the A\&D stuff (attack, submit, patch). Therefore, we made our script to SCP files from the machine to our GitHub.

The rationale behind this is that if your patch fails and you lose SLA points, you could immediately re-push all the original files back onto the machine to revert and save yourself. Essentially, we just want to minimise loss.

This idea would've been miraculous; unfortunately, access to GitHub was restricted that day, so we couldn't really put this to use fully. The files were still pulled locally, so at least we had that, but there was no need to use it since we didn't commit to patching our systems.

### Tulip

Tulip, on the other hand, is an integrated tool. Essentially, a glorified tcpdump. We just want to monitor traffic to see what payload is tried against us. If a team has been successful in attacking our challenge, and we have yet to exploit it, at the very least, we can steal their exploit (payload) and use it for ourselves.

However, like the challenge dumper, it wasn't fully put to use as we were the team to first-blood 4/5 challenges during the competition.

&#x20;

## Automated Runner

When you're in a competition to pwn challenges and submit flags, how do you:

1. attack more than 5 teams during that tick
2. attack more than a single challenge within the same tick
3. submit all those flags WITHIN the same tick?

&#x20;

If you answered "with a script", then you're correct. We had some structure to the way we managed our scripts. Essentially, we named our scripts like `challenge_exploit.py`. With the syntax of the script being `python challenge_exploit.py <ip_address>`, it would just return the flags for that machine.

So then, we structured our runner script to do the following:

1. run all the scripts under `exploits/`
2. provide the IP address of all the machines to pass as arguments into the exploit scripts
3. cache all the flags from each machine pwned, and submit them to the game server

Additionally, to prevent other teams from using their own tcpdump setup to steal our payload, we added an obfuscator. With all this, we just had to make the exploit scripts during the competition.

There were also some minor adjustments that made our runner more efficient. During the competition, there were 25 teams, each with 5 challenges. Each challenge also contained 3 active flags. If you do the math, that's 375 flags per tick! Even if your script is perfect, if the server lags, you are LOSING that flag.

Therefore, my solution was to have multiple workers that worked on a smaller range of IP addresses, and each worker is designated for 1 challenge only. Meaning, `chall_A` would have 5 workers to itself, and `chall_B` would have its own set of 5 workers. Each worker would then exploit only 5 IP addresses, like `worker_1` pwns 10.0.1.1 until 10.0.5.1, then `worker_2` pwns 6-10, etc. This is vastly more time-efficient because within a short time, I'm able to pwn and submit all the flags before the tick ends. During the competition, this design proved vital, as the web challenge Postcards had an issue where the exploit script would hang unresponsively for a long time by design.

On top of that, Rui Yi had other ideas of an automated runner. His idea worked in tandem with Tulip, and it was to use S4DFarm to run the exploit scripts from a GUI. This GUI had a granular control because it allowed you to select which scripts to run.

Unfortunately, during the competition, my runner script had errors submitting the flags :skull:, so I had to default to simple bash `while true` and `for loop` scripts to run Whymir's script. It's just a funny incident I thought I should mention. Everyone's so used to using AI to write scripts that we forgot how to do it by hand. I had to Google the right syntax for a simple looping script.

## My Thoughts?

Overall, this A\&D CTF was very fun. There were some complaints I heard, but after hearing the trouble and the amount of work put into it, I couldn't help but be grateful for the opportunity to play Whymir's setup.

The whole anti-AI rule definitely threw us back to the Stone Age because it meant we couldn't just AI-slop everything and had to get down and dirty.

It was also really stressful because we didn't know that the points were dynamic, meaning the points you earn per flag would dwindle over time. Whymir said this was to discourage first-blood dominance, but we only found out from him after an hour of panicking because we thought 2nd place was about to catch up to us.

I know I said it before that CURTINv3 was meant to be my last CTF, but we couldn't help ourselves. I'm glad that my team, C0UGH1NGB4BY, got to close out another CTF as reigning and 2-time UMCS Champions

&#x20;
