2024 SANS Holiday Hack Challenge Prologue

Welcome back to the Geese Islands! Let’s help the elves pack up to return to the North Pole.

  1. Orientation
  2. Elf Connect
  3. Elf Minder

Orientation

Our story begins with Jingle Ringford walking us through a quick orientation exercise.

There’s not much to this. We follow the instructions and in return we get a little bit of information about how this years event will work.

Elf Connect

Angel Candysalt is struggling with a game called Elf Connect and is asking for our assistance.
Silver and Gold Solution

We are given two hints.

  • I love brain games! This one is like the New York Times Connections game. Your goal here is to find groups of items that share something in common. Think of each group as having a hidden connection or theme—four items belong together, and there are multiple groups to find! See if you can spot patterns or common threads to make connections. Group all the items correctly to win!
  • WOW! A high score of 50,000 points! That’s way beyond the limit! With only four rounds and a max of 400 points per round, the top possible score should be 1,600 points. So, how did someone get to 50,000? Something unusual must be happening! If you’re curious, you might want to check under the hood. Try opening the browser’s developer tools console and looking around—there might even be a variable named ‘score’ that could give you some insights. Sometimes, games hold secrets for those who dig a little deeper. Give it a shot and see what you can discover!

The challenge is a matching game in sets of four. At first review, the code appears like it might simply give us the answers, but there’s just enough obfuscation involved that I decided to look for a simpler way.

Further review showed that it’s possible to trigger the Silver and Gold completion from the browser console and skip the game entirely.

Silver and Gold Solution

To get both achievements at once, we can start the game and paste the code below into the browser development tools console.

correctSets.forEach((set, setIndex) => {
    set.forEach(i => {
        const box = wordBoxes[shuffledIndices[i]];
        box.selected = true; // Mark the box as selected
        box.disableInteractive(); // Simulate it being finalized
    });
    completedSets.push(setIndex); // Mark the set as completed
    score += 100000; // Award points for each set
});

// Update the displayed score
scoreText.setText('Score: ' + score);
highScoreText.setText('High Score: ' + score);

// Check for gold achievement
if (score > 50000) {
    highScoreText.setText('High Score: ' + score);
    emitter.explode(20);
    submitAction(2); // Gold achievement action
    displaySuccessMessage('Great Job Hacker! Elf Connect Complete and Hacked!', function () {});
}

// Simulate completing the game
roundComplete = 4; // Assume this is the final round
sessionStorage.setItem('score', score);
sessionStorage.setItem('roundComplete', roundComplete);

// Trigger the game completion logic
gameStatus();

This is the original code we are overriding with the function above.


It’s also possible to trigger what looks like it should give the gold achievement without doing so. Setting the score isn’t enough – you need to include the ‘check for gold achievement’ code (or complete a round manually) so that the score is seen to be over 50,000 within that function.

It’s also possible to earn the Silver Medal by playing the game and matching the categories.

We are given accolades suitable to our accomplishment.

Elf Minder 9000

Elf Minder is a game of madness and confusion guaranteed to be fun for the whole family.

The game is introduced by Poinsetta McMittens. Who also gives us a nudge in the direction of a potential issue related to one of the game mechanics.

Elf Minder Silver Solutions
Elf Minder Gold Solution

We are given three hints.

  • When developing a video game—even a simple one—it’s surprisingly easy to overlook an edge case in the game logic, which can lead to unexpected behavior.
  • Some levels will require you to click and rotate paths in order for your elf to collect all the crates.
  • Be sure you read the “Help” section thoroughly! In doing so, you will learn how to use the tools necessary to safely guide your elf and collect all the crates.

The rules are…simplestraighforward…as follows:

 Instructions 
1. Draw a path for the elf.
2. Collect all crates.
3. Head to the checkered flag.
Path Rotation
Click on a path segment to rotate it. You cannot rotate paths on an occupied cell.
• Basic Gameplay
• Path Rotation
• Scoring
• Tunnels
• Springs
• Sizzling Sand
• Sleepy Crab
Scoring
Scoring happens in two modes: Good Boss, and Evil Boss.
A Good Boss tries to get work done efficiently, so Good Boss scoring is calculated using the formula:
Score = 1000 – (seconds taken * 25) – (path segments * 5)

An Evil Boss wastes your time and doesn't pitch in, so Evil Boss is scored using this formula:
Score = 0 + (seconds taken * 12.5) + (path segments * 10) – (path rotations * 250)

Tunnels
1. Tunnels teleport the elf.
2. Tunnels come in pairs.
3. You can only dig one tunnel.

Springs
When stepped on, springs launch the elf in the current direction of travel. If there isn't a path in that direction, the elf will skip the spring.


Sizzling Sand
You walk quickly on sizzling sand.

Sleepy Crab
You walk slowly near sleeping crabs.

Simple Right?

Here are some fun rules that aren’t immediately obvious.

  1. An item placed before the game is reset will remain where it is when the level is restarted.
  2. Items are not necessarily where they appear to be, but where they appear to be is also taken into account when determining whether rules were broken.
  3. Time Remaining is not necessarily and indicator of time remaining.
  4. Item placement is not limited to where you can place items.
  5. The outer border of the game is not the outer border of the game.

And so on. DO YOU SEE???

The levels are defined here: https://hhc24-elfminder.holidayhackchallenge.com/levels.js

With each level being represented in an array by the coordinates of the obstacle or flag along with a number indicating what it is.

Guide.js is a less straightforward but more complete description of the actual rules, including a truly inspired group of error messages.


A quick inspection of the traffic via burp shows us that there is a secret level that we haven’t unlocked yet. Adjusting the level paramer in the URL to point there (&level=%A%Real%Pickle) just gives us the Elf Minder logo, but we can access the level immediately via the ‘edit mode’ (see below).


Edit mode can be accessed by adding an edit=true flag to the URL. From here we can access ‘A Real Pickle’ before finishing the other levels, as well as play with the game creation to explore mechanics.
Opening dev tools with edit mode enabled we see the following message:

The dev tools can also be accessed by running this command from the browser console: document.querySelector(‘.admin-controls’).classList.remove(‘hidden’);

Also…there are ticks.


A bit of exploration shows us some of what the elf was talking about with the comment about springs. This error can be triggered by placing too many springs (item placement can be done via the console), but we can also trigger the event directly in the console with the command: “whyCantIHoldAllTheseSprings”

A lot of strategies that seem like they might be useful to manipulate the game result in errors. For example removing all crates with the following command:
game.entities = game.entities.filter(entity => entity[2] !== EntityTypes.CRATE);

Gives us this informative error:

Removing rocks:
game.entities = game.entities.filter(entity => entity[2] !== EntityTypes.BLOCKER);

Placing segments that are longer than intended seems clever…alas:

There are a lot of dead ends and logic loops to troubleshooting this, so I’m not going to go into all of them. All of the levels can be solved (including pickle, though that’s not how I did it) just using the provided game controls.

Some things that do work.

  1. Removing crabs to negate their slowdown effect works, but we can’t walk where they were.
    • game.entities = game.entities.filter(entity => entity[2] !== EntityTypes.HAZARD);
  2. Replacing sections of sand with hot sand to speed the elf up works, but only has an impact if you are moving straight for 2 or more blocks.
  3. Manually placing tunnels (or other entities)…even at the edge of squares where you cannot place them via the gui.
    • The console command to do this is:
      game.entities.push([X, Y, <Entity Number>]);
    • The entity numbers are:
      • Start Flag: 0
      • End Flag: 1
      • Crate: 2
      • Blocker/Boulder: 3
      • Hazard: 4
      • Steam Hazard: 5
      • Portal/Tunnel: 6
      • Spring: 7
  4. Placing tunnels on boxes, placing springs on tunnels, and placing springs on tunnels on boxes all at once.
  5. Replacing the ‘game loop’ function with our own version can speed up the elf. This version works as long as springs or tunnels aren’t required. Some levels will still error out if this finishes the level too quickly. To do this the code below can be pasted into the console.
game.gameLoop = function() {
    const click = this.clicks.find(([cell, ts]) => ts === this.EMMEHGERDTICKSTHERESTICKSEVERYWHEREARHGHAHGREHUHGHH);
    
    if (click) {
        this.path.push(`${this.EMMEHGERDTICKSTHERESTICKSEVERYWHEREARHGHAHGREHUHGHH}: CLICKED ${click[0]}`);
        this.rotateSegments(click[0]);
    }
    if (typeof this.hero.cell === 'undefined') {
        // initialize hero
        this.hero.cell = this.getEntitiesByType(EntityTypes.START)[0];
        this.hero.x = this.hero.cell[0] * cellSize;
        this.hero.y = this.hero.cell[1] * cellSize; 
        this.hero.progress = 0;
        const startSegments = this.getSegmentsThatTouchPoint(this.entities.find(entity => entity[2] === EntityTypes.START));
        if (!startSegments.length) {
            console.log('no start segment found');
            return;
        }
        const startCell = this.hero.cell;
        this.hero.journey = startSegments[0];
        this.hero.journey = this.hero.journey[0][0] === startCell[0] && this.hero.journey[0][1] === startCell[1] ? this.hero.journey : [ this.hero.journey[1], this.hero.journey[0] ];
    } else {
        if (this.hero.airtime > 0) {
            this.hero.airtime -= 1;
        } else {
            const midpoint = this.getCellMidpoint(this.hero.journey[0]);
            const nearHazard = this.isNearHazard();
            const cellEnts = this.getEntitiesAtCell(midpoint);
            const isHot = cellEnts.some(entity => entity[2] === EntityTypes.STEAM);
            this.hero.progress += nearHazard ? 0.1 : (isHot ? 0.3 : 0.2); // Increased speed
            if (this.hero.progress >= 1) {
                const currentCellEntities = this.getEntitiesAtCell(this.hero.journey[0]);
                
                // Check for crates and remove them
                if (currentCellEntities.some(entity => entity[2] === EntityTypes.CRATE)) {
                    this.path.push(`${this.EMMEHGERDTICKSTHERESTICKSEVERYWHEREARHGHAHGREHUHGHH}: REMOVED CRATE`);
                    this.removeEntity(this.hero.journey[0], EntityTypes.CRATE);
                }

                // Check for end flag and trigger end result
                if (currentCellEntities.some(entity => entity[2] === EntityTypes.END)) {
                    const allCratesRemoved = !this.entities.find(entity => entity[2] === EntityTypes.CRATE);
                    if (allCratesRemoved) {
                        console.log("Level completed!");
                        return {
                            success: true,
                            data: {
                                segments: this.segments.length,
                                clicks: this.clicks.length,
                                level: this.level,
                                ticks: this.EMMEHGERDTICKSTHERESTICKSEVERYWHEREARHGHAHGREHUHGHH,
                                goodScore: calculateScore(this.EMMEHGERDTICKSTHERESTICKSEVERYWHEREARHGHAHGREHUHGHH, this.segments.length, this.clicks.length),
                                evilScore: calculateScore2(this.EMMEHGERDTICKSTHERESTICKSEVERYWHEREARHGHAHGREHUHGHH, this.segments.length, this.clicks.length),
                            }
                        };
                    }
                }

                this.hero.progress = 0;
                this.hero.cell = this.getCellMidpoint(this.hero.journey[1]);
                this.hero.journey = this.getNextJourney();
            }
        }
    }
    this.EMMEHGERDTICKSTHERESTICKSEVERYWHEREARHGHAHGREHUHGHH += 1;
    return { success: false };
};

Regretfully I did not find time to optimize scores on the various levels. I am including only simple expedient solutions for each. I look forward to seeing some of the other write ups on this one.

Silver trophy is awarded for completing the first 12 levels. Gold is awarded for completing ‘A Real Pickle’ (skip to the end for that one).

Silver Level Solutions

Sandy Start

Waves and Crates

Tidal Treasures

Dune Dash
For this level I used the following console commands to remove the crab and place tunnels in convenient places.

game.entities = game.entities.filter(entity => entity[2] !== EntityTypes.HAZARD);
game.entities.push([7, 9, EntityTypes.PORTAL]);
game.entities.push([10, 9, EntityTypes.PORTAL]);

Coral Cove
For this solution it is necessary to rotate the square with the 3rd crate on it (by clicking on it) after the elf collects that crate, then click the first path to rotate the elf towards the end flag after the elf returns that way.

Shell Seekers

Palm Grove Shuffle

Tropical Tangle
For this solution I used the following console commands to place tunnels.
game.entities.push([2, 1, 6]);
game.entities.push([4, 1, 6]);

Crate Caper
Click on the central tunnel so the elf goes back and forth enough to get all the crates, then click on the second tunnel to redirect the elf to the end flag.

Shoreline Shuffle
This solution used the commands below to place springs.
game.entities.push([2, 3, 7]);
game.entities.push([6, 3, 7]);

Beachy Bounty
This console command was used for spring placement.
game.entities.push([5, 2, 7]);

Driftwood Dunes

Elf Minder Gold: A Real Pickle.

I used this command to place the 2nd spring, and the tunnel near the exit.
game.entities.push([8, 3, 7]);
game.entities.push([10, 9, 6]);