Level 1
Level 1
- Points
- 25
- Flag
-
CTF{CapTA1n-cRUCh}
Do you know how websockets work?
Adding an Expandable GUI
The last thing I want to add was a web-based “controller”, which can steer the ship on-click and start new levels. I moved all my code from a local .js
file to CodePen for instant page regeneration and accessability by teammates. Here’s the HTML:
<p>Start Level:</p><button id="lvl0">Level 1</button>
<p>Steer Ships:</p><button id="steer0">Steer 0</button>
Here’s the JS that adds functionality to these buttons. Note that these are made to be scalable/“future-proof”, meaning I can freely add more buttons without needing to copy/paste slight alterations of the same code. I also made some changes upon switching to the CodePen, including deleting the require()
method and preventing level 1 from automatically starting on-open:
// Make sure you install WebSocket with "npm i ws"!const WebSocket = require('ws');// Regex so that I can freely paste the URL when the instance is changedconst url = "https://[REDACTED].challenge.hackazon.org/";// Opens WebSocket connectionconst socket = new WebSocket(`wss://${url.replace(/^https?:\/\//, "")}ws`);// Object literal for level lookupconst passwords = [{ level: 1, password: "" }];
// Runs on socket open, equivalent to .addEventListener()socket.onopen = function() { console.log("[+] Connected!"); // Converts object to string socket.send(JSON.stringify({ "type": "START_GAME", "level": 1 }));};29 collapsed lines
// Pretend there's stuff here
// Assigns onclick listeners for each level buttonfindAll("lvl").forEach(function(element, index) { element.onclick = function() { socket.send(JSON.stringify({ type: "START_GAME", level: passwords[index].level, password: passwords[index].password })); };});
// Assigns onclick listeners for each steer buttonfindAll("steer").forEach(function(element, index) { element.onclick = function() { socket.send(JSON.stringify({ type: "SHIP_STEER", shipId: `${index}` })); };});
// Creates DOM array for each element with name id + intfunction findAll(id) { let i = 0; let list = []; while (document.getElementById(id + i)) { list[i] = document.getElementById(id + i); i++; } return list;}
The preview on CodePen will look something like this:
Let’s see if it actually works:
We could totally flag the challenge right now, but currently there’s no way to see the filtered output we created. I know there’s a “Console” button at the bottom-left of CodePen, but I’d like to see the output on the actual webpage, outside of the IDE. To do this, let’s create a log()
function to append strings to a <textarea>
we’ll add in the HTML:
const text = document.getElementById("textarea");25 collapsed lines
// Pretend there's stuff here
// For each ship in obj.ships, push class object into ships array for(const i of obj.ships) { ships.push(new Ship(i.id, i.area[0], i.area[1], i.direction)); } // Call the string literal getter for(const i of ships) { console.log(i.printState); log(i.printState); } } else { log(JSON.stringify(JSON.parse(event.data))); }};32 collapsed lines
function log(str) { text.value += "\n" + str; text.value = text.value.substring(text.value.length - 10000); text.scrollTop = text.scrollHeight;}
We’ll also spice up the page slightly with flexboxes, a <fieldset>
and some CSS:
<div class="flex-container"> <div> <fieldset> <p>Start Level:</p> <div> <button id="lvl0">Level 1</button> </div> <p>Steer Ships:</p> <div> <button id="steer0">Steer 0</button> </div> </fieldset> </div> <div> <textarea id="textarea" cols="80" rows="20"></textarea> </div></div>
.flex-container { display: flex; flex-wrap: nowrap; justify-content: center; gap: 10px;}
body { background-color: #1d1f21; color: #c9cacc; font-size: 12px;}
fieldset { text-align: center; font-family: 'Trebuchet MS';}
textarea { font-family: 'Courier New';}
p { margin-top: 5px; margin-bottom: 5px;}
button { border: none; cursor: pointer; height: 25px; padding: 0px 10px; border-radius: 10px; color: #222; font-size: 11px;}
Here’s the preview now:
Sorry I was being extra. Let’s flag the challenge now (sped up):
...ID: 0 | (688, 115) (748, 383) | DIR: UPID: 0 | (688, 115) (748, 383) | DIR: UPID: 0 | (688, 111) (748, 379) | DIR: UP{"type":"WIN","flag":"CTF{CapTA1n-cRUCh}"}
We’ve succesfully completed Level 1!