Introduction

In the DMZ between Ribb Bonbowford stands near the closed North Pole Monitoring Station and the Santa Vision terminal:

image-20241205152837484

Ribb explains the Santa Broadcast Network (SBN) and lays out the challenge:

Ribb Bonbowford

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:

image-20241205153416608

Clicking the gator in the bottom right opens the GateXOR terminal:

image-20241205153635220

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:

image-20241205153829304

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 &#x27;Invalid HTTP request line: &#x27;&#x27;&#x27;
|     </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&#x27;Invalid\x2
SF:0HTTP\x20request\x20line:\x20&#x27;&#x27;&#x27;\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:

image-20241205160937663

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:

image-20241205161139848

I’ll note the creds elfanon:elfanon. This username provides the silver solve.

Ribb

On solving Silver, Ribb has more to add:

Ribb Bonbowford

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:

image-20241227134446638

It fails to connect:

image-20241227134412979

However, when I add the elfanon creds:

image-20241227134520229

It works:

image-20241227134504050

I’m now sitting at the SantaVision connection I created and there’s no data. I’ll need a subscription:

image-20241227141834820

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:

image-20241227142138451

Messages start to come in:

image-20241227142203417

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:

image-20241227142357397

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

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:

image-20241227160258590Click for full size image

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!”:

image-20241227161015948

“List Available Clients” shows three:

image-20241227161055738

“List Available Roles” shows four:

image-20241227161120729

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:

image-20241227160645745

It is making HTTP requests, and there’s one other HTTP port on this server, 9001. I’ll try that:

image-20241227160733018

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:

image-20241227160821830

The response is not useful:

image-20241227160841621

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!

image-20241227161631491

The monitors now say “No Feed”, which suggests they are powered on.

Interestingly, this time there’s more traffic on the websocket:

image-20241227161719858

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:

image-20241227163705769

Ribb

Ribb gives a hint towards the gold solution:

Ribb Bonbowford

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:

image-20241227172252741

When I load /auth, there’s an unusual and interesting response header:

image-20241227172338591

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:

image-20241227172459433

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

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:

image-20241227173059508

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:

image-20241227173322278

There’s a bunch of uninteresting messages about frostbit, but three other messages jump out:

image-20241227173352071 image-20241227173410583 image-20241227173421408

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:

image-20241227173630438

“Idemcerybu” solves silver.

Ribb

On solving Silver, Ribb has more to add:

Ribb Bonbowford

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):

image-20241227200431487

Santa Vision D

Ribb

Ribb Bonbowford

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:

image-20241228064247143

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:

image-20241228064420956

I’ll send a message to set singleAdminMode to true:

image-20241228064451437

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:

image-20241228062528014

And the northpolefeeds change their message to new images:

image-20241228062549146

The solution to silver is “pogo stick”.

image-20241228062600538

Ribb

Ribb offers congratulations and a hint for gold:

Ribb Bonbowford

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:

image-20241228064909685

In MQTTX I’ll send the message to santafeed:

image-20241228063817956

It shows up, followed by the message that santa is coming to the north pole:

image-20241228063851076

The images are different now in northpolefeeds:

image-20241228063924805

And Santa is riding a hovercraft (the gold answer):

image-20241228063945852

Outro

Ribb is pleased:

Ribb Bonbowford

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

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

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

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.