Hackvent 2023 was a ton of fun, and this year I made it through 22 of the 24 challenges (25 of 27 counting hidden challenge), only running out of time on two of the final three. The first seven plus a hidden challenge had QRcodes, Geek Codes, a Grille Cipher, a very simple RE challenge, image editing, memory analysis, steg, and a flag hidden in HTTP chunk metadata.
HV23.01
Challenge
HV23.01 A letter from Santa
Categories:
FUN
Level:
easy
Author:
coderion
Finally, after 11 months of resting, Santa can finally send out his presents and challenges again. He was writing a letter to his youngest baby elf, who’s just learning his ABC/A-Z’s. Can you help the elf read the message?
Hint #1: ■■■■■■ ■ ■■■■■■■ ■■■■■■■
The challenge scope includes only the main website on port 443, which you get linked to in the Resources. Do not attack other ports.
Start the website from the Resources and get the flag.
Flag format: HV23{}
There’s a download named “Source” and a container resource I can spawn. The hint was added about 18 hours after the initial release.
Enumeration
Site
The site is a simple form:
The dropdown offers each lowercase ASCII letter:
I can enter any text in the “Your text here…” field.
If I enter “0xdf” and submit, the result looks like this:
The source is much longer than expected, with a ton of empty spans, with only a few having “0xdf” in them:
For GET requests, it sends index.html. For POST requests, it starts with a dictionary for each lowercase letter as the key, and an empty string as the value. Then it sets the selected character’s value to the user_input string. It renders the santa.j2 template, using ** as a dictionary unpack. This means that the dictionary of {"a": 1, "b": 2} when passed as func(**dict) is the same as calling func(a=1, b=2).
The santa.j2 template has all the spans noticed above, but they each have a template variable in them:
So when all but one of the values are the empty string, that explains the empty spans.
Analysis
Failings / Strategy
I went down a ton of rabbit holes. All the “a” and “b” made me think of Bacon encoding. Getting the variable in order and looking for various encodings. Trying to get the first characters to be “HV23{“. None of these led anywhere.
I did a lot of grepping to look for patterns, and there were two things that helped get me to the answer.
Frequency
I’ll use bash foo to look at the various spans. There are 625 spans matching the pattern, and that’s all the spans:
Rather than returning the single render, it loops over and does all the renders, appending each with a <br/> between then to give a new line. Now visiting that page gives the flag:
HV23.02
Challenge
HV23.02 Who am I?
Categories:
FUN
Level:
easy
Author:
explo1t
Have you ever wished for an efficient dating profile for geeks? Here’s a great example:
G d--? s+: a+++ C+++$ UL++++$ P--->$ L++++$ !E--- W+++$ N* !o K--? w O+ M-- V PS PE Y PGP++++ t+ 5 X R tv-- b DI- D++ G+++ e+++ h r+++ y+++
Flag format: HV23{<Firstname Lastname>}
Solution
The main challenge here is recognizing the format of this string. If I ask ChatGPT, it gives the answer:
Wikipedia has a nice page on Geek Code. It’s used to show other geeks what and how geeky you are. Unfortunately, there’s no name in ChatGPT’s output.
Searching for this string shows another site, the Geek Code Decoder:
On this site, it breaks down the code as well, similar to ChatGPT, but there’s a place where I notice a name:
While contemplating the grille and turning some burgers, Santa decided to send all the hackers worldwide some season’s greetings.
</picture>
Solution
Grille Cipher
The is a cipher called the Grille Cipher. It involves putting the message into a grid (like the one shown above), and then making an overlay sheet of paper with holes over certain squares. The decode the ciphertext, the reader simply records the values that show through, and then rotates the grid 90 degrees, showing a new set of characters.
Solver
dcode.fr has a decoder for Grille, but it doesn’t work with symbols, and seems a bit buggy to me. This one is really nice (with one issue). I’ll enter the grid and it shows it to me:
To set the Grille, I know the first letter must be “H”. So there can’t be any before that:
The next letter must be “V”, so that turns on the next block:
The next character has to be “2”, so it must be one of the two in green, and then one of the “3” in red:
For now I’ll just pick one of each, and also turn on the “{“:
It’s possible that this is the solution, but clicking “Decrypt” does not give a valid flag: “HV23{t02e3crvenrVryh”. I’ll try each of the four combinations of “2” and “3”, but none work. The issue is that the last character has to be “}”. That means after three rotations, it must be under a hole, and the last hole.
After three clockwise rotations, it will be in the bottom right corner:
That means I should turn that bottom corner on:
The resulting flag doesn’t look right: “HV23{mt02e3}8crven3rVryh”, for two reasons:
It’s kinda jibberish.
It seems to be rotating counter-clockwise (hence the “}” showing up at the end of the second group of six characters, not the last.)
I can fix the first issue by messing with which “2” and “3” I select. This configuration gives the necessary pieces:
The resulting flag is “HV23{mt2023}8ckven3rry_h”.
That is really six characters from each rotation:
“HV23{m”
“t2023}”
“8ckven”
“3rry_h”
If I had rotated the other way, it would reorder 1, 4, 3, 2, which gives the flag.
Flag: HV23{m3rry_h8ckvent2023}
HV23.04
Challenge
HV23.04 Bowser
Categories:
REVERSE_ENGINEERING
Level:
easy
Author:
veganjay
Santa has heard that some kids appreciate a video game as a christmas gift. He would rather have the kids solve some CTF challenges, so he took some inspiration and turned it into a challenge. Can you save the princess?
The download is bowser.elf, a 64-bit Linux executable:
oxdf@hacky$file bowser.elf
bowser.elf: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=bbd1c57f7e67e5dee6ce5838d7a271fc48812960, for GNU/Linux 3.2.0, with debug_info, not stripped
The initial binary had a flag format of HV2023{} rather than HV23{}, so there was an updated binary to fix that. I initially solved with the original binary, but I’ll show the updated on here.
Run Binary
Running the binary prints some ASCII art and then says I need to give it a password as an arg:
If I give it a password that’s wrong, it tells me:
oxdf@hacky$./bowser.elf 0xdf
...[snip]...
Sorry, that is not the correct password.
Reverse
I’ll open the binary in Ghidra. Everything of interest takes place main:
intmain(intargc,char**argv){intres;longin_FS_OFFSET;char**argv-local;intargc-local;uint8_t*c;uint8_tflag[75];longcanary;canary=*(long*)(in_FS_OFFSET+0x28);flag[0]=0xac;flag[1]=0x90;flag[2]=0x8d;flag[3]=0x8d;flag[4]=0x86;flag[5]=0xd3;flag[6]=0xdf;flag[7]=0x86;flag[8]=0x90;...[snip]...flag[68]=0x91;flag[69]=0x9c;flag[70]=0x9a;flag[71]=0x8c;flag[72]=0x8c;flag[73]=0x82;flag[74]='\0';bowser();if(argc==2){res=strcmp(argv[1],"mario");if(res==0){for(c=flag;*c!='\0';c=c+1){*c=~*c;}puts((char*)flag);res=0;}else{puts("Sorry, that is not the correct password.");res=1;}}else{printf("Usage: %s password\n",*argv);res=1;}if(canary!=*(long*)(in_FS_OFFSET+0x28)){/* WARNING: Subroutine does not return */__stack_chk_fail();}returnres;}
73 bytes are set on the stack in the “flag” variable (named from the binary, not changed by me). Then it seems to check for the argument “mario”, and if so, it loops over each byte until it reaches a null (at 74 bytes), inverting it (binary not), and then passes the string to puts.
If I run with “mario”, it doesn’t give a flag:
oxdf@hacky$./bowser.elf mario
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠲⣄⢀⡀⠀⠀⠀⠀⠀⠀⢀⠄⠀⣸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡄⠈⠳⣕⢄⠀⠀⠀⠀⢠⣏⠀⠀⣹⡆⠀⠀⠀⠀⠀⠀⣀⡀⣀⠀⠀⠀⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⢸⡿⡷⣄⣤⣾⣿⣯⣿⣿⣿⣧⡀⠀⠀⢀⠀⠀⠈⣻⣿⣻⢿⣶⢿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣽⠀⡖⣯⢳⣿⣿⣿⡟⠛⡞⣿⣽⣿⣿⣧⣼⠃⢸⣧⣷⣿⡟⣷⣯⡟⣾⢻⡞⣿⡆⠀⠀⠀⠀⢠⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠠⠤⣀⡀⠀⠀⠀⠀⣀⣼⣧⠽⠒⠋⠉⠉⠉⠉⠉⠙⠓⠿⠿⠛⠋⠉⣄⠀⢻⣿⣿⡿⣽⣳⢯⡿⣽⢯⡿⣽⣷⠀⠀⠀⠀⢸⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠱⡀⠀⠈⠉⢓⢾⣿⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣿⡄⠀⠐⢹⣿⡷⣯⢿⡽⣯⢿⡽⣷⣿⠀⢀⣤⣷⣼⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠘⢦⠀⣠⢯⡿⠋⠀⠀⠀⠀⢀⣀⠀⠀⢀⣠⣆⣴⡄⣀⠀⢄⠂⠄⡷⠻⣦⣤⣾⣿⣽⣯⡿⣽⢿⣾⡉⢏⡿⣿⣿⣻⣿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢿⣵⠟⠀⢀⡠⠔⠚⠉⣡⡈⠉⠉⠛⠻⣿⣿⣿⣷⣮⣦⣴⣾⣷⣿⠿⠿⠾⣌⣛⡟⠉⣻⣯⣿⣧⠨⣽⣿⣞⣿⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⣼⠏⠀⡔⠁⠀⠀⠀⣀⢴⣹⠶⢳⣀⠀⢻⣿⣛⡹⠿⠿⣿⣭⠝⠀⠀⠀⠀⠈⠹⣷⣤⣿⣈⣽⣻⠵⠿⠿⣭⣿⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⣠⣼⡟⠀⣸⠀⠀⠀⠀⣦⣾⣿⣿⣿⣿⡿⠟⠚⠋⢄⡀⠀⢰⠋⢳⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠈⠀⠀⠐⠋⣟⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⢄⡀⠀⠀⠀⠈⡷⡿⠀⠀⡇⠀⠀⢠⣮⣁⣽⣿⣿⠟⠋⠁⠀⠀⢀⠞⠻⣦⢾⣦⡾⠁⠀⢠⢶⣷⡀⠀⠀⠀⠀⠀⠈⣇⠀⠀⠀⣠⡾⣼⡟⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠉⠲⢤⣠⡴⣹⠃⠀⠀⣧⠀⢠⣾⣿⣿⣿⠏⠀⠀⠀⠱⣽⠞⢻⠦⡤⢿⣌⢿⣿⣤⠀⠈⣿⠿⣷⡄⣀⠀⠀⠀⣠⠹⣄⣠⠾⢋⡴⢇⢣⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠘⢷⡟⠀⠀⠀⣿⢤⠘⣿⣿⣿⡏⠀⢠⡀⠀⠀⣸⣷⢪⠝⣰⢃⡞⢮⣿⣿⡄⠀⢹⣶⣿⣿⣶⡴⢶⣿⣲⣯⣿⣿⡏⡙⣬⠼⠋⠀⠀⠀⠀⠀⠀⣠⡄⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢸⠁⠀⠀⣸⠇⢸⣿⠏⣿⣿⡁⠀⠀⢿⣆⡾⠀⣿⣇⠹⣆⢏⡸⢆⡈⣹⣷⡀⢸⠏⢸⣿⣿⣷⣿⣿⣿⣿⣿⣾⣇⣾⢀⣶⣆⣀⣀⣀⣰⠶⡿⢱⠎⣀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠸⠀⠀⢀⡏⢀⣿⣽⠲⢾⣿⡇⠀⠠⢜⢢⠟⣦⡼⢧⢋⡖⢎⡱⠮⢵⡏⡹⡇⠀⠑⣿⡿⠛⣿⣿⣿⣿⡿⣭⣟⣹⣿⣿⣾⣿⡟⢏⡱⢌⢣⡱⢣⣫⢖⢧⣋⠖⠄
⠀⠀⠀⠀⢠⠀⠀⠀⡘⠁⣼⡿⠁⠀⠀⠉⠛⠦⣵⣎⣦⠕⢊⣀⣊⣜⠸⣏⡛⡛⠞⡹⠳⣷⠀⠀⠀⠁⠀⠋⠉⠉⠉⠀⠻⣧⣿⣿⣿⣿⢣⡙⣌⠲⣩⢲⡱⣣⠏⣎⣓⡬⠆⠀
⠀⠀⠀⠀⠎⠀⠀⠠⠁⢠⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⡴⢢⡔⡿⠀⠑⠨⠙⠶⣥⣆⣑⠌⢣⡀⠀⠀⠀⢀⠀⣀⠂⣄⡾⢩⣿⣹⣻⣿⠋⠛⠛⠶⣇⢇⡚⡥⢞⡭⣚⠼⣱⡀
⠀⠀⠀⠀⣽⠀⠀⠄⢐⡾⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⣌⠳⣼⡅⠈⡲⢦⣉⠒⡰⢈⠉⡉⢉⣽⡷⣶⣟⡛⠻⢤⡃⠊⡤⣞⣿⣿⣿⣿⣆⠀⠀⣠⠞⢾⡴⡙⡮⠆⠉⢚⠀⠃
⠀⠀⠀⠀⣯⠽⠖⠖⢻⡁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠠⢌⠻⣜⢛⢦⡁⢆⡉⡙⠁⠂⠀⣴⡞⢯⡜⢧⡹⣛⣦⡀⠉⠓⠛⠶⠾⣿⣿⣿⣿⣷⣦⣽⣦⣤⠷⠋⠁⠀⠀⠀⠀⠀
⢀⣠⠴⠚⠁⢠⠠⡀⠼⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠐⢌⢣⡝⡌⠦⡉⢆⡐⠄⠁⣴⣞⠳⣜⢣⠞⣥⠳⣍⠞⣵⡀⠀⠀⠀⠀⠀⠉⠙⠛⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠘⠻⢦⣱⣌⢢⡑⣌⠲⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠌⡜⡲⠜⢨⠓⠈⢄⣠⣴⢛⢧⠪⡝⣌⢧⣋⠶⡹⢌⡻⢼⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠈⠉⠉⠉⢉⡇⠀⠀⠀⠀⠀⠀⠀⠀⡀⢢⠑⡬⢱⢩⠟⠙⠛⠛⠒⣳⢏⠶⣙⠼⣘⠦⣍⢮⡱⣍⣾⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⡎⠀⠀⠀⠀⠀⠀⠀⠄⢢⢅⢣⠚⡔⢣⠏⠀⠀⠀⠀⠀⣟⢎⡳⣉⠮⢥⢫⠴⣢⢓⢾⣁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⡸⠀⠀⠀⠀⠀⠀⢀⠇⡸⢃⠼⡘⠟⣸⠟⠀⠀⠀⠀⠀⢸⣛⡜⢣⡛⡼⣃⢟⡼⣣⢟⡻⢼⢧⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⢰⠃⠀⠀⠀⠀⠠⠨⣐⢪⢑⡋⣎⣱⠽⠃⠀⠀⠀⠀⠀⠀⣿⢄⡏⢧⡙⢶⠩⡞⢴⢣⠎⣽⡷⣿⣻⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⣾⠀⠀⠀⠀⠀⠀⠀⠀⠈⠈⣏⠉⣻⣆⠀⠀⠀⠀⠀⠀⠀⠈⠚⠾⠧⠾⠥⠿⠼⠾⠾⠽⠾⠓⠓⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠘⠦⣀⠀⢀⡤⠒⢦⣠⠖⠚⣟⡎⠙⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠑⠒⠤⠞⠻⠦⢄⡟⠋⠒⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
Sorry, your flag is in another castle.
The first thing I noticed is that “Sorry, your flag is in another castle.” is way less than 73 bytes. It’s 38:
oxdf@hacky$echo-n"Sorry, your flag is in another castle." | wc-c38
In fact, if I look at byte 38 in the flag, it’s 0xff:
When that is inverted, it will become null, which then ends the puts.
Recover
I want to see what comes after that. To initially solve, I’ll copy the bytes into a text file:
A much cleaner way to get the flag is just to make a copy of the binary and edit that 0xff byte. The instruction that sets it on the stack is at 10136a:
I’ll open the binary in a hex editor and find that similar offset. The 100000 is added by Ghidra, so I find it at 136a:
I’ll change that to0xf5, (which will invert into a newline) and save. Now when I run, it prints the flag on a new line:
oxdf@hacky$./bowser-mod.elf mario
...[snip]...
Sorry, your flag is in another castle.
HV23{You_Have_Saved_the_Princess}
HV23.05
Challenge
HV23.05 Aurora
Categories:
FUN
Level:
easy
Author:
monkey9508
The Northern Lights appeared at exceptionally low latitudes this year due to the high level of solar activity. But from Santa’s grotto at the North Pole, it’s not unusual at all to see them stretching across the sky. Snowball the elf tried to capture a video of the aurora for his Instagram feed, but his phone doesn’t work well in poor light, and the results were rather grainy and disappointing. Is there anything you can do to obtain a clearer image?
The attachment is a video, and it’s very grainy:
I can see the outlines of a flag in there across the top, but it’s not clean enough to make out at any point.
Solution - Dirty
My initial solution was to throw this into KDenLive and look for filters. With the “Denoiser” filter applied, and both “Spatial” and “Temporal” all the way up, I was able to get this image:
With enough staring at it, I was able to get the flag.
Flag: HV23{M4gn3t0sph3r1c_d1sturb4nc3}
Solution - FFMPEG
The noise is completely random, but the picture behind the noise is relatively static. If that’s the case, I can use ffmpeg to generate a bunch of images from the video, and then average them with ImageMagick.
There are other variations on this that work. convert can actually handle the video directly with convert aurora.mp4 -evaluate-sequence mean aurora.png.
Alternatively, I can use ffmpeg to break out the images and pass them to DeepSkyStacker.
HV23.06
Challenge
HV23.06 Santa should use a password manager
Categories:
FORENSIC FUN
Level:
easy
Author:
wangibangi
Santa is getting old and has troubles remembering his password. He said password Managers are too complicated for him and he found a better way. So he screenshotted his password and decided to store it somewhere handy, where he can always find it and where its easy to access.
Santa recommends the volatility profile Win10x64_18362
Solution
I’ll use Volatility3 to analyze the file. I can use the windows.info plugin to get information about the file:
oxdf@hacky$python /opt/volatility3/vol.py -f memory.raw windows.info
Volatility 3 Framework 2.5.2
Progress: 100.00 PDB scanning finished
Variable Value
Kernel Base 0xf8060c4b4000
DTB 0x1ad000
Symbols file:///opt/volatility3/volatility3/symbols/windows/ntkrnlmp.pdb/35A038B1F6E2E8CAF642111E6EC66F57-1.json.xz
Is64Bit True
IsPAE False
layer_name 0 WindowsIntel32e
memory_layer 1 FileLayer
KdVersionBlock 0xf8060c8d93c8
Major/Minor 15.18362
MachineType 34404
KeNumberProcessors 1
SystemTime 2023-11-18 13:35:07
NtSystemRoot C:\Windows
NtProductType NtProductWinNt
NtMajorVersion 10
NtMinorVersion 0
PE MajorOperatingSystemVersion 10
PE MinorOperatingSystemVersion 0
PE Machine 34404
PE TimeDateStamp Sun Aug 27 03:21:15 2090
I know I’m looking for an image file. I’ll use the windows.filescan.FileScan plugin to get a list of files from memory, saving it to a file to make it easily searchable:
Either of the resulting two files opens as an image:
Flag: HV23{FANCY-W4LLP4p3r}
HV23.H1
Challenge
HV23.H1 Kringle's Secret
Categories:
FUN
Level:
easy
Author:
wangibangi
Can you feel it? I feel like there’s a… hidden flag in one of the easy challenges!
Solution
The image from day 6 has a message hidden in it with stegonography. If I take the low bit of each color byte and use it to make a bit stream, it has the ASCII flag. StegSolve.jar does this nicely (as does a lot of online solvers):
Flag: HV23{no_ctf_without_stego}
HV23.07
Challenge
HV23.07 The golden book of Santa
Categories:
FORENSIC WEB_SECURITY
Level:
easy
Author:
darkstar
An employee found out that someone is selling secret information from Santa’s golden book. For security reasons, the service for accessing the book was immediately stopped and there is now only a note about the maintenance work. However, it still seems possible that someone is leaking secret data.
Solution
No matter what I send to the server, it just returns the same image. In fact, I don’t have to send valid HTTP verbs or even anything more than one character:
The weird thing about this response is that instead of a content length header, it’s using Transfer-Encoding: chunked. This works by sending chunks with each chunk starting with a hex length of that chunk (0x948 in the image above), and then that data, and then the next chunk. So 0x948 bytes later, there’s another chunk:
This next chunk is 0x956.
I’ve been playing Hackvent long enough to recognize a pattern that starts 0x48 then 0x56! That’s HV. The next chunk is 0x932:
And then 0x933:
That’s HV23!
I’ll use a simple Bash pipeline to get the flag:
nc to get the raw request (it doesn’t matter what I send, but a simple HTTP header looks nice), then grep to get lines starting with 9, then cut to get rid of the “9” character, then xxd to convert from hex to ASCII: