Holiday Hack 2023: Frosty's Beach
Geography
Getting To
Based on Jingle’s suggestion I’ll head to Frosty’s Beach, just around the feet of Christmas Island from the Orientation Dock.
Location Layout
Santa and the Goose of Christmas Island are waiting for me by the dock. The island also has sponsor booths as well as Morcel Nougat waiting by the Snowball Hero game.
Inside Santa’s Surf Shack, there’s a cranberry pi and Ginger Breddie:
Santa
Santa welcomes me to the game and gives the backstory for how he ended up in these tropical islands:
Santa
Welcome to the Geese Islands, fellow traveler! This one is called Christmas Island. Nooo ho ho, not that Christmas Island.
After countless years of shivering and shaking through each holiday season, I thought to myself, “Why not trade the snowflakes for sunbeams, just once?”
Oh, the North Pole has its charm, but the bones do yearn for a bit of warmth now and then.
The notion was suggested by my good friend, Chat North Pole Technology, or as we like to call it, ‘ChatNPT’. That’s the one we use, but there’s a whole slew of other AI platforms. You should try them out!
It came to me describing palm trees and gentle waves, saying, “Santa, let your holidays take flight to Geese Islands, where the warmth isn’t just a setting.”
“There, every day is a sunny scene straight out of a vintage film reel.”
I chuckled at the thought, my belly shaking like a bowl full of jelly.
But the AI persisted, “Winter’s best kept secret: the balmy breezes of Geese Islands!”
And I must confess, the sound of that did stroke my beard with curiosity.
So, I called a meeting with the elves, the reindeer, and Mrs. Claus, of course.The elves were all a-buzz with the idea of crafting toys with a view of the ocean!
Thus, we packed up our sleighs and ChatNPT charted a course for the Geese Islands, a tropical paradise just north of the equator..
And I must say, there’s something quite magical about a Christmas carol sung to the strum of a ukulele.
After all, the magic of the holidays isn’t in the snow or the cold, but in the love and the care that we put into each and every gift.
So here’s to trying new things, to following the sunshine, and to the Geese Islands, where the holiday cheer is sun-kissed and the Christmas spirit is as warm as the tropical breeze.
And it’s all thanks to a little nudge from ChatNPT.
Now, why not start off your vacation with a snowball fight with Morcel, or check out my surf shack on the other end of the beach?
However you decide to relax, be sure to soak in all the whimsical beauty of these magical islands, and enjoy the activities to the fullest!
The Goose of Christmas Island just says:
Goose of Christmas Island
Honk honk
It gives me a new lay for around the mast of my boat:
Snowball fight
Challenge
Arriving here unlocks two objectives in my badge. The first says:
I’ll find Marcel Nougat by the “Free Snowball Fights” sign:
Morcel introduces the challenge:
Morcel Nougat
Hey there, I’m Morcel Nougat, elf extraordinaire!
You won’t believe this, but we’re on a magical tropical island called Christmas Island, and it even has snow!
I’m so glad ChatNPT suggested we come here this year!
Santa, some elves, and I are having a snowball fight, and we’d love you to join us. Santa’s really good, so trust me when I say it’s way more fun when played with other people.
But hey, if you can figure out a way to play solo by tinkering with client side variables or parameters to go solo mode, go for it!
There’s also ways to make the elves’ snowballs do no damage, and all kinds of other shenanigans, but you didn’t hear that from me.
Just remember, it’s all about having fun and sharing the joy of the holiday season with each other.
So, are you in? We’d really love your company in this epic snowball battle!
Game Overview
The game starts with a menu:
The modes are “VS Santa” and “VS Players”. I can create a room and get an ID to share with others, or enter a random room.
I’ll pick “VS Santa” so I can complete the challenge. I’m swarmed by Elves and Santa throwing snowballs, and it’s very difficult to stay alive.
Source Analysis
Finding Source
I’ll open the Chrome browser dev tools and look in the “Sources”, where under “room/” I’ll find game page loaded in the iFrame:
The JavaScript for the game is in this file. There’s a lot of interesting stuff in here.
Single Player Mode
Morcel Nougat suggested getting into Single Player mode. This is initialized in the JavaScript here:
var singlePlayer = "false"
function checkAndUpdateSinglePlayer() {
const localStorageValue = localStorage.getItem('singlePlayer');
if (localStorageValue === 'true' || localStorageValue === 'false') {
singlePlayer = String(localStorageValue === 'true');
}
const urlParams = new URLSearchParams(window.location.search);
const urlValue = urlParams.get('singlePlayer');
if (urlValue === 'true' || urlValue === 'false') {
singlePlayer = String(urlValue === 'true');
}
}
checkAndUpdateSinglePlayer
is called shortly after during page initiation. This is looking for the value singlePlayer
in local storage and again in the url parameters.
Damage
The function snowballHitsEntity
is interesting:
function snowballHitsEntity(entity, snowball) {
if (snowball.isdying || entity.isdefeated) return
if (snowball.owner_entityobj !== entity) {
if (gameType === 'free-for-all') {
...[snip]...
} else if ((entity === player || entity.entity_type === 'otherPlayer') && (snowball.owner_entityobj.entity_type === 'elf' || snowball.owner_entityobj.entity_type === 'santa')) {
snowball.isdying = true
snowball.playAnimThenDestroy();
if (entity === player) {
entity.takehit(snowballDmg, snowball.owner_entityobj)
}
} else if (entity.entity_type === 'elf' && snowball.owner_entityobj.entity_type !== 'elf' && snowball.owner_entityobj.entity_type !== 'santa') {
...[snip]...
} else if (entity.entity_type === 'santa') {
snowball.isdying = true
snowball.playAnimThenDestroy();
if (typeof santaObject.tintTimeoutInterval === 'number') {
clearInterval(santaObject.tintTimeoutInterval)
}
santaObject.tintTimeoutInterval = setTimeout(function() {
santaObject.clearTint();
}, 2000)
if (snowball.owner_entityobj.assigned_id === assigned_id || singlePlayer === "true") {
santaObject.setTint(myPlayerTint);
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({a: 's', i: playerId}));
}
} else {
santaObject.setTint(otherPlayerTint);
}
} else if (entity.entity_type === 'dwarf' && ['santa','elf'].includes(snowball.owner_entityobj.entity_type)) {
snowball.isdying = true
snowball.playAnimThenDestroy();
}
}
}
There are several different options here, but one interesting case is when the entity hit is a player and the snowball’s owner is Santa or an Elf. It calls entity.takehit(snowballDmg, snowball.owner_entityobj)
.
It’s also interesting to see what happens when Santa is hit. Some of the code is for turning Santa a color based on which player threw the snowball. Then it sends a message over the websocket:
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({a: 's', i: playerId}));
}
So there’s no ability to change the amount of damage that Santa’s taking. It’s just up to the server. I did play a bit with sending hit messages over the websocket to see if I could defeat Santa quickly, but didn’t succeed.
Single Player Mode
I’ll go back to the menu before the game, and look at its source. The function buildAndGotoUrl
is interesting:
function buildAndGotoUrl(roomId, gamet, roomt) {
var new_url = window.location.href.split('/').slice(0, -1).join('/') + '/room/'
new_url += "?username=" + username
new_url += "&roomId=" + roomId
new_url += "&roomType=" + roomt
new_url += "&gameType=" + gamet
if (resourceId && resourceId.length) {
new_url += "&id=" + resourceId
}
if (playerAvatar && playerAvatar.length) {
new_url += "&dna=" + playerAvatar
}
window.location.href = new_url
}
I know the game behaves differently if the singlePlayer
parameter is set to true
. I’ll put a breakpoint in this function by clicking on the line number:
Now I select “Random Match Making”, and when it hits the breakpoint, I’ll go to the dev tools console. First, it’s important to make sure the console is targeted at the right window:
Then, in the console, I’ll do a parameter injection by updating the gamet
parameter to include both it’s current value and singlePlayer
:
Now I’ll let the code continue, and when I join the game, Elf the dwarf is on my team:
Elf is quite strong, and I don’t really need to do much else here to win the match. He launches many snowballs at once that do serious damage. When Santa goes down:
Other Cheats
There are tons of other things I can do to tilt the game in my favor. Immediately on entering the game, I’ll drop into the console and can do some of the following:
- Modify the
player.takehit
function by runningplayer.takehit = () => {}
in the dev tools console. This changes that function that’s called when I take a hit to do nothing!
- Increase
elfThrowDelay
andsantaThrowDelay
. They are set to 2000 and 500 respectively (2 and 0.5 seconds). By changing them to 60 seconds or more, then I almost never get targeted. - Mess with
santaHitBoxSize
. It initialized to[60, 60, 70, 70]
. Multiplying each of those by 10 makes for a much easier throw!
Epilogue
Morcel is impressed:
Morcel Nougat
You’re like a snowball fighting ninja! A real-life legend. Can I have your autograph!?
Back with Santa, he recommends checking out the rest of the island:
Santa
Congratulations! You are a true snowball fight champion and thank you so much for helping out Ginger Breddie!
Oh, it feels like the warm and gentle winds are starting to pick up!
The perfect time to head back to your boat and embark on an adventure to all the other whimsical places the Geese Islands have to offer!
Safe travels my friend and thank you again for your help!
Linux 101
Challenge
Before leaving Frosty’s Beach, there’s one more objective in the badge:
I’ll head to the Surf Shack, where Ginger Breddie and the Linux 101 terminal await.
Ginger Breddie
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?
The terminal has a split screen with instructions on the top and a terminal on the bottom:
Solution
I’ll walk through and explain the answers in this video:
Here’s the series of questions and answers:
- The North Pole 🎁 Present Maker: All the presents on this system have been stolen by trolls. Capture trolls by following instructions here and 🎁’s will appear in the green bar below. Run the command “hintme” to receive a hint.
yes
- Perform a directory listing of your home directory to find a troll and retrieve a present!
elf@20de0aa600bb:~$ ls HELP troll_19315479765589239 workshop
- Now find the troll inside the troll.
elf@20de0aa600bb:~$ cat troll_19315479765589239 troll_24187022596776786
- Great, now remove the troll in your home directory.
elf@20de0aa600bb:~$ rm troll_19315479765589239
- Print the present working directory using a command.
elf@20de0aa600bb:~$ pwd /home/elf
- Good job but it looks like another troll hid itself in your home directory. Find the hidden troll!
elf@20de0aa600bb:~$ ls -a . .. .bash_history .bash_logout .bashrc .profile .troll_5074624024543078 HELP workshop
- Excellent, now find the troll in your command history.
elf@20de0aa600bb:~$ history 1 echo troll_9394554126440791 2 ls 3 cat troll_19315479765589239 4 rm troll_19315479765589239 5 pwd 6 ls -a 7 history
- Find the troll in your environment variables.
elf@20de0aa600bb:~$ envSHELL=/bin/bashTMUX=/tmp/tmux-1050/default,17,0HOSTNAME=20de0aa600bb RESOURCE_ID=5208a138-9b29-4dbf-9532-8767e9951ae3 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:*.png=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=real0xdf 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,7 _=/usr/bin/env
- Next, head into the workshop.
elf@20de0aa600bb:~$ cd workshop/
- A troll is hiding in one of the workshop toolboxes. Use “grep” while ignoring case to find which toolbox the troll is in.
elf@20de0aa600bb:~/workshop$ ls electrical toolbox_142.txt toolbox_189.txt toolbox_234.txt toolbox_280.txt toolbox_326.txt toolbox_372.txt toolbox_418.txt toolbox_464.txt toolbox_6.txt present_engine toolbox_143.txt toolbox_19.txt toolbox_235.txt toolbox_281.txt toolbox_327.txt toolbox_373.txt toolbox_419.txt toolbox_465.txt toolbox_60.txt toolbox_0.txt toolbox_144.txt toolbox_190.txt toolbox_236.txt toolbox_282.txt toolbox_328.txt toolbox_374.txt toolbox_42.txt toolbox_466.txt toolbox_61.txt toolbox_1.txt toolbox_145.txt toolbox_191.txt toolbox_237.txt toolbox_283.txt toolbox_329.txt toolbox_375.txt toolbox_420.txt toolbox_467.txt toolbox_62.txt ...[snip]... elf@20de0aa600bb:~/workshop$ grep -ir troll * toolbox_191.txt:tRoLl.4056180441832623
- A troll is blocking the present_engine from starting. Run the present_engine binary to retrieve this troll.
elf@b5cb36ca662a:~/workshop$ ./present_engine bash: ./present_engine: Permission denied elf@b5cb36ca662a:~/workshop$ chmod +x ./present_engine elf@b5cb36ca662a:~/workshop$ ./present_engine troll.898906189498077
- Trolls have blown the fuses in /home/elf/workshop/electrical. cd into electrical and rename blown_fuse0 to fuse0.
elf@b5cb36ca662a:~/workshop$ cd electrical/ elf@b5cb36ca662a:~/workshop/electrical$ mv blown_fuse0 fuse0
- Now, make a symbolic link (symlink) named fuse1 that points to fuse0
elf@b5cb36ca662a:~/workshop/electrical$ ln -s fuse0 fuse1
- Make a copy of fuse1 named fuse2.
elf@b5cb36ca662a:~/workshop/electrical$ cp fuse1 fuse2
- We need to make sure trolls don’t come back. Add the characters “TROLL_REPELLENT” into the file fuse2.
elf@b5cb36ca662a:~/workshop/electrical$ echo "TROLL_REPELLENT" >> fuse2
- Find the troll somewhere in /opt/troll_den. Entering
find .
from within the directory does solve this one. Interestingly,find . | grep -i troll
does not.elf@79c9ff8666d4:/opt/troll_den$ find . ...[snip]...
- Find the file somewhere in /opt/troll_den that is owned by the user troll.
elf@79c9ff8666d4:/opt/troll_den$ find . -user troll ./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@b5cb36ca662a:/opt/troll_den$ find . -size +108k -size -110k ./plugins/portlet-mocks/src/test/java/org/apache/t_r_o_l_l_2579728047101724
- List running processes to find another troll.
elf@b5cb36ca662a:/opt/troll_den$ ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND init 1 0.0 0.0 20112 16572 pts/0 Ss+ 22:20 0:00 /usr/bin/python3 /usr/local/bin/tmuxp load ./mysession.yaml elf 6945 0.6 0.0 31520 26868 pts/2 S+ 22:31 0:00 /usr/bin/python3 /14516_troll elf 7198 0.0 0.0 7672 3312 pts/3 R+ 22:31 0:00 ps aux
- The 14516_troll process is listening on a TCP port. Use a command to have the only listening port display to the screen.
elf@79c9ff8666d4:/opt/troll_den$ netstat -tnlp (Not all processes could be identified, non-owned process info will not be shown, you would have to be root to see it all.) Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:54321 0.0.0.0:* LISTEN 2939/python3
- The service listening on port 54321 is an HTTP server. Interact with this server to retrieve the last troll.
elf@b5cb36ca662a:/opt/troll_den$ curl localhost:54321 troll.73180338045875
- Your final task is to stop the 14516_troll process to collect the remaining presents.
elf@b5cb36ca662a:/opt/troll_den$ ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND init 1 0.0 0.0 20112 16572 pts/0 Ss+ 22:20 0:00 /usr/bin/python3 /usr/local/bin/tmuxp load ./mysession.yaml elf 2939 0.0 0.0 105616 27472 pts/2 S+ 22:31 0:00 /usr/bin/python3 /14516_troll elf 11297 0.0 0.0 7672 3236 pts/3 R+ 22:38 0:00 ps aux elf@b5cb36ca662a:/opt/troll_den$ kill 2939
Congratulations, you caught all the trolls and retrieved all the presents!
Epilogue
Ginger is impressed:
Ginger Breddie
Wow, if your surfing skills are as good as your Linux skills, you could be winning competitions!