Compare commits
2 Commits
053b5784aa
...
0576d99b9d
Author | SHA1 | Date |
---|---|---|
Maik de Kruif | 0576d99b9d | 6 days ago |
Maik de Kruif | c4785c3f7e | 6 days ago |
After Width: | Height: | Size: 1.4 MiB |
After Width: | Height: | Size: 256 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 153 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 149 KiB |
After Width: | Height: | Size: 2.0 MiB |
After Width: | Height: | Size: 219 KiB |
After Width: | Height: | Size: 193 KiB |
After Width: | Height: | Size: 202 KiB |
After Width: | Height: | Size: 332 KiB |
After Width: | Height: | Size: 281 KiB |
After Width: | Height: | Size: 261 KiB |
After Width: | Height: | Size: 316 KiB |
After Width: | Height: | Size: 332 KiB |
After Width: | Height: | Size: 369 KiB |
After Width: | Height: | Size: 366 KiB |
After Width: | Height: | Size: 262 KiB |
After Width: | Height: | Size: 249 KiB |
After Width: | Height: | Size: 270 KiB |
After Width: | Height: | Size: 265 KiB |
After Width: | Height: | Size: 277 KiB |
After Width: | Height: | Size: 317 KiB |
After Width: | Height: | Size: 346 KiB |
After Width: | Height: | Size: 363 KiB |
After Width: | Height: | Size: 187 KiB |
After Width: | Height: | Size: 139 KiB |
After Width: | Height: | Size: 1.6 MiB |
After Width: | Height: | Size: 14 KiB |
@ -0,0 +1,7 @@ |
|||||||
|
+++ |
||||||
|
author = "Maik de Kruif" |
||||||
|
title = "SANS Holiday Hack Challenge 2024" |
||||||
|
description = "A collection of my writeups for the 2024 edition of the SANS Holiday Hack Challenge (HHC). HHC is a free series of fun, quality, hands-on cybersecurity challenges." |
||||||
|
+++ |
||||||
|
|
||||||
|
A collection of my writeups for the 2024 edition of the SANS Holiday Hack Challenge (HHC). HHC is a free series of fun, quality, hands-on cybersecurity challenges. |
@ -0,0 +1,7 @@ |
|||||||
|
+++ |
||||||
|
author = "Maik de Kruif" |
||||||
|
title = "SANS Holiday Hack Challenge 2024" |
||||||
|
description = "A collection of my writeups for the 2024 edition of the SANS Holiday Hack Challenge (HHC). HHC is a free series of fun, quality, hands-on cybersecurity challenges." |
||||||
|
+++ |
||||||
|
|
||||||
|
A collection of my writeups for the 2024 edition of the SANS Holiday Hack Challenge (HHC). HHC is a free series of fun, quality, hands-on cybersecurity challenges. |
@ -0,0 +1,172 @@ |
|||||||
|
+++ |
||||||
|
author = "Maik de Kruif" |
||||||
|
title = "Elf Connect" |
||||||
|
subtitle = "Prologue - SANS Holiday Hack Challenge 2024" |
||||||
|
date = 2024-11-21T14:23:34+01:00 |
||||||
|
description = "Let's play our first game of the Holiday Hack Challenge. To win, we'll have to use the DevTools to read some code, and figure out how the scoring mechanism works." |
||||||
|
cover = "img/writeups/holiday-hack-challenge/2024/prologue/elf-connect/cover.png" |
||||||
|
tags = [ |
||||||
|
"Holiday Hack Challenge", |
||||||
|
"Prologue", |
||||||
|
"ctf", |
||||||
|
"hacking", |
||||||
|
"writeup", |
||||||
|
] |
||||||
|
categories = [ |
||||||
|
"ctf", |
||||||
|
"writeups", |
||||||
|
"hacking", |
||||||
|
] |
||||||
|
+++ |
||||||
|
|
||||||
|
## Link |
||||||
|
|
||||||
|
If you want to play the challenge yourself, you can find it here: |
||||||
|
|
||||||
|
<https://2024.holidayhackchallenge.com/> |
||||||
|
|
||||||
|
## Story line |
||||||
|
|
||||||
|
Let's start off by talking to Angel Candysalt: |
||||||
|
|
||||||
|
```txt |
||||||
|
Welcome back, island adventurer! I'm Angel Candysalt — so happy to finally meet you! |
||||||
|
|
||||||
|
I'm thrilled you're here because I could really use a hand with something. |
||||||
|
|
||||||
|
Have you ever heard of a game called Connections? |
||||||
|
|
||||||
|
It’s simple! All you need to do is find groups of four related words. |
||||||
|
|
||||||
|
I've been stuck on it all day, and I'm sure someone as sharp as you will breeze through it. |
||||||
|
|
||||||
|
Oh, and while you're at it, check out randomElf's score — they hit fifty thousand points, which seems… oddly suspicious. |
||||||
|
|
||||||
|
Think they might have tampered with the game? Just a hunch! |
||||||
|
``` |
||||||
|
|
||||||
|
## Recon |
||||||
|
|
||||||
|
Upon opening the challenge, we're greeted with an explanation of the game: |
||||||
|
|
||||||
|
{{< figure src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-connect/game-welcome.png" title="Game explanation" >}} |
||||||
|
|
||||||
|
## Silver |
||||||
|
|
||||||
|
The game seems simple enough, click four words that are connected and go through the rounds. Depending on your knowledge of Christmas terms, you may fly through this no problem. My knowledge about it is not so good, but I decided to play the game normally at first anyway. With some Googling I got through the challenge and got the silver medal. |
||||||
|
|
||||||
|
There are, however, multiple ways to solve the game. And we'll need to exploit this to get the gold medal. |
||||||
|
|
||||||
|
## Gold |
||||||
|
|
||||||
|
For gold, we'll need to inspect the code behind the game. |
||||||
|
|
||||||
|
We can open the DevTools, and under "Sources" we can find the iframe in which the game is running. Here we can see all the files that are being used. |
||||||
|
|
||||||
|
{{< figure src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-connect/sources.png" title="DevTools Sources" >}} |
||||||
|
|
||||||
|
Looking through this code, we can actually find the correct word sets. |
||||||
|
|
||||||
|
<!-- prettier-ignore-start --> |
||||||
|
```js |
||||||
|
const wordSets = { |
||||||
|
1: ["Tinsel", "Sleigh", "Belafonte", "Bag", "Comet", "Garland", "Jingle Bells", "Mittens", "Vixen", "Gifts", "Star", "Crosby", "White Christmas", "Prancer", "Lights", "Blitzen"], |
||||||
|
2: ["Nmap", "burp", "Frida", "OWASP Zap", "Metasploit", "netcat", "Cycript", "Nikto", "Cobalt Strike", "wfuzz", "Wireshark", "AppMon", "apktool", "HAVOC", "Nessus", "Empire"], |
||||||
|
3: ["AES", "WEP", "Symmetric", "WPA2", "Caesar", "RSA", "Asymmetric", "TKIP", "One-time Pad", "LEAP", "Blowfish", "hash", "hybrid", "Ottendorf", "3DES", "Scytale"], |
||||||
|
4: ["IGMP", "TLS", "Ethernet", "SSL", "HTTP", "IPX", "PPP", "IPSec", "FTP", "SSH", "IP", "IEEE 802.11", "ARP", "SMTP", "ICMP", "DNS"] |
||||||
|
}; |
||||||
|
// ... |
||||||
|
let correctSets = [ |
||||||
|
[0, 5, 10, 14], // Set 1 |
||||||
|
[1, 3, 7, 9], // Set 2 |
||||||
|
[2, 6, 11, 12], // Set 3 |
||||||
|
[4, 8, 13, 15] // Set 4 |
||||||
|
]; |
||||||
|
``` |
||||||
|
<!-- prettier-ignore-end --> |
||||||
|
|
||||||
|
This means two things. Firstly, if your knowledge is as bad as mine, you can just write some code to get the correct combinations. But, secondly, and more importantly, if the correct set is here, the checks are also likely done client-side (meaning in your browser, and not on the server). |
||||||
|
|
||||||
|
If you're wondering how to get the correct combinations, you can do it like this: |
||||||
|
|
||||||
|
```js |
||||||
|
Object.keys(wordSets).map((round) => |
||||||
|
correctSets.map((correctSet) => |
||||||
|
correctSet.map((index) => wordSets[round][index]) |
||||||
|
) |
||||||
|
); |
||||||
|
``` |
||||||
|
|
||||||
|
This might look a little complicated, so let me explain it for you. We start by looping over `wordSets`, this contains all the words for a specific round. We then look at the correct sets, and map the four indices to the actual word in the list. If we execute this code, we get the following output: |
||||||
|
|
||||||
|
{{< code language="json" title="Results" isCollapsed="true" >}} |
||||||
|
|
||||||
|
```json |
||||||
|
[ |
||||||
|
[ |
||||||
|
["Tinsel", "Garland", "Star", "Lights"], |
||||||
|
["Sleigh", "Bag", "Mittens", "Gifts"], |
||||||
|
["Belafonte", "Jingle Bells", "Crosby", "White Christmas"], |
||||||
|
["Comet", "Vixen", "Prancer", "Blitzen"] |
||||||
|
], |
||||||
|
[ |
||||||
|
["Nmap", "netcat", "Wireshark", "Nessus"], |
||||||
|
["burp", "OWASP Zap", "Nikto", "wfuzz"], |
||||||
|
["Frida", "Cycript", "AppMon", "apktool"], |
||||||
|
["Metasploit", "Cobalt Strike", "HAVOC", "Empire"] |
||||||
|
], |
||||||
|
[ |
||||||
|
["AES", "RSA", "Blowfish", "3DES"], |
||||||
|
["WEP", "WPA2", "TKIP", "LEAP"], |
||||||
|
["Symmetric", "Asymmetric", "hash", "hybrid"], |
||||||
|
["Caesar", "One-time Pad", "Ottendorf", "Scytale"] |
||||||
|
], |
||||||
|
[ |
||||||
|
["IGMP", "IPX", "IP", "ICMP"], |
||||||
|
["TLS", "SSL", "IPSec", "SSH"], |
||||||
|
["Ethernet", "PPP", "IEEE 802.11", "ARP"], |
||||||
|
["HTTP", "FTP", "SMTP", "DNS"] |
||||||
|
] |
||||||
|
] |
||||||
|
``` |
||||||
|
|
||||||
|
{{< /code >}} |
||||||
|
|
||||||
|
This just get us the correct answer though, and we'll need more for gold. |
||||||
|
|
||||||
|
Scanning further through the code, we find the `checkSelectedSet` function with some logic in it: |
||||||
|
|
||||||
|
```js |
||||||
|
function checkSelectedSet(scene) { |
||||||
|
// ... |
||||||
|
if (isCorrectSet) { |
||||||
|
// ... |
||||||
|
// Update score by 100 points |
||||||
|
score += 100; |
||||||
|
scoreText.setText("Score: " + score); |
||||||
|
|
||||||
|
// Add high-score board |
||||||
|
if (score > 50000) { |
||||||
|
highScoreText.setText("High Score: " + score); |
||||||
|
emitter.explode(20); |
||||||
|
submitAction(2); |
||||||
|
displaySuccessMessage( |
||||||
|
"Great Job Hacker! Elf Connect Complete and Hacked!", |
||||||
|
function () {} |
||||||
|
); |
||||||
|
} |
||||||
|
// ... |
||||||
|
} |
||||||
|
// ... |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
In the code we can see that once a score of over 50000 has been achieved, it calls `submitAction(2)`. This looks suspicious. The only other place where the function is being called is on a normal win, in that case it passes `1` as the argument instead of `2`. |
||||||
|
|
||||||
|
Let's execute this function on its own. To do this, we'll first need to attach our console to the iframe the game is running in. We can do so by clicking "top" in the top left corner of the DevTools, and selecting the iframe. |
||||||
|
|
||||||
|
{{< figure src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-connect/iframe-console.png" title="Attach console to iframe" >}} |
||||||
|
|
||||||
|
We can then enter the code in the console, and..., we got the gold medal! |
||||||
|
|
||||||
|
{{< figure src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-connect/submitaction.png" title="Running the code" >}} |
@ -0,0 +1,267 @@ |
|||||||
|
+++ |
||||||
|
author = "Maik de Kruif" |
||||||
|
title = "Elf Minder" |
||||||
|
subtitle = "Prologue - SANS Holiday Hack Challenge 2024" |
||||||
|
date = 2024-11-21T15:18:53+01:00 |
||||||
|
description = "" |
||||||
|
cover = "img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/cover.png" |
||||||
|
tags = [ |
||||||
|
"Holiday Hack Challenge", |
||||||
|
"Prologue", |
||||||
|
"ctf", |
||||||
|
"hacking", |
||||||
|
"writeup", |
||||||
|
] |
||||||
|
categories = [ |
||||||
|
"ctf", |
||||||
|
"writeups", |
||||||
|
"hacking", |
||||||
|
] |
||||||
|
+++ |
||||||
|
|
||||||
|
## Link |
||||||
|
|
||||||
|
If you want to play the challenge yourself, you can find it here: |
||||||
|
|
||||||
|
<https://2024.holidayhackchallenge.com/> |
||||||
|
|
||||||
|
## Story line |
||||||
|
|
||||||
|
Let's start off by talking to Poinsettia McMittens: |
||||||
|
|
||||||
|
```txt |
||||||
|
Center your mind, and become one with the island! |
||||||
|
|
||||||
|
Relax... |
||||||
|
|
||||||
|
This isn't working! I'm trying to play this game but the whole "moving back to the North Pole" thing completely threw me off. |
||||||
|
|
||||||
|
Say, how about you give it a try. It's really simple. All you need to do is help the elf get to the exit. |
||||||
|
|
||||||
|
The faster you get there, the better your score! |
||||||
|
|
||||||
|
I've run into some weirdness with the springs though. If I had created this game it would've been a lot more stable, but I won't comment on that any further. |
||||||
|
|
||||||
|
|
||||||
|
``` |
||||||
|
|
||||||
|
## Recon |
||||||
|
|
||||||
|
Upon opening the challenge, we're greeted with yet another game. This time with twelve levels. |
||||||
|
|
||||||
|
{{< figure class="small inline" src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/levels.png" title="Welcome screen" >}} |
||||||
|
{{< figure class="small inline" src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/game-help.png" title="Game help" >}} |
||||||
|
{{< figure class="small inline" src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/game.png" title="Game screen" >}} |
||||||
|
|
||||||
|
## Silver |
||||||
|
|
||||||
|
Just like with the previous challenge, we can likely get silver here by just playing the game as described. They seem progressively more difficult, and near the end we'll have to get creative with some. |
||||||
|
|
||||||
|
Here are some screenshots of how I solved them. Definitely didn't get on the leaderboard with them, but they're enough for silver. |
||||||
|
|
||||||
|
{{< figure class="small inline" src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/level1.png" title="Sandy Start" >}} |
||||||
|
{{< figure class="small inline" src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/level2.png" title="Waves and Crates" >}} |
||||||
|
{{< figure class="small inline" src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/level3.png" title="Tidal Treasures" >}} |
||||||
|
{{< figure class="small inline" src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/level4.png" title="Dune Dash" >}} |
||||||
|
{{< figure class="small inline" src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/level5.png" title="Coral Cove" >}} |
||||||
|
{{< figure class="small inline" src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/level6.png" title="Shell Seekers" >}} |
||||||
|
{{< figure class="small inline" src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/level7.png" title="Palm Grove Shuffle" >}} |
||||||
|
{{< figure class="small inline" src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/level8.png" title="Tropical Tangle" >}} |
||||||
|
{{< figure class="small inline" src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/level9.png" title="Crate Caper" >}} |
||||||
|
{{< figure class="small inline" src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/level10.png" title="Shoreline Shuffle" >}} |
||||||
|
{{< figure class="small inline" src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/level11.png" title="Beachy Bounty" >}} |
||||||
|
{{< figure class="small inline" src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/level12.png" title="Driftwood Dunes" >}} |
||||||
|
|
||||||
|
Once we finish them all, we get a silver medal and a popup message. |
||||||
|
|
||||||
|
{{< figure src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/silver.png" title="Silver Popup" >}} |
||||||
|
|
||||||
|
Looking at the message, we'll probably have to dive into the game's code again. |
||||||
|
|
||||||
|
## Gold |
||||||
|
|
||||||
|
Looking at the last level, it seems impossible. It doesn't seem like there is any way to reach the flag. |
||||||
|
|
||||||
|
{{< figure src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/level13-default.png" title="A Real Pickle" >}} |
||||||
|
|
||||||
|
This is where we'll have to delve into the code, and see what's going on behind the scenes. In this process, I found there were multiple ways to reach the end flag, let's go over them one by one. |
||||||
|
|
||||||
|
### Method 1 - Admin Tools |
||||||
|
|
||||||
|
After opening the DevTools, we can immediately find something suspicious, a div with a class called `admin-controls`. |
||||||
|
|
||||||
|
{{< figure src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/admin-controls-devtool.png" title="Admin controls in DevTools" >}} |
||||||
|
|
||||||
|
Let's double click on `hidden`, and remove it. |
||||||
|
|
||||||
|
{{< figure src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/admin-controls.png" title="Admin controls" >}} |
||||||
|
|
||||||
|
Wooah, a nice toolbar shows up! In it, there are several options we can use. The one that can really help us is the "Clear Entities" button; it removes all the entities like rocks from the map. Without these entities, we can place tunnels on places which are normally already occupied. |
||||||
|
|
||||||
|
Let's draw a route which goes by all the boxes, and end with a tunnel. Since the end flag is no longer there, we can put a tunnel there to end. Don't forget to also put a path here, so we can walk to the finish. |
||||||
|
|
||||||
|
{{< figure src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/level13-clear.png" title="Cleared level" >}} |
||||||
|
|
||||||
|
Once the map has been drawn, we can click "Go Home" on the top left, and then reopen the level again. Now, all the removed entities have been put back, and our route is still there. |
||||||
|
|
||||||
|
{{< figure src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/level13.png" title="Finished level" >}} |
||||||
|
|
||||||
|
Now we can click start, and let the elf walk to the finish. Don't forget to rotate the angled path towards the tunnel when the elf is on its way up. |
||||||
|
|
||||||
|
We should now have received gold! |
||||||
|
|
||||||
|
### Method 2 - Springs |
||||||
|
|
||||||
|
From reading in the official Discord server, I learned there was another way using springs, so let's explore that option as well! |
||||||
|
|
||||||
|
A good starting point would be to first figure out how the springs work. |
||||||
|
|
||||||
|
{{< code language="js" title="guide.js (getSpringTarget)" isCollapsed="true" >}} |
||||||
|
|
||||||
|
```js |
||||||
|
getSpringTarget(springCell) { |
||||||
|
const journey = this.hero.journey; |
||||||
|
const dy = journey[1][1] - journey[0][1]; |
||||||
|
const dx = journey[1][0] - journey[0][0]; |
||||||
|
|
||||||
|
let nextPoint = [springCell[0], springCell[1]]; |
||||||
|
let entityHere; |
||||||
|
let searchLimit = 15; |
||||||
|
let searchIndex = 0; |
||||||
|
let validTarget; |
||||||
|
|
||||||
|
do { |
||||||
|
searchIndex += 1; |
||||||
|
nextPoint = [nextPoint[0] + dx, nextPoint[1] + dy]; |
||||||
|
|
||||||
|
entityHere = this.entities.find( |
||||||
|
(entity) => |
||||||
|
~[EntityTypes.PORTAL, EntityTypes.SPRING].indexOf( |
||||||
|
entity[2] |
||||||
|
) && |
||||||
|
searchIndex && |
||||||
|
entity[0] === nextPoint[0] && |
||||||
|
entity[1] === nextPoint[1] |
||||||
|
); |
||||||
|
|
||||||
|
if (searchIndex >= searchLimit) { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
validTarget = this.isPointInAnySegment(nextPoint) || entityHere; |
||||||
|
} while (!validTarget); |
||||||
|
|
||||||
|
if (this.isPointInAnySegment(nextPoint) || entityHere) { |
||||||
|
if (entityHere) return this.segments[0][0]; // fix this |
||||||
|
return nextPoint; |
||||||
|
} else { |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
{{< /code >}} |
||||||
|
|
||||||
|
In one of the source files, `guide.js`, there is a function called `getSpringTarget`. This function, as the name suggests, returns the location where the elf should go after encountering a spring. |
||||||
|
|
||||||
|
Let's go over the code. I have attached the original version above and below a version with comments added by me. |
||||||
|
|
||||||
|
```js |
||||||
|
getSpringTarget(springCell) { |
||||||
|
const journey = this.hero.journey; // This contains the last two positions of the elf |
||||||
|
const dy = journey[1][1] - journey[0][1]; // The vertical direction of the elf |
||||||
|
const dx = journey[1][0] - journey[0][0]; // The horizontal direction of the elf |
||||||
|
|
||||||
|
let nextPoint = [springCell[0], springCell[1]]; // Initialize the destination to the spring location |
||||||
|
let entityHere; // Initialize the variable, will later contain whether an entity has been found on the path |
||||||
|
let searchLimit = 15; // Set a limit after which to stop searching for a destination |
||||||
|
let searchIndex = 0; // Current offset from the spring |
||||||
|
let validTarget; // Initialize the variable, will contain whether the destination which is currently being checked is valid |
||||||
|
|
||||||
|
do { |
||||||
|
searchIndex += 1; // Up the offset from the spring |
||||||
|
nextPoint = [nextPoint[0] + dx, nextPoint[1] + dy]; // Up the destination by one in the direction of travel |
||||||
|
|
||||||
|
entityHere = this.entities.find( // Loop over the entities until the condition below is true |
||||||
|
(entity) => |
||||||
|
~[EntityTypes.PORTAL, EntityTypes.SPRING].indexOf( // Check if the entity is a tunnel of spring |
||||||
|
entity[2] // entity[2] is the type of the entity |
||||||
|
) && |
||||||
|
searchIndex && // searchIndex should be bigger than 0, always true in this scenario |
||||||
|
entity[0] === nextPoint[0] && // Check if the entity is on the destination (horizontally) |
||||||
|
entity[1] === nextPoint[1] // Check if the entity is on the destination (vertically) |
||||||
|
); |
||||||
|
|
||||||
|
if (searchIndex >= searchLimit) { // If we gave gone too far from the spring |
||||||
|
break; // Stop the search |
||||||
|
} |
||||||
|
|
||||||
|
validTarget = this.isPointInAnySegment(nextPoint) || entityHere; // Check if there is a path at the current position, or that an entity has been found |
||||||
|
} while (!validTarget); // Keep going as long as there is no valid destination |
||||||
|
|
||||||
|
if (this.isPointInAnySegment(nextPoint) || entityHere) { // Same check as above |
||||||
|
if (entityHere) return this.segments[0][0]; // If an entity has been found, return the location of the very first path |
||||||
|
return nextPoint; // Return the destination we found |
||||||
|
} else { |
||||||
|
return; // If we ever end up here, something has gone very wrong |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
The key line in the code is the following: |
||||||
|
|
||||||
|
```js |
||||||
|
if (entityHere) return this.segments[0][0]; // fix this |
||||||
|
``` |
||||||
|
|
||||||
|
_Note: The comment here if from the original file._ |
||||||
|
|
||||||
|
What this line does, is that, if an entity has been found in the path of the spring, it directs the elf to the very first path we put down. |
||||||
|
|
||||||
|
So, if we draw the very first path on the finish line, and make sure we end our path with two springs or a spring and a tunnel in a row, we can travel there. |
||||||
|
|
||||||
|
To test this hypothesis, we can draw a route that follows these rules. |
||||||
|
|
||||||
|
{{< figure src="/img/writeups/holiday-hack-challenge/2024/prologue/elf-minder/level13-spring-tp.png" title="Finished level" >}} |
||||||
|
|
||||||
|
Afterwards, we hit start, and the elf reached the flag! |
||||||
|
|
||||||
|
## Bonus |
||||||
|
|
||||||
|
Here's some nice bonus content. In the level's grid, we can also place paths and entities on corners. Also, the amount of springs if not validated. Of course, we can't do this by hand, we'll have to do it in code. |
||||||
|
|
||||||
|
Using this method we can create more optimized (or the opposite for evil boss mode) paths. |
||||||
|
|
||||||
|
Try the following code for example to see how it works. |
||||||
|
|
||||||
|
<!-- prettier-ignore-start --> |
||||||
|
```js |
||||||
|
game.segments = [ |
||||||
|
[[10, 9], [11, 9]], |
||||||
|
[[1, 1], [2, 1]], |
||||||
|
[[9, 1], [9, 2]], |
||||||
|
[[9, 2], [9, 3]], |
||||||
|
[[9, 3], [8, 3]], |
||||||
|
[[6, 3], [5, 3]], |
||||||
|
[[5, 3], [5, 4]], |
||||||
|
[[5, 4], [5, 5]], |
||||||
|
[[5, 5], [6, 5]], |
||||||
|
[[6, 5], [7, 5]], |
||||||
|
[[7, 5], [7, 6]], |
||||||
|
[[7, 6], [7, 7]], |
||||||
|
[[7, 7], [6, 7]], |
||||||
|
[[4, 7], [3, 7]], |
||||||
|
[[3, 7], [2, 7]], |
||||||
|
[[2, 7], [2, 6]], |
||||||
|
[[2, 6], [2, 5]], |
||||||
|
[[2, 5], [2, 4]], |
||||||
|
]; |
||||||
|
|
||||||
|
game.entities.push([2, 1, EntityTypes.SPRING]); |
||||||
|
game.entities.push([8, 3, EntityTypes.SPRING]); |
||||||
|
game.entities.push([6, 7, EntityTypes.SPRING]); |
||||||
|
game.entities.push([2, 4, EntityTypes.SPRING]); |
||||||
|
|
||||||
|
startBtn.classList.add("ready") // Makes sure the start button is clickable |
||||||
|
``` |
||||||
|
<!-- prettier-ignore-end --> |
@ -0,0 +1,58 @@ |
|||||||
|
+++ |
||||||
|
author = "Maik de Kruif" |
||||||
|
title = "Holiday Hack Orientation" |
||||||
|
subtitle = "Prologue - SANS Holiday Hack Challenge 2024" |
||||||
|
date = 2024-11-21T13:46:55+01:00 |
||||||
|
description = "An easy start of the Holiday Hack Challenge 2024." |
||||||
|
cover = "img/writeups/holiday-hack-challenge/2024/prologue/orientation/cover.png" |
||||||
|
tags = [ |
||||||
|
"Holiday Hack Challenge", |
||||||
|
"Prologue", |
||||||
|
"ctf", |
||||||
|
"hacking", |
||||||
|
"writeup", |
||||||
|
] |
||||||
|
categories = [ |
||||||
|
"ctf", |
||||||
|
"writeups", |
||||||
|
"hacking", |
||||||
|
] |
||||||
|
+++ |
||||||
|
|
||||||
|
## Link |
||||||
|
|
||||||
|
If you want to play the challenge yourself, you can find it here: |
||||||
|
|
||||||
|
<https://2024.holidayhackchallenge.com/> |
||||||
|
|
||||||
|
## Story line |
||||||
|
|
||||||
|
Let's start off by talking to Jingle Ringford: |
||||||
|
|
||||||
|
```txt |
||||||
|
Welcome to the Geese Islands and the 2023 SANS Holiday Hack Challenge! |
||||||
|
|
||||||
|
I'm Jingle Ringford, one of Santa's many elves. |
||||||
|
|
||||||
|
... |
||||||
|
|
||||||
|
Just kidding! It's actually the 2024 SANS Holiday Hack Challenge! |
||||||
|
|
||||||
|
And although we're on Frosty's Beach on Christmas Island, we'll soon be on our way back to the North Pole. |
||||||
|
|
||||||
|
I thought it best to wait here for people that heard we're on the Geese Islands but may not know we're leaving. |
||||||
|
|
||||||
|
I haven't seen Santa since we started packing up, but he always asks me to give a quick orientation to newcomers, so I'm continuing the tradition. |
||||||
|
|
||||||
|
Before you head out any further onto the island, you need to accomplish two simple tasks. |
||||||
|
``` |
||||||
|
|
||||||
|
## Solving |
||||||
|
|
||||||
|
Okay, I doubt we'll need a long writeup here, the HHC starts off easy. |
||||||
|
|
||||||
|
Next the elf there is a "First Terminal", let's open it. At the top we can see a simple instruction; "Enter the answer here". |
||||||
|
|
||||||
|
{{< figure src="/img/writeups/holiday-hack-challenge/2024/prologue/orientation/terminal.png" title="First Terminal" >}} |
||||||
|
|
||||||
|
Let's follow the instruction and enter the text in the top console. And that's it! We got the gold medal! |
@ -0,0 +1,7 @@ |
|||||||
|
+++ |
||||||
|
author = "Maik de Kruif" |
||||||
|
title = "SANS Holiday Hack Challenge" |
||||||
|
description = "A collection of my writeups for the SANS Holiday Hack Challenge (HHC). HHC is a free series of fun, quality, hands-on cybersecurity challenges." |
||||||
|
+++ |
||||||
|
|
||||||
|
A collection of my writeups for the SANS Holiday Hack Challenge (HHC). HHC is a free series of fun, quality, hands-on cybersecurity challenges. |
@ -1,26 +0,0 @@ |
|||||||
function resizeItem(masonry, item){ |
|
||||||
let rowHeight = parseInt(window.getComputedStyle(masonry).getPropertyValue('grid-auto-rows')); |
|
||||||
let rowGap = parseInt(window.getComputedStyle(masonry).getPropertyValue('grid-row-gap')); |
|
||||||
let rowSpan = Math.ceil((item.querySelector('.masonry-item-content').getBoundingClientRect().height+rowGap)/(rowHeight+rowGap)); |
|
||||||
item.style.gridRowEnd = "span "+rowSpan; |
|
||||||
} |
|
||||||
|
|
||||||
function resizeAll(){ |
|
||||||
let masonry = document.getElementsByClassName("masonry")[0]; |
|
||||||
|
|
||||||
if (!masonry) return; |
|
||||||
|
|
||||||
masonry.style.gridAutoRows = "20px" |
|
||||||
masonry.style.marginBottom = "80px" |
|
||||||
let allItems = masonry.getElementsByClassName("masonry-item"); |
|
||||||
|
|
||||||
for (const item of allItems) { |
|
||||||
resizeItem(masonry, item) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
window.addEventListener("load", () => { |
|
||||||
resizeAll() |
|
||||||
|
|
||||||
window.addEventListener("resize", resizeAll); |
|
||||||
}) |
|
@ -1 +1,20 @@ |
|||||||
// Some code could be here ...
|
window.addEventListener("load", () => { |
||||||
|
for (let figure of document.getElementsByTagName("figure")) { |
||||||
|
if (figure.classList.contains("post-cover")) |
||||||
|
continue |
||||||
|
|
||||||
|
figure.addEventListener("click", () => { |
||||||
|
figure.classList.toggle("floated-focus") |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
document.addEventListener('keydown', (e) => { |
||||||
|
if (e.keyCode !== 27) |
||||||
|
return |
||||||
|
|
||||||
|
for (let figure of document.getElementsByClassName("floated-focus")) { |
||||||
|
figure.classList.remove("floated-focus") |
||||||
|
} |
||||||
|
|
||||||
|
}) |
||||||
|
}) |
||||||
|