Holiday Hack 2024: Santa Vision
Introduction
In the DMZ between Ribb Bonbowford stands near the closed North Pole Monitoring Station and the Santa Vision terminal:
Ribb explains the Santa Broadcast Network (SBN) and lays out the challenge:
Ribb Bonbowford
Hi, Ribb Bonbowford here, ready to guide you through the SantaVision dilemma!
The Santa Broadcast Network (SBN) has been hijacked by Wombley’s goons—they’re using it to spread propaganda and recruit elves! And Alabaster joined in out of necessity. Quite the predicament, isn’t it?
To access this challenge, use this terminal to access your own instance of the SantaVision infrastructure.
Once it’s done baking, you’ll see an IP address that you’ll need to scan for listening services.
Our target is the technology behind the SBN. We need make a key change to its configuration.
We’ve got to remove their ability to use their admin privileges. This is a delicate maneuver—are you ready?
We need to change the application so that multiple administrators are not permitted. A misstep could cause major issues, so precision is key.
Once that’s done, positive, cooperative images will return to the broadcast. The holiday spirit must prevail!
This means connecting to the network and pinpointing the right accounts. Don’t worry, we’ll get through this.
Let’s ensure the broadcast promotes unity among the elves. They deserve to see the season’s spirit, don’t you think?
Remember, it’s about cooperation and togetherness. Let’s restore that and bring back the holiday cheer. Best of luck!
The first step to unraveling this mess is gaining access to the SantaVision portal. You’ll need the right credentials to slip through the front door—what username will get you in?
Santa Vision A
Silver
Set Up Instance
Clicking on the terminal launches a page reminicent of the Satellite Ground Station Control Panel from 2023:
Clicking the gator in the bottom right opens the GateXOR terminal:
The Time Travel button starts up an instance for me, and the Collapse button tears that down. Clicking Time Travel generates an IP for me to attack:
Port Enumeration
I’ll scan the target with nmap
to see what ports are open. I’m scanning all ports (-p-
) and telling it to go relatively fast (--min-rate 5000
):
oxdf@hacky$ nmap -p- --min-rate 5000 34.30.227.116
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-12-05 19:15 GMT
Nmap scan report for 116.227.30.34.bc.googleusercontent.com (34.30.227.116)
Host is up (0.047s latency).
Not shown: 65523 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
25/tcp filtered smtp
111/tcp filtered rpcbind
135/tcp filtered msrpc
137/tcp filtered netbios-ns
138/tcp filtered netbios-dgm
139/tcp filtered netbios-ssn
445/tcp filtered microsoft-ds
1883/tcp open mqtt
5355/tcp filtered llmnr
8000/tcp open http-alt
9001/tcp open tor-orport
Nmap done: 1 IP address (1 host up) scanned in 13.35 seconds
I’ll rescan the open ports with -sCV
to get more information:
oxdf@hacky$ nmap -p 22,1883,8000,9001 -sCV 34.30.227.116
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-12-05 19:16 GMT
Nmap scan report for 116.227.30.34.bc.googleusercontent.com (34.30.227.116)
Host is up (0.020s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u3 (protocol 2.0)
| ssh-hostkey:
| 256 3d:ac:bc:06:7e:47:5d:89:bd:47:a2:93:54:ae:6e:53 (ECDSA)
|_ 256 a5:3c:cf:2b:0b:19:39:b9:0d:7f:ad:2b:d0:89:9e:bd (ED25519)
1883/tcp open mqtt
|_mqtt-subscribe: Connection rejected: Not Authorized
8000/tcp open http-alt gunicorn
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.0 404 NOT FOUND
| Server: gunicorn
| Date: Thu, 05 Dec 2024 20:43:31 GMT
| Connection: close
| Content-Type: text/html; charset=utf-8
| Content-Length: 1820
| Vary: Cookie
| <!DOCTYPE html>
| <html lang="en">
| <head>
| <meta charset="utf-8">
| <title>Santa Vision</title>
| <!-- meta -->
| <meta name="description" content="">
| <meta name="author" content="">
| <meta name="viewport" content="width=device-width,initial-scale=1">
| <!-- styles -->
| <!-- CSS only -->
| <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
| <link rel="stylesheet" href="/static/css/styles.css">
| </head>
| <body>
| <!-- Navigation -->
| <header class="p-3 mb-3 text-bg-dark">
| <div class="container">
| <div class="d-flex flex-
| GenericLines:
| HTTP/1.1 400 Bad Request
| Connection: close
| Content-Type: text/html
| Content-Length: 193
| <html>
| <head>
| <title>Bad Request</title>
| </head>
| <body>
| <h1><p>Bad Request</p></h1>
| Invalid Request Line 'Invalid HTTP request line: '''
| </body>
| </html>
| GetRequest:
| HTTP/1.0 200 OK
| Server: gunicorn
| Date: Thu, 05 Dec 2024 20:43:26 GMT
| Connection: close
| Content-Type: text/html; charset=utf-8
| Content-Length: 2946
| Vary: Cookie
| Set-Cookie: svCookie=jL9CPksSS2kPHH-JXBstbgBmGtXPHyH9OPQHGIsuv8A; Expires=Sun, 05 Jan 2025 20:43:26 GMT; HttpOnly; Path=/
| <!DOCTYPE html>
| <html lang="en">
| <head>
| <meta charset="utf-8">
| <title>Santa Vision</title>
| <!-- meta -->
| <meta name="description" content="">
| <meta name="author" content="">
| <meta name="viewport" content="width=device-width,initial-scale=1">
| <!-- styles -->
| <!-- CSS only -->
| <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
| <link rel="stylesheet" href="/static/css/styles.css">
| </head>
|_ <body>
|_http-server-header: gunicorn
|_http-title: Santa Vision
9001/tcp open tor-orport?
| fingerprint-strings:
| JavaRMI, Radmin, SSLSessionReq, SSLv23SessionReq, TLSSessionReq, mongodb, tarantool:
| HTTP/1.0 403 Forbidden
| content-type: text/html
| content-length: 173
|_ <html><head><meta charset=utf-8 http-equiv="Content-Language" content="en"/><link rel="stylesheet" type="text/css" href="/error.css"/></head><body><h1>403</h1></body></html>
2 services unrecognized despite returning data. If you know the service/version, please submit the following fingerprints at https://nmap.org/cgi-bin/submit.cgi?new-service :
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
SF-Port8000-TCP:V=7.94SVN%I=7%D=12/5%Time=6751FC21%P=x86_64-pc-linux-gnu%r
SF:(GenericLines,11E,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nConnection:\x2
SF:0close\r\nContent-Type:\x20text/html\r\nContent-Length:\x20193\r\n\r\n<
SF:html>\n\x20\x20<head>\n\x20\x20\x20\x20<title>Bad\x20Request</title>\n\
SF:x20\x20</head>\n\x20\x20<body>\n\x20\x20\x20\x20<h1><p>Bad\x20Request</
SF:p></h1>\n\x20\x20\x20\x20Invalid\x20Request\x20Line\x20'Invalid\x2
SF:0HTTP\x20request\x20line:\x20'''\n\x20\x20</body>\n</htm
SF:l>\n")%r(GetRequest,CA6,"HTTP/1\.0\x20200\x20OK\r\nServer:\x20gunicorn\
SF:r\nDate:\x20Thu,\x2005\x20Dec\x202024\x2020:43:26\x20GMT\r\nConnection:
SF:\x20close\r\nContent-Type:\x20text/html;\x20charset=utf-8\r\nContent-Le
SF:ngth:\x202946\r\nVary:\x20Cookie\r\nSet-Cookie:\x20svCookie=jL9CPksSS2k
SF:PHH-JXBstbgBmGtXPHyH9OPQHGIsuv8A;\x20Expires=Sun,\x2005\x20Jan\x202025\
SF:x2020:43:26\x20GMT;\x20HttpOnly;\x20Path=/\r\n\r\n<!DOCTYPE\x20html>\n<
SF:html\x20lang=\"en\">\n\x20\x20<head>\n\x20\x20\x20\x20<meta\x20charset=
SF:\"utf-8\">\n\x20\x20\x20\x20<title>Santa\x20Vision</title>\n\x20\x20\x2
SF:0\x20<!--\x20meta\x20-->\n\x20\x20\x20\x20<meta\x20name=\"description\"
SF:\x20content=\"\">\n\x20\x20\x20\x20<meta\x20name=\"author\"\x20content=
SF:\"\">\n\x20\x20\x20\x20<meta\x20name=\"viewport\"\x20content=\"width=de
SF:vice-width,initial-scale=1\">\n\x20\x20\x20\x20<!--\x20styles\x20-->\n\
SF:x20\x20\x20\x20<!--\x20CSS\x20only\x20-->\n\x20\x20\x20\x20<link\x20hre
SF:f=\"https://cdn\.jsdelivr\.net/npm/bootstrap@5\.2\.0/dist/css/bootstrap
SF:\.min\.css\"\x20rel=\"stylesheet\"\x20integrity=\"sha384-gH2yIJqKdNHPEq
SF:0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx\"\x20crossorigin=\"a
SF:nonymous\">\n\x20\x20\x20\x20<link\x20rel=\"stylesheet\"\x20href=\"/sta
SF:tic/css/styles\.css\">\n\x20\x20\x20\x20\n\x20\x20</head>\n\x20\x20<bod
SF:y>\n\x20")%r(FourOhFourRequest,7CC,"HTTP/1\.0\x20404\x20NOT\x20FOUND\r\
SF:nServer:\x20gunicorn\r\nDate:\x20Thu,\x2005\x20Dec\x202024\x2020:43:31\
SF:x20GMT\r\nConnection:\x20close\r\nContent-Type:\x20text/html;\x20charse
SF:t=utf-8\r\nContent-Length:\x201820\r\nVary:\x20Cookie\r\n\r\n<!DOCTYPE\
SF:x20html>\n<html\x20lang=\"en\">\n\x20\x20<head>\n\x20\x20\x20\x20<meta\
SF:x20charset=\"utf-8\">\n\x20\x20\x20\x20<title>Santa\x20Vision</title>\n
SF:\x20\x20\x20\x20<!--\x20meta\x20-->\n\x20\x20\x20\x20<meta\x20name=\"de
SF:scription\"\x20content=\"\">\n\x20\x20\x20\x20<meta\x20name=\"author\"\
SF:x20content=\"\">\n\x20\x20\x20\x20<meta\x20name=\"viewport\"\x20content
SF:=\"width=device-width,initial-scale=1\">\n\x20\x20\x20\x20<!--\x20style
SF:s\x20-->\n\x20\x20\x20\x20<!--\x20CSS\x20only\x20-->\n\x20\x20\x20\x20<
SF:link\x20href=\"https://cdn\.jsdelivr\.net/npm/bootstrap@5\.2\.0/dist/cs
SF:s/bootstrap\.min\.css\"\x20rel=\"stylesheet\"\x20integrity=\"sha384-gH2
SF:yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx\"\x20cros
SF:sorigin=\"anonymous\">\n\x20\x20\x20\x20<link\x20rel=\"stylesheet\"\x20
SF:href=\"/static/css/styles\.css\">\n\x20\x20\x20\x20\n\x20\x20</head>\n\
SF:x20\x20<body>\n\x20\x20\x20\x20<!--\x20Navigation\x20-->\n<header\x20cl
SF:ass=\"p-3\x20mb-3\x20text-bg-dark\">\n\x20\x20<div\x20class=\"container
SF:\">\n\x20\x20\x20\x20<div\x20class=\"d-flex\x20flex-");
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
SF-Port9001-TCP:V=7.94SVN%I=7%D=12/5%Time=6751FC21%P=x86_64-pc-linux-gnu%r
SF:(SSLSessionReq,F5,"HTTP/1\.0\x20403\x20Forbidden\r\ncontent-type:\x20te
SF:xt/html\r\ncontent-length:\x20173\r\n\r\n<html><head><meta\x20charset=u
SF:tf-8\x20http-equiv=\"Content-Language\"\x20content=\"en\"/><link\x20rel
SF:=\"stylesheet\"\x20type=\"text/css\"\x20href=\"/error\.css\"/></head><b
SF:ody><h1>403</h1></body></html>")%r(TLSSessionReq,F5,"HTTP/1\.0\x20403\x
SF:20Forbidden\r\ncontent-type:\x20text/html\r\ncontent-length:\x20173\r\n
SF:\r\n<html><head><meta\x20charset=utf-8\x20http-equiv=\"Content-Language
SF:\"\x20content=\"en\"/><link\x20rel=\"stylesheet\"\x20type=\"text/css\"\
SF:x20href=\"/error\.css\"/></head><body><h1>403</h1></body></html>")%r(SS
SF:Lv23SessionReq,F5,"HTTP/1\.0\x20403\x20Forbidden\r\ncontent-type:\x20te
SF:xt/html\r\ncontent-length:\x20173\r\n\r\n<html><head><meta\x20charset=u
SF:tf-8\x20http-equiv=\"Content-Language\"\x20content=\"en\"/><link\x20rel
SF:=\"stylesheet\"\x20type=\"text/css\"\x20href=\"/error\.css\"/></head><b
SF:ody><h1>403</h1></body></html>")%r(JavaRMI,F5,"HTTP/1\.0\x20403\x20Forb
SF:idden\r\ncontent-type:\x20text/html\r\ncontent-length:\x20173\r\n\r\n<h
SF:tml><head><meta\x20charset=utf-8\x20http-equiv=\"Content-Language\"\x20
SF:content=\"en\"/><link\x20rel=\"stylesheet\"\x20type=\"text/css\"\x20hre
SF:f=\"/error\.css\"/></head><body><h1>403</h1></body></html>")%r(Radmin,F
SF:5,"HTTP/1\.0\x20403\x20Forbidden\r\ncontent-type:\x20text/html\r\nconte
SF:nt-length:\x20173\r\n\r\n<html><head><meta\x20charset=utf-8\x20http-equ
SF:iv=\"Content-Language\"\x20content=\"en\"/><link\x20rel=\"stylesheet\"\
SF:x20type=\"text/css\"\x20href=\"/error\.css\"/></head><body><h1>403</h1>
SF:</body></html>")%r(mongodb,F5,"HTTP/1\.0\x20403\x20Forbidden\r\ncontent
SF:-type:\x20text/html\r\ncontent-length:\x20173\r\n\r\n<html><head><meta\
SF:x20charset=utf-8\x20http-equiv=\"Content-Language\"\x20content=\"en\"/>
SF:<link\x20rel=\"stylesheet\"\x20type=\"text/css\"\x20href=\"/error\.css\
SF:"/></head><body><h1>403</h1></body></html>")%r(tarantool,F5,"HTTP/1\.0\
SF:x20403\x20Forbidden\r\ncontent-type:\x20text/html\r\ncontent-length:\x2
SF:0173\r\n\r\n<html><head><meta\x20charset=utf-8\x20http-equiv=\"Content-
SF:Language\"\x20content=\"en\"/><link\x20rel=\"stylesheet\"\x20type=\"tex
SF:t/css\"\x20href=\"/error\.css\"/></head><body><h1>403</h1></body></html
SF:>");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 134.33 seconds
Port 22 is SSH, and unlikely to be something useful to me until I get valid credentials on the system.
Port 1883 is MQTT, an messaging protocol very common in IoT due to it’s minimal resource requirements. nmap
is showing a Not Authorized message. I’ll have to find creds here as well.
Port 8000 has an HTTP server running gunicorn, a Python HTTP server.
Port 9001 looks like an HTTPS server, but it’s also returning a 403. This turns out to be a websocket service used by the website after authenticate to interact with MQTT.
TCP 8000 - Website
The page on 8000 presents a login form:
Some basic SQL injection login bypasses fail. There is a reference to a “topic” named “sitestatus” at the bottom of the page. That’s likely an MQTT reference, as it works by having clients subscribe to get all messages within a given topic.
Looking at bit closer at the page source, there’s an HTML comment near the topic text:
I’ll note the creds elfanon:elfanon. This username provides the silver solve.
Ribb
On solving Silver, Ribb has more to add:
Ribb Bonbowford
Great work! You’ve taken the first step—nicely done. You’re on the silver path and off to a strong start!
(Gold hint) Stay curious. Sometimes, the smallest details—often overlooked—hold the keys to the kingdom. Pay close attention to what’s hidden in the source.
Gold
MQTT as elfanon
I’ll download and install MQTTX, and open it to interact with MQTT. If I try to create a connection without creds:
It fails to connect:
However, when I add the elfanon creds:
It works:
I’m now sitting at the SantaVision connection I created and there’s no data. I’ll need a subscription:
I spent some time trying to enumerate topics from the server, but it seems the elfanon account doesn’t have permissions. I can try with the mosquitto_sub
command line tool, and the result is explicit:
oxdf@hacky$ mosquitto_sub -h 34.71.133.198 -p 1883 -u elfanon -P elfanon -t '#'
All subscription requests were denied.
I’ll click “New Subscription” and add the “sitestatus” topic mentioned on the webpage:
Messages start to come in:
Using this, I’ll collect the following usernames:
- superadmin (succeeded)
- admin (succeeded)
- AlabasterS (succeeded and failed)
- WomblyC (failed)
There’s also a file that gets downloaded:
That looks like a relative path on a webserver.
applicationDefault.bin
I’ll download this file from the webserver:
oxdf@hacky$ wget http://34.71.133.198:8000/static/sv-application-2024-SuperTopSecret-9265193/applicationDefault.bin
--2024-12-27 19:31:03-- http://34.71.133.198:8000/static/sv-application-2024-SuperTopSecret-9265193/applicationDefault.bin
Connecting to 34.71.133.198:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 7340032 (7.0M) [application/octet-stream]
Saving to: ‘applicationDefault.bin’
applicationDefault.bin 100%[===================>] 7.00M 16.3MB/s in 0.4s
2024-12-27 19:31:03 (16.3 MB/s) - ‘applicationDefault.bin’ saved [7340032/7340032]
oxdf@hacky$ file applicationDefault.bin
applicationDefault.bin: Linux jffs2 filesystem data little endian
It’s a jffs2 filesystem. I’ll use jefferson (pipx install jefferson
) to extract files from it:
oxdf@hacky$ jefferson applicationDefault.bin -d fs
dumping fs to /media/sf_CTFs/SansHolidayChallenge-2024/act3/santavision/fs (endianness: <)
Jffs2_raw_inode count: 47
Jffs2_raw_dirent count: 47
writing S_ISREG .bashrc
writing S_ISREG .profile
writing S_ISDIR app
writing S_ISDIR app/src
writing S_ISREG app/src/__init__.py
writing S_ISDIR app/src/accounts
writing S_ISDIR app/src/core
writing S_ISDIR app/src/static
writing S_ISDIR app/src/templates
writing S_ISREG app/src/accounts/__init__.py
writing S_ISREG app/src/accounts/forms.py
writing S_ISREG app/src/accounts/models.py
writing S_ISREG app/src/accounts/views.py
writing S_ISREG app/src/core/__init__.py
writing S_ISREG app/src/core/views.py
writing S_ISDIR app/src/static/DB
writing S_ISREG app/src/static/DS-DIGI.TTF
writing S_ISDIR app/src/static/css
writing S_ISDIR app/src/static/images
writing S_ISDIR app/src/static/js
writing S_ISREG app/src/static/css/styles.css
writing S_ISREG app/src/static/images/login-bg.png
writing S_ISREG app/src/static/images/login.jpg
writing S_ISREG app/src/static/images/logo.png
writing S_ISREG app/src/static/images/monitor1.png
writing S_ISREG app/src/static/images/monitor2.png
writing S_ISREG app/src/static/images/monitor3.png
writing S_ISREG app/src/static/images/monitor4.png
writing S_ISREG app/src/static/images/monitoroff.png
writing S_ISREG app/src/static/images/monitors.png
writing S_ISREG app/src/static/images/nofeed.png
writing S_ISREG app/src/static/images/noimage.png
writing S_ISREG app/src/static/js/jquery.min.js
writing S_ISREG app/src/static/js/mqttJS.js
writing S_ISREG app/src/templates/_base.html
writing S_ISDIR app/src/templates/accounts
writing S_ISDIR app/src/templates/core
writing S_ISDIR app/src/templates/errors
writing S_ISREG app/src/templates/navigation.html
writing S_ISREG app/src/templates/accounts/login.html
writing S_ISREG app/src/templates/accounts/no-token.html
writing S_ISREG app/src/templates/core/index.html
writing S_ISREG app/src/templates/core/invalid-token.html
writing S_ISREG app/src/templates/core/no-token.html
writing S_ISREG app/src/templates/errors/401.html
writing S_ISREG app/src/templates/errors/404.html
writing S_ISREG app/src/templates/errors/500.html
This is the source for the website. There are a bunch of routes defined in app/src/core/views.py
, but also four defined in app/src/accounts/views.py
. /
and /login
handle login, but there’s also two static routes:
@accounts_bp.route("/static/sv-application-2024-SuperTopSecret-9265193/applicationDefault.bin", methods=["GET"])
def firmware():
return send_from_directory("static", "sv-application-2024-SuperTopSecret-9265193/applicationDefault.bin", as_attachment=True)
@accounts_bp.route("/sv2024DB-Santa/SantasTopSecretDB-2024-Z.sqlite", methods=["GET"])
def db():
return send_from_directory("static", "sv2024DB-Santa/SantasTopSecretDB-2024-Z.sqlite", as_attachment=True)
I’ve already got applicationDefault.bin
, but SantasTopSecretDB-2024-Z.sqlite
is new.
Database
I’ll download the database file and open it:
oxdf@hacky$ wget http://34.71.133.198:8000/sv2024DB-Santa/SantasTopSecretDB-2024-Z.sqlite
--2024-12-27 19:51:52-- http://34.71.133.198:8000/sv2024DB-Santa/SantasTopSecretDB-2024-Z.sqlite
Connecting to 34.71.133.198:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 20480 (20K) [application/vnd.sqlite3]
Saving to: ‘SantasTopSecretDB-2024-Z.sqlite’
SantasTopSecretDB-2024-Z.sqlite 100%[====================>] 20.00K --.-KB/s in 0.04s
2024-12-27 19:51:52 (552 KB/s) - ‘SantasTopSecretDB-2024-Z.sqlite’ saved [20480/20480]
oxdf@hacky$ sqlite3 SantasTopSecretDB-2024-Z.sqlite
SQLite version 3.45.1 2024-01-30 16:01:20
Enter ".help" for usage hints.
sqlite> .headers on
sqlite> .tables
alembic_version users
The only interesting table is users
. It has one row:
sqlite> select * from users;
id|username|password|created_on|is_admin
1|santaSiteAdmin|S4n+4sr3411yC00Lp455wd|2024-01-23 06:05:29.466071|1
“santaSiteAdmin” solves gold.
Santa Vision B
Ribb
On solving Santa Vision A, Ribb has more to add about the way ahead:
Ribb Bonbowford
Impressive! You dug deeper and uncovered something hidden—very strategic work. You’re well on your way to gold!
You’ve gained access, but there’s still much more to uncover. Patience and persistence will guide you—silver or gold, you’re making progress!
Now that you’re in, it’s time to go deeper. We need access to the northpolefeeds. This won’t work if you use Wombley or Alabaster’s credentials—find the right user to log in.
Silver
Authenticated Web Enumeration
Logging into the website as elfanon provides access to the “SantaVision” panel:
There are three forms on the page. The top two forms are the targets of this objective. Clicking “Connect to broadcast feed” displays “Power on monitors first!”:
“List Available Clients” shows three:
“List Available Roles” shows four:
It’s worth noting that this is different than the source, where the route shows elfanon and Elf as users:
@core_bp.route("/listClients", methods=["GET"])
@login_required
def listClients():
clientsForPlayerReview = "'elfanon', 'Elf', 'WomblyC', 'AlabasterS'"
return make_response(jsonify(clients=clientsForPlayerReview), 200)
The roles are different in the source as well:
@core_bp.route("/listRoles", methods=["GET"])
@login_required
def listRoles():
rolesForPlayerReview = "'SiteStatusElfRole', 'NorthPoleFeedsAdminRole', 'NorthPoleFeedsElfRole'"
return make_response(jsonify(roles=rolesForPlayerReview), 200)
Identify Camera Feed Port
The next challenge is to turn the monitors on. To do so, I’ll need a username, password, server IP, and port. The first thing I’ll try is the creds I already have, elfanon / elfanon, the target IP, and the MTTQ port (1883). Looking in the browser dev tools, I’ll see it sends a request to /mqqt?clientConnect=elfanon
and then immediately tries to go to the host/port I gave, which fails:
It is making HTTP requests, and there’s one other HTTP port on this server, 9001. I’ll try that:
The 101 response is it switching to websockets, where it sends binary data including the username I entered with “-viewer” appended, as well as the password:
The response is not useful:
Identify User / Password
The question specifically says to use an account that isn’t Alabaster’s or Wombley’s. elfmonitor is a good option here! I’ll need a password. A bit of guessing with the different roles, “SiteElfMonitorRole” works!
The monitors now say “No Feed”, which suggests they are powered on.
Interestingly, this time there’s more traffic on the websocket:
The same four-byte binary response comes, but then other activity. This provides the silver solution, “elfmonitor”.
Entering “northpolefeeds” in the “Broadcast Feed” box and clicking “Connect to broadcast feed” shows images on the screens:
Ribb
Ribb gives a hint towards the gold solution:
Ribb Bonbowford
Excellent progress! You’ve moved us closer to understanding this network—keep it up on the silver path!
(Gold hint) Look beyond the surface. Headers and subtle changes might just open new doors. Pay close attention to everything as you log in.
Gold
Going back to look at the path for logging in, when I log in there’s a POST request to /login
which returns a 302 redirect to /auth
:
When I load /auth
, there’s an unusual and interesting response header:
That header is present regardless of if it’s loaded directly after login or on a refresh. As elfanon, there’s only the one header. But if I log in as santaSiteAdmin, there’s three:
Those creds work to power on the monitors, and putting in “northpolefeeds” and clicking “Connect to broadcast feed” starts the images. It’s worth noting that they creds change on different logins.
Santa Vision C
Ribb
Ribb is cheering me on:
Ribb Bonbowford
That’s the kind of attention to detail we need! You’ve uncovered a hidden path—solid gold effort!
You’re doing fantastic! The northpolefeeds are now in your sights. Silver or gold, you’re pushing forward with great momentum!
We’re getting closer. Now, we need to dig into the frostbitfeed. It’s time to figure out if any other feeds are lurking beneath the surface—and uncover the elves’ secret operation.
Silver
I need to look at feeds, so I’ll log into MQTT with the elfmonitor account and start with the norhtpolefeeds topic. It has a single message:
This is the list of images that rotates across the monitors.
I know there’s a frostbitfeeds topic from the objective and from Ribb. I’ll subscribe to that:
There’s a bunch of uninteresting messages about frostbit, but three other messages jump out:
The .key
file will be important for decrypting the Frostbit ransomware, and the API key will be important for deactivating it. I’ll subscribe to santafeed. It has messages about AlabasterS and WombleyC both being admins, some status messages about Santa, and this:
“Idemcerybu” solves silver.
Ribb
On solving Silver, Ribb has more to add:
Ribb Bonbowford
Wonderful job! You’ve uncovered a critical piece of the puzzle—well on track with the silver approach!
(Gold hint) Sometimes the answers are in the quiet moments. Pay attention to every feed and signal—you may find what you’re looking for hidden deep in the streams.
Gold
This was particularly tricky, and I didn’t see much of a clue as to where to go. But, looking at the operation name from silver, it doesn’t really make much sense as a word, “Idemcerybu”. However, it does become a word under a ROT cipher. The hint of 16 elves is also relevant, as it’s really rotated by -16 characters. rot13.com shows the result (ROT10 == ROT-16):
Santa Vision D
Ribb
Ribb Bonbowford
Brilliant work! You cracked the secret operation’s code on the gold path. Analytical thinking like that will lead to success!
You’re almost there! The operation’s code is unlocked, but the final challenge is waiting. Silver or gold, you’re close to victory!
It’s time to take back control of the Santa Broadcast Network. There really shouldn’t be multiple administrators—send the right message, and Santa’s true spirit will return. What’s Santa test-driving this season?
Silver
Identify Target Message
Looking more at the messages coming to santafeed, I’ve seen messages saying that AlabasterS and WombleyC are both admin role, as well as Santa’s role of superadmin. There’s another message that jumps out as interesting:
I’m looking for a single request that will demote both Alabaster and Wombey - this seems like a good target.
Send Message
Logged into the website as elfanon and then connecting to the feeds as elfmonitor (following the silver path), another form shows up on the page:
I’ll send a message to set singleAdminMode
to true:
On sending:
The images of the snowball war are replaced with images of Santa on a pogo stick!
In MQTTX I’ll see Alabaster and Wombley’s roles change:
And the northpolefeeds
change their message to new images:
The solution to silver is “pogo stick”.
Ribb
Ribb offers congratulations and a hint for gold:
Ribb Bonbowford
Mission accomplished! The airwaves are restored, and the message is one of unity and teamwork. Whether silver or gold, you’ve done an incredible job!
(Gold hint) Think about the kind of ride Santa would take in a world filled with innovation. His vehicle of choice might surprise you—pay attention to the futuristic details.
Excellent! You’ve successfully removed the propaganda and restored the true spirit of the season. A solid silver finish—well done!
Gold
I’ll log out of the site and come back in as santaSiteAdmin. I’ll connect with the santashelper2024 creds to get the feeds. This time, the form to send messages isn’t there:
In MQTTX I’ll send the message to santafeed:
It shows up, followed by the message that santa is coming to the north pole:
The images are different now in northpolefeeds:
And Santa is riding a hovercraft (the gold answer):
Outro
Ribb is pleased:
Ribb Bonbowford
Phenomenal! You’ve figured it out—Santa’s on a new ride. You’ve earned your gold badge with this one!
Santa is relieved as well:
Santa
Finally, the dreadful propaganda is finally taken off the airwaves. That should go a long way towards healing the divide between the elves.
Wombley doesn’t mind either:
Wombley Cube
Oh right, I had forgotten about that broadcast. Thank you for shutting it down. There’s no need for it now that the conflict is no more.
Alabaster is still blaming Wombley:
Alabaster Snowball
I hate to seem childish by saying Wombley started it again, but, well, he did. He took over Santa’s broadcasts in an attempt to recruit more elves to his cause!
But I should have known better than to stoop to his level. As the saying goes, two wrongs don’t make a right.