Hackvent 2024: Day 15
[HV24.15] Rudolph's Symphony
Rudolph has decided to organise a music concert as a special surprise for Santa this Christmas!
Four of the other reindeer have eagerly signed up to perform, but things aren't going as smoothly as planned. Unfortunately, the reindeer have been pranking each other and as a result they are having issues preparing their material.
Can you step in and help each reindeer get ready in time for the big event?
Analyze the resource and get the flag.
Flag format: HV24{}
sha256sum of rudolphs-symphony.zip: 8b1d38d37e8172e2c2694d52afc29de66ddb8fef760224cd6de4a4b39d8ee673
This challenge was written by mobeigi. Big effort for the big challenge.
File:
Hackvent 2024 - Day 15 - rudolphs-symphony.zipThis challenge was also written by myself!
Solution
Initial Analysis
We are presented with a zip file that contains four folders, one for each of the reindeer that have signed up to perform. The reindeer are Blitzen
, Prancer
, Comet
, and Dancer
.
Let's list all the files in all folders:
$ ls -LR
.:
Blitzen Comet Dancer Prancer
./Blitzen:
blitzen-passwords.kdbx flag.txt update.txt
./Comet:
update.jpg
./Dancer:
update.pdf
./Prancer:
Chrome.zip update.md
Note that each reindeer has an update
document (in varying file formats). Some reindeer also have additional files.
Blitzen
We begin the challenge by looking at Blitzen's files. There are other potential starting points, but this one is hinted at as the intended starting point by the CTF author.
To begin, we read Blitzen's update.txt
:
Greetings Rudolph,
I am excited to sing for everyone at the concert! I've been working on my vocals all year round. I feel like it will really bring all the performances together. It's going to be a really fun party!
I have hidden my top secret lyrics in my military strength password manager but I forgot the master password which unlocks it :(
Luckily, I thought something like this might happen so I keep my master password in my flag.txt file. However, some jokster of a Reindeer has come along and filled this file with lots and lot of fakes. To make things worse, it looks like they've also somehow changed the real master password because I tried every single password I found in the file and all of them were incorrect.
Once I get back into my password manager, I'll do one final vocal training session before the big day!
Take care,
- Blitzen
We need to access their KeePass 2 safe (blitzen-passwords.kdbx
), but it is password-encrypted, and we don't know the password. Blitzen tells us that they keep their master password in their flag.txt
file. Upon inspection, we see that the file contains many fake flags in the expected HV24{...}
format. We also notice that some flags appear to be mutated.
The mutations we observe in the flag contents are:
- Base64 encoding
- Hex encoding
- Reversed string
There are two methods to solve this part of the challenge.
Method 1: Brute-forcing the password
Since we know the real flag exists in flag.txt
and has simply been mutated, we can write a brute-force script to try each flag candidate. For each flag candidate, we would also attempt to reverse each mutation. There are 1672
candidates and 3
mutations, giving us a total of 4 * 1672 = 6688
passwords to try.
This method is left as an exercise for the reader.
Method 2: Hidden QR code
The other approach is to find the hidden QR code in the file. Notice that the file contains 57
lines. If you open the text file in a hex editor, you will observe the presence of many NUL
characters on each line, which is not normal for a text file. Furthermore, some of the flags mention QR
as a hint that there could be a QR code hidden in this data.
As it turns out, this file is hiding a 57x57
QR code. If we treat every flag in the HV24{...}
format as a 1 and every NUL character as a 0, we get a valid 57x57
QR code.
We use the following script to build the QR code image:
from PIL import Image
def generate_qr_code(input_file, output_file):
"""
Generate a QR code image directly from a binary representation where '1' = black pixel, '0' = white pixel.
:param input_file: Path to the input file containing the binary representation (57x57 matrix).
:param output_file: Path to save the generated QR code image.
"""
qr_matrix = []
# Read the binary matrix from the input file
with open(input_file, "rb") as file:
for line in file:
binary_row = ""
i = 0
while i < len(line):
# Detect flags
if line[i:i+5] == b"HV24{":
# Read until the closing '}'
end_idx = line.find(b"}", i)
if end_idx != -1:
binary_row += "1"
i = end_idx + 1 # Move past the '}'
else:
raise ValueError("Malformed flag detected")
elif line[i:i+1] == b"\x00":
binary_row += "0"
i += 1
else:
# Skip unknown characters
i += 1
if len(binary_row) != 57:
raise ValueError(f"Line length {len(binary_row)} does not match 57 binary digits.")
qr_matrix.append(list(map(int, binary_row)))
if len(qr_matrix) != 57:
raise ValueError(f"File does not contain 57 rows for a 57x57 QR code.")
# Create an image from the binary matrix
scale = 10 # Scale factor for enlarging the QR code
img_size = 57 * scale
img = Image.new("1", (img_size, img_size), 1) # "1" mode for 1-bit pixels, default to white
pixels = img.load()
for y, row in enumerate(qr_matrix):
for x, value in enumerate(row):
color = 0 if value == 1 else 1 # 0 = black, 1 = white
for dy in range(scale):
for dx in range(scale):
pixels[x * scale + dx, y * scale + dy] = color
# Save the QR code as an image
img.save(output_file)
print(f"QR code saved to {output_file}")
# Input and output paths
input_file = "flag.txt" # The input file containing 'HV24{...}' and '\x00'
output_file = "qr-code.png"
# Generate the QR code
generate_qr_code(input_file, output_file)
This gives us the following QR code:
Scanning this QR codes gives us the text:
gur znfgre cnffjbeq lbh ner ybbxvat sbe vf gur 1336gu bar ohg jvgu pbagragf erirefrq! Nyjnlf rkcrpg gur bss-ol-bar reebe!
Using character analysis, you might be able to tell that this is English. This text decodes using ROT13 to:
the master password you are looking for is the 1336th one but with contents reversed! Always expect the off-by-one error!
We'll use another script to retrieve the 1336th flag and reverse its contents:
def get_1336th_flag(file_path):
"""
Extracts the 1336th 'HV24{...}' flag from the file, reverses its contents, and prints it.
:param file_path: Path to the input file containing the flags.
"""
flags = []
with open(file_path, "rb") as file:
for line in file:
# Find all occurrences of 'HV24{...}' in the line
start = 0
while (start := line.find(b"HV24{", start)) != -1:
end = line.find(b"}", start)
if end != -1:
flag = line[start:end + 1] # Extract the full flag
flags.append(flag)
start = end + 1 # Move past the current flag
else:
break
# Ensure there are enough flags in the file
if len(flags) < 1336:
raise ValueError(f"The file contains only {len(flags)} flags, less than the required 1336th flag.")
# Get the 1336th flag
flag_1336 = flags[1335] # Index is 1335 because lists are 0-based
reversed_content = flag_1336[5:-1][::-1] # Reverse the content inside 'HV24{...}'
# Print the result
print("HV24{" + reversed_content.decode("utf-8") + "}")
# Input file path
input_file = "flag.txt" # Replace with the actual file name
# Run the function
get_1336th_flag(input_file)
This prints out: HV24{#?#?#?#ToG3th3r#}
This provides us with the password for the KeePass safe and Blitzen's flag component.
KeePass Safe
We open the KeePass safe using the password HV24{#?#?#?#ToG3th3r#}
:
There are various folders in the safe with a few entries.
Notably, we study the following entries:
General
→master_password
- Password:
HV24{#?#?#?#ToG3th3r#}
- Note:
My personal secure password. Rumour has it all of the other Reindeer have similar ones for themselves.
- Password:
Windows
→Windows 11
- Username:
blitzen
- Password:
Blitzen1994
- Note:
Santa tells me he often finds many of the reindeers passwords used online by other people. I guess its because some of the reindeer like to include their own names in their passwords.
- Username:
Recycle Bin
→prancers keys
:- File:
Prancers-keys.zip
- Note:
There can only be one great vocalist this year and its going to be me.
- File:
We observe that each reindeer seems to have their own flag components in the format: HV24{#?#?#?#?#}
, where one of the question marks is replaced by a secret word. We predict that the final flag for this challenge will be a combination of all four flag components.
Another interesting note is that Blitzen used their own name as part of their Windows password. They also mention that other reindeer like to do the same. If you are especially observant, you will notice that Blitzen1994
is a password found in the rockyou.txt
wordlist.
Finally, we notice a very suspicious prancers-keys.zip
file, which we save for later.
Prancer
Let's begin by reading Prancer's update:
## Prancer's Update
YO YO RUDOLPH!
How's that famous red nose! Still shining bright I hope!
For my performance, in your honour, I am going to rap this masterpiece:
https://www.youtube.com/watch?v=B4W_XYI4AN0
All my practice was going well! Until I went to visit the vet because when I came back my web browser was broken!@#$
My browser preferences were completely gone! Luckily, I found the browser related files they tried to delete in my recycle bin and I restored everything. Everything seems to work again now expect my saved passwords...I can't seem to figure out why. I checked the security tapes and it showed Blitzen leaving the scene of the crime! Trying to interfere with my practice so they can outshine me haha.
Oh and one more thing, I found out that one of the other Reindeer had a suspicious file on their computer. After Santa told us about that nasty Ransomware that infected his computer, I am taking no chances. I scanned the file for viruses but all of the anti-virus softwares think it is clean. But you should never risk things like this so I deleted the file permanently to keep everyone safe. :)
Anyway, this will be the best event ever thanks to you!
YOU'LL GO DOOOOWN IN HISTA, YOU'LL GO DOOOOWN IN HISTA, YOU'LL GO DOOOOWN IN HISTOOORY!
Rock on,
Prancer
Prancer tells us they saw Blitzen messing with them by trying to break their web browser. We are given a Chrome.zip
file containing Prancer's Chrome user data directory. Prancer mentions that everything seems to work again now, except for their saved passwords.
Cracking Chrome Passwords protected using Windows DPAPI
The ZIP file (prancers-keys.zip
) from earlier is a DPAPI
archive of Prancer's master key on a Windows-based machine. It contains a file at:C\Users\prancer\AppData\Roaming\Microsoft\Protect\S-1-5-21-3152064623-1017805262-467371474-1001\c790ec71-1c98-404a-9dab-4bfe8f6871a5
,
which is the directory used to store DPAPI
master keys.
Blitzen must have deleted these files from Prancer's machine, causing Prancer's saved passwords on Chrome to fail the decryption process. We decide to use DB Browser for SQLite with the Chrome/Default/Login Data
file to explore saved passwords. We find one entry with an encrypted password for Prancer's email. We have a hunch that we need to crack this.
We use the tool mimikatz
to crack the Windows DPAPI master password, which is encrypting the Chrome passwords. We use the dpapi::masterkey
command, ensuring that we pass in the c790ec71-1c98-404a-9dab-4bfe8f6871a5
file:
$ mimikatz # dpapi::masterkey /in:c790ec71-1c98-404a-9dab-4bfe8f6871a5
**MASTERKEYS**
dwVersion : 00000002 - 2
szGuid : {c790ec71-1c98-404a-9dab-4bfe8f6871a5}
dwFlags : 00000005 - 5
dwMasterKeyLen : 000000b0 - 176
dwBackupKeyLen : 00000090 - 144
dwCredHistLen : 00000014 - 20
dwDomainKeyLen : 00000000 - 0
[masterkey]
**MASTERKEY**
dwVersion : 00000002 - 2
salt : dd9910a8f9dbed99caf0eb98527e4bff
rounds : 00001f40 - 8000
algHash : 0000800e - 32782 (CALG_SHA_512)
algCrypt : 00006610 - 26128 (CALG_AES_256)
pbKey : 024ed8b5eef41f45a96d52eaeb98c06e75054c5369076328ac49abbc5e956ce6a043adbdd63899646afc1c5820b3d2021d5230846f26250ea92ee8dc7a4691e6fbc6c5c3abae38c0a0b47ae10d146b5a20171cfdb4a8556edb53c4a4cdeeab33ada65f67c3f3131e5c4c057297487d60e85e2de5123daf63171c6a2f5aac00afbd19efa9ec9f3232ed307d7a66f3ed3e
[backupkey]
**MASTERKEY**
dwVersion : 00000002 - 2
salt : 29f8e374eedee9b6f5d2a7ae8c0b77bc
rounds : 00001f40 - 8000
algHash : 0000800e - 32782 (CALG_SHA_512)
algCrypt : 00006610 - 26128 (CALG_AES_256)
pbKey : f0aa63b9dc61a1da66de3137d9830bd1996a0bfa9942deb3f16d66d07949cd6070dfdebe3aa7a3e08393e2ec438f28fce6e28380f7f37b656ba5fcd174215bb2ee07850b263b520a0f15637b0fe1ce2863068a68236030ae1d98c0793ade307aa20d136429e48ee0b1bded7fb26bb545
[credhist]
**CREDHIST INFO**
dwVersion : 00000003 - 3
guid : {a163913c-885c-42aa-b243-4581bc47fd80}
At this stage, we need to brute-force the password using a popular tool like John
or Hashcat
. I decided to use Hashcat.
First, we create a Hashcat hash from the available data using the DPAPI example hashes as a guide: Hashcat Example Hashes.
In our case, we want to use mode 15900 (DPAPI masterkey file v2 + local context)
.
The format will look something like this:
$DPAPImk$<version>*<context>*<SID>*<encryption_algorithm>*<hash_algorithm>*<iterations>*<salt>*<encrypted_masterkey_length>*<encrypted_masterkey>
This is the data we extracted from our dpapi::masterkey
results from earlier:
Version: 2
Context: 1 (local)
SID: S-1-5-21-3152064623-1017805262-467371474-1001
encryption_algorithm: aes256
hash_algorithm: sha512
iterations: 8000
salt: dd9910a8f9dbed99caf0eb98527e4bff
encrypted_masterkey_length: 288
encrypted_masterkey: 024ed8b5eef41f45a96d52eaeb98c06e75054c5369076328ac49abbc5e956ce6a043adbdd63899646afc1c5820b3d2021d5230846f26250ea92ee8dc7a4691e6fbc6c5c3abae38c0a0b47ae10d146b5a20171cfdb4a8556edb53c4a4cdeeab33ada65f67c3f3131e5c4c057297487d60e85e2de5123daf63171c6a2f5aac00afbd19efa9ec9f3232ed307d7a66f3ed3e
Substituting our values into the hash format, we obtain the following hash:
$DPAPImk$2*1*S-1-5-21-3152064623-1017805262-467371474-1001*aes256*sha512*8000*dd9910a8f9dbed99caf0eb98527e4bff*288*024ed8b5eef41f45a96d52eaeb98c06e75054c5369076328ac49abbc5e956ce6a043adbdd63899646afc1c5820b3d2021d5230846f26250ea92ee8dc7a4691e6fbc6c5c3abae38c0a0b47ae10d146b5a20171cfdb4a8556edb53c4a4cdeeab33ada65f67c3f3131e5c4c057297487d60e85e2de5123daf63171c6a2f5aac00afbd19efa9ec9f3232ed307d7a66f3ed3e
To effectively brute-force this hash, we use the rockyou.txt
wordlist and filter out any password that does not contain the word prancer
(case-insensitive), based on our earlier hint about reindeer liking to use their own names in their passwords:
$ cat rockyou.txt | grep -i 'prancer' > wordlist.txt
$ cat wordlist.txt | wc -l
60
This results in 60
potential passwords, making it very fast to brute-force.
We then begin our brute-force using our wordlist.txt
in DPAPI masterkey file v2 (context 1 & 2) (15900)
mode:
$ hashcat -m 15900 -a 0 hash.txt wordlist.txt
After a few seconds, we successfully crack the password! The Windows password for Prancer is prancer2
.
Now, we rerun dpapi::masterkey
and include the /password
parameter for the Windows password:
$ mimikatz # dpapi::masterkey /in:c790ec71-1c98-404a-9dab-4bfe8f6871a5 /password:prancer2 /sid:S-1-5-21-3152064623-1017805
262-467371474-1001
**MASTERKEYS**
dwVersion : 00000002 - 2
szGuid : {c790ec71-1c98-404a-9dab-4bfe8f6871a5}
dwFlags : 00000005 - 5
dwMasterKeyLen : 000000b0 - 176
dwBackupKeyLen : 00000090 - 144
dwCredHistLen : 00000014 - 20
dwDomainKeyLen : 00000000 - 0
[masterkey]
**MASTERKEY**
dwVersion : 00000002 - 2
salt : dd9910a8f9dbed99caf0eb98527e4bff
rounds : 00001f40 - 8000
algHash : 0000800e - 32782 (CALG_SHA_512)
algCrypt : 00006610 - 26128 (CALG_AES_256)
pbKey : 024ed8b5eef41f45a96d52eaeb98c06e75054c5369076328ac49abbc5e956ce6a043adbdd63899646afc1c5820b3d2021d5230846f26250ea92ee8dc7a4691e6fbc6c5c3abae38c0a0b47ae10d146b5a20171cfdb4a8556edb53c4a4cdeeab33ada65f67c3f3131e5c4c057297487d60e85e2de5123daf63171c6a2f5aac00afbd19efa9ec9f3232ed307d7a66f3ed3e
[backupkey]
**MASTERKEY**
dwVersion : 00000002 - 2
salt : 29f8e374eedee9b6f5d2a7ae8c0b77bc
rounds : 00001f40 - 8000
algHash : 0000800e - 32782 (CALG_SHA_512)
algCrypt : 00006610 - 26128 (CALG_AES_256)
pbKey : f0aa63b9dc61a1da66de3137d9830bd1996a0bfa9942deb3f16d66d07949cd6070dfdebe3aa7a3e08393e2ec438f28fce6e28380f7f37b656ba5fcd174215bb2ee07850b263b520a0f15637b0fe1ce2863068a68236030ae1d98c0793ade307aa20d136429e48ee0b1bded7fb26bb545
[credhist]
**CREDHIST INFO**
dwVersion : 00000003 - 3
guid : {a163913c-885c-42aa-b243-4581bc47fd80}
[masterkey] with volatile cache: SID:S-1-5-21-3152064623-1017805262-467371474-1001;GUID:{a163913c-885c-42aa-b243-4581bc47fd80};MD4:1e413b40a2ba3a786b1a0361a1eb6822;SHA1:ca183909baa0edd8c03e4bf794c2c7df21586388;
key : a0b912995883940fac279a75f575c3ead6b037de200e99b52ae2749218568c80a54c602d52a65a991ca93ee830f3d2856c27c7c18ee9f12c657f3e94b6755afd
sha1: 0b1601b9711202469e94a2188eb40005d26849db
[masterkey] with password: prancer2 (normal user)
key : a0b912995883940fac279a75f575c3ead6b037de200e99b52ae2749218568c80a54c602d52a65a991ca93ee830f3d2856c27c7c18ee9f12c657f3e94b6755afd
sha1: 0b1601b9711202469e94a2188eb40005d26849db
This provides us with the master key in hex format: a0b912995883940fac279a75f575c3ead6b037de200e99b52ae2749218568c80a54c602d52a65a991ca93ee830f3d2856c27c7c18ee9f12c657f3e94b6755afd
In the Chrome data directory, we also have access to the Local State
files. The Local State
file exposes the encrypted_key
, which is: RFBBUEkBAAAA0Iyd3wEV0RGMegDAT8KX6wEAAABx7JDHmBxKQJ2rS/6PaHGlEAAAABwAAABHAG8AbwBnAGwAZQAgAEMAaAByAG8AbQBlAAAAEGYAAAABAAAgAAAA83EeusVzPjsnBVv3kOCD/WKv+vPupZfVCEgS3AnHVV4AAAAADoAAAAACAAAgAAAAcOiLSJLvHXKfCWrte7WoT1LDS6MibMnPMJphcKpyKIcwAAAAXDXiwe0JEFrieifkUUh/6KGGvIJjel63WP6cE4S34UZvJ2O3ddz1dkrmXi6s88ntQAAAACdkCr/EYzTDN+gooyH/0LhTdOIbmFN+YzIozk+MAc6NvlansMQvrfygFCrRuellkLGskRmnT7qAOAmnb2k9iCE=
We can use the mimikatz dpapi::chrome
plugin to crack Chrome passwords.
At a minimum, we must pass in the following parameters:
/in
- TheDefault/Login Data
file./state
- TheLogin State
file./encryptedkey
- The encrypted key (found in theLocal State
file).
Since we are not running on Prancer's original Windows system, we must also provide the master key using the /masterkey
parameter.
$ mimikatz # dpapi::chrome /in:"C:\Users\Mo\Documents\CTF\Hacking-Lab\Hackvent\2024\day 15\Chrome\Default\Login Data" /state:"C:\Users\Mo\Documents\CTF\Hacking-Lab\Hackvent\2024\day 15\Chrome\Local State" /encryptedkey:RFBBUEkBAAAA0Iyd3wEV0RGMegDAT8KX6wEAAABx7JDHmBxKQJ2rS/6PaHGlEAAAABwAAABHAG8AbwBnAGwAZQAgAEMAaAByAG8AbQBlAAAAEGYAAAABAAAgAAAA83EeusVzPjsnBVv3kOCD/WKv+vPupZfVCEgS3AnHVV4AAAAADoAAAAACAAAgAAAAcOiLSJLvHXKfCWrte7WoT1LDS6MibMnPMJphcKpyKIcwAAAAXDXiwe0JEFrieifkUUh/6KGGvIJjel63WP6cE4S34UZvJ2O3ddz1dkrmXi6s88ntQAAAACdkCr/EYzTDN+gooyH/0LhTdOIbmFN+YzIozk+MAc6NvlansMQvrfygFCrRuellkLGskRmnT7qAOAmnb2k9iCE= /masterkey:a0b912995883940fac279a75f575c3ead6b037de200e99b52ae2749218568c80a54c602d52a65a991ca93ee830f3d2856c27c7c18ee9f12c657f3e94b6755afd
> Encrypted Key seems to be protected by DPAPI
* masterkey : a0b912995883940fac279a75f575c3ead6b037de200e99b52ae2749218568c80a54c602d52a65a991ca93ee830f3d2856c27c7c18ee9f12c657f3e94b6755afd
> AES Key is: 633084eb0dd8e8d89d1d015428bcb873f3f44d1c982502147f318a60a49dd6c1
URL : https://accounts.santamail.christmas/ ( https://accounts.santamail.christmas/ )
Username: prancer@santamail.christmas
* using BCrypt with AES-256-GCM
Password: HV24{#?#?#BEST#?#}
Running this command results in a successful decryption!
We discover that Prancer's password and flag component is: HV24{#?#?#BEST#?#}
Exploring Chrome History
Prancer's update mentions they found a suspicious file on another reindeer's machine. They scanned the file with multiple antivirus software programs, which found it to be safe. Nevertheless, they deleted the file. The lyrics YOU'LL GO DOOOOWN IN HISTA, YOU'LL GO DOOOOWN IN HISTA, YOU'LL GO DOOOOWN IN HISTOOORY!
hint at looking into the browsing history.
Since we have access to Prancer's Chrome user data directory, we can explore their browsing history. Looking at the Chrome history file at Default/History
reveals some interesting visits.
In particular, we see that Prancer visited this URL:
https://www.virustotal.com/gui/file/5e875d3743c7e69c2b09d9b9556e08ff1992012bb25d73e080d8db3b72fbe9bc
This VirusTotal scan corresponds to a file called Silent_Night_by_Comet.mid
, which appears to have been deleted from Comet's machine. The community tab on VirusTotal reveals a comment by a user called Prancer_the_reindeer
:
Luckily, the analysis comment by Prancer links to the sample file for download:
https://drive.google.com/file/d/1CEohyNqduw9hA4P0OUmGxo1Zd6uymptg/view?usp=sharing
We visit this link and download the file. We then validate that the SHA256 hash of the file matches both the VirusTotal hash and the hash found in Prancer's comment:
$ sha256sum Silent_Night_by_Comet.mid
5e875d3743c7e69c2b09d9b9556e08ff1992012bb25d73e080d8db3b72fbe9bc *Silent_Night_by_Comet.mid
Comet
Silent Night MIDI file
For Comet, the flag component is hidden in plain sight but may be tricky to discover. Playing the Silent_Night_by_Comet.mid
MIDI file produces the normal Silent Night
song. However, the clue lies in the name of the song, Silent Night
. There are silent notes embedded in the MIDI file that you cannot hear.
We open the MIDI file in a powerful online MIDI editor called Signal. Many other editors may not be able to handle this due to a limited octave designation range, but some local tools, such as Audacity, are capable of solving it.
Using Signal, we upload the MIDI file for analysis. Scrolling down, we observe something unusual in the C0
octave designation.
The flag component is visible in plain sight, as the notes resemble letters:
It spells out: HV24{#?#jAM#?#?#}
Note: A jar of jam was also visible in the original update.jpg
image.
This provides us with Comet's flag component.
Hidden files in update.jpg
We use binwalk
on Comet's update.jpg
file, which extracts four images:
- One showing a location logo.
- One showing a Rick Roll image.
- One showing a locked PDF logo.
- One showing an XML logo.
This serves as a hint for the password to unlock the update.pdf
file in Dancer's folder. The hint suggests that the location of the Rick Roll is key to unlocking the PDF. The XML logo suggests the need to locate an XML file hidden within the binary data.
Some versions of binwalk
may fail to locate or extract files like XML files. By using tools such as strings
, a hex editor, or other utilities, you should be able to identify an XML file embedded in the raw update.jpg
binary.
We then extract the XML file:
<?xml version="1.0" encoding="UTF-8"?>
<GeolocationData>
<Location>2dUURCy1VnuDyonM1oyKq5VoMjApQabrAXxr</Location>
<TimeStamp>2024-12-12T14:35:00Z</TimeStamp>
<Address>
<StreetNumber>REDACTED</StreetNumber>
<StreetName>REDACTED</StreetName>
<RegexpPattern>^\d{1,5}\s[A-Z][a-z]+\s(?:St|Rd|Cl)$</RegexpPattern>
</Address>
<Details>
<Accuracy>5 meters</Accuracy>
<Elevation>25 meters</Elevation>
<Provider>GPS</Provider>
</Details>
</GeolocationData>
The string 2dUURCy1VnuDyonM1oyKq5VoMjApQabrAXxr
is a Base58-encoded string.
It decodes to: 51°30'45.09"N, 0°13'8.52"W
.
By Googling this, we find it is the location where the Rick Roll video was filmed. Google provides the address: 154 Freston Rd
. The XML file also includes an RegexpPattern
for the Address
, which is: ^\d{1,5}\s[A-Z][a-z]+\s(?:St|Rd|Cl)$
. This confirms the correct format for the address.
Therefore, the password for the locked update.pdf
file in Dancer's folder is: 154 Freston Rd
Dancer
We first unlock the PDF file using the password: 154 Freston Rd
.
Dancer's update on the first page of the PDF file is:
Dancer’s Update
Heeeeey Rudolph!
Are you as excited as I am!
For my performance, I will be playing Jingle Bells on the Guitar!
I know everybody just thought I would be dancing instead because of my name
but I do have other talents.
I’m almost ready but today when I was playing from my music sheet,
I noticed that the song did not sound correct at all.
Argh…one of the other reindeer must be pranking me by changing some of my notes.
Serves me right for messing with Blitzen earlier I suppose.
I’ll try to get a new music sheet from Santa as an early Christmas present.
Otherwise, I won’t be able to get more practice in.
Wishing you a wonderful holiday,
Dancer
Dancer mentions that their music sheet, located on the second page of the PDF, has been tampered with:
By conducting some research (perhaps by comparing other Jingle Bells music sheets online), we discover the following:
- The
treble
clef appears normal and accurately represents the Jingle Bells song. - The
bass
clef, however, is unusual. It has a strange16/4
time signature (compared to the4/4
treble signature), and the notes appear suspicious. It seems there is hidden encoded data embedded in this section.
This turns out to be a Music Sheet Cipher. We visit this website for decoding and manually enter each character from the bass section into the decoder. We then attempt to decode the hidden message:
The first few plaintext results appear to be a series of space-separated integers. These likely represent ASCII characters in decimal form.
Using an online Decimal-to-ASCII converter, we decode the message and obtain: HV24{#ReiNd33r#?#?#?}
This provides us with Dancer's flag component.
Final flag
We now have all 4 flag components:
- Blitzen:
HV24{#?#?#?#ToG3th3r#}
- Prancer:
HV24{#?#?#BEST#?#}
- Comet:
HV24{#?#jAM#?#?#}
- Dancer:
HV24{#ReiNd33r#?#?#?#}
We simply combine the flag components in the correct positions to get the final flag.
Flag:
HV24{#ReiNd33r#jAM#BEST#ToG3th3r#}
Bonus Hidden
This challenge also contained the solution to: [HV24.HH] Frosty's Secret