Holiday Hack 2025: Mail Detective
Introduction
Mail Detective
Difficulty:❅❅❅❅❅Maurice Wilson is in City Hall beside the Mail Detective: Curly IMAP Investigation terminal with a laptop, a mobile, a sword, and a pig:
Maurice Wilson
Hey there! I’m Mo, on loan from the Air Force, and let me tell you - Counter Hack is the best job I have ever had!
So here’s our situation: those gnomes have been sending JavaScript-enabled emails to everyone in the neighborhood, and it’s causing chaos.
We had to shut down all the email clients because they weren’t blocking the malicious scripts - kind of like how we’d ground aircraft until we clear a security threat.
The only safe way to access the email server now is through curl - yes, the HTTP tool!
Think you can help me use curl to connect to the IMAP server and hunt down one of these gnome emails?
Chat with Maurice Wilson
Congratulations! You spoke with Maurice Wilson!
The terminal opens up a terminal with two panes with instructions in the MOTD:
Solution
Initial Connection
Build a URL
I touched on the format of a URL in the It’s All About Defang challenge, and included this image from Wikipedia:
I’ll need to make a URL that defines a connection to the IMAP server.
The Internet Assigned Numbers Authority (IANA) maintains a list of URI schemes. From Wikipedia:
URI schemes registered with the IANA, both provisional and fully approved, are listed in its registry for Uniform Resource Identifier (URI) Schemes.[1] These include well known schemes such as:
file- File URI schemeftp– File Transfer Protocolhttp– Hypertext Transfer Protocolhttps– Hypertext Transfer Protocol Securemailto– mailto for email addressestel– for telephone numbersimap– Internet Message Access Protocolirc– Internet Relay Chatnntp– Network News Transfer Protocolas well as many lesser known schemes such as:
acap– Application Configuration Access Protocolicap– Internet Content Adaptation Protocolmtqp– Message Tracking Query Protocol (RFC3887)wss– Encrypted WebSocket connections
imap is an approved scheme, so that seems like what I need here.
The userinfo is:
An optional userinfo subcomponent followed by an at symbol (
@), that may consist of a user name and an optional password preceded by a colon (:). Use of the formatusername:passwordin the userinfo subcomponent is deprecated for security reasons. Applications should not render as clear text any data after the first colon (:) found within a userinfo subcomponent unless the data after the colon is the empty string (indicating no password).
The host is given in the description as the local machine, and the port is 143. Putting that all together, I’ll get:
imap://dosismail:holidaymagic@localhost:143
curl POC
To test this out, I’ll use curl with that URL:
dosismail @ Neighborhood Mail ~$ curl imap://dosismail:holidaymagic@localhost:143
* LIST (\HasNoChildren) "." Spam
* LIST (\HasNoChildren) "." Sent
* LIST (\HasNoChildren) "." Archives
* LIST (\HasNoChildren) "." Drafts
* LIST (\HasNoChildren) "." INBOX
It returns what looks like a list of five directories, each of which has no children directories.
Interacting with Mailbox
Using curl to request web pages over HTTP(S), -X or --request typically defines the HTTP verb (GET, POST, etc). The curl man page shows that it has a different application for IMAP:
-X, --request <method> Change the method to use when starting the transfer. curl passes on the verbatim string you give it its the request without any filter or other safe guards. That includes white space and control characters. HTTP Specifies a custom request method to use when communicating with the HTTP server. The specified request method is used instead of the method otherwise used (which defaults to GET). Read the HTTP 1.1 specification for details and explanations. Common additional HTTP requests include PUT and DELETE, but related technologies like WebDAV offers PROPFIND, COPY, MOVE and more. ...[snip]... IMAP Specifies a custom IMAP command to use instead of LIST.
LIST is the default command, which explains the output above.
EXAMINE
I’ll use the EXAMINE command to get details about each of the directories that came back from LIST:
dosismail @ Neighborhood Mail ~$ curl imap://dosismail:holidaymagic@localhost:143/ -X "EXAMINE Spam"
* FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
* OK [PERMANENTFLAGS ()] Read-only mailbox.
* 3 EXISTS
* 0 RECENT
* OK [UNSEEN 1] First unseen.
* OK [UIDVALIDITY 1762699034] UIDs valid
* OK [UIDNEXT 4] Predicted next UID
dosismail @ Neighborhood Mail ~$ curl imap://dosismail:holidaymagic@localhost:143/ -X "EXAMINE Sent"
* FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
* OK [PERMANENTFLAGS ()] Read-only mailbox.
* 0 EXISTS
* 0 RECENT
* OK [UIDVALIDITY 1762699033] UIDs valid
* OK [UIDNEXT 1] Predicted next UID
dosismail @ Neighborhood Mail ~$ curl imap://dosismail:holidaymagic@localhost:143/ -X "EXAMINE Archives"
* FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
* OK [PERMANENTFLAGS ()] Read-only mailbox.
* 7 EXISTS
* 7 RECENT
* OK [UNSEEN 1] First unseen.
* OK [UIDVALIDITY 1762699032] UIDs valid
* OK [UIDNEXT 8] Predicted next UID
dosismail @ Neighborhood Mail ~$ curl imap://dosismail:holidaymagic@localhost:143/ -X "EXAMINE Drafts"
* FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
* OK [PERMANENTFLAGS ()] Read-only mailbox.
* 2 EXISTS
* 2 RECENT
* OK [UNSEEN 1] First unseen.
* OK [UIDVALIDITY 1762699031] UIDs valid
* OK [UIDNEXT 3] Predicted next UID
dosismail @ Neighborhood Mail ~$ curl imap://dosismail:holidaymagic@localhost:143/ -X "EXAMINE INBOX"
* FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
* OK [PERMANENTFLAGS ()] Read-only mailbox.
* 7 EXISTS
* 0 RECENT
* OK [UNSEEN 1] First unseen.
* OK [UIDVALIDITY 1762699035] UIDs valid
* OK [UIDNEXT 8] Predicted next UID
Every folder but Sent has mail in it.
Spam
Overview
I’ll use the FETCH command to get all emails in the Spam directory and just get the ENVELOPE macro, which is according to RFC-3501:
ENVELOPE The envelope structure of the message. This is computed by the server by parsing the [RFC-2822] header into the component parts, defaulting various fields as necessary.
It returns data about each of the three emails:
dosismail @ Neighborhood Mail ~$ curl imap://dosismail:holidaymagic@localhost:143/Spam -X 'FETCH 1:* (ENVELOPE)'
* 1 FETCH (ENVELOPE ("Mon, 16 Sep 2025 12:05:00 +0000" "Coolant Acquisition Protocol Initiated" (("ATNAS Recon Unit" NIL "recon.unit" "atnas.mail")) (("ATNAS Recon Unit" NIL "recon.unit" "atnas.mail")) (("ATNAS Recon Unit" NIL "recon.unit" "atnas.mail")) (("Counter Hack Innovation Crew" NIL "counterhack.crew" "dosisneighborhood.mail")) NIL (("Old Pete the Gardener" NIL "gardener" "dosisneighborhood.mail")) NIL "<gnome-js-2@atnas.mail>"))
* 2 FETCH (ENVELOPE ("Mon, 16 Sep 2025 12:10:00 +0000" "Frost Protocol: Dosis Neighborhood Freezing Initiative" (("Frozen Network Bot" NIL "frozen.network" "mysterymastermind.mail")) (("Frozen Network Bot" NIL "frozen.network" "mysterymastermind.mail")) (("Frozen Network Bot" NIL "frozen.network" "mysterymastermind.mail")) (("Dosis Neighborhood Residents" NIL "dosis.residents" "dosisneighborhood.mail")) (("Jessica and Joshua" NIL "siblings" "dosisneighborhood.mail")("CHI Team" NIL "chi.team" "counterhack.com")) NIL NIL "<gnome-js-3@mysterymastermind.mail>"))
* 3 FETCH (ENVELOPE ("Mon, 16 Sep 2025 12:00:00 +0000" "Your Refrigerator Systems Compromised!" (("Frost's Minion" NIL "frost.minion" "atnas.mail")) (("Frost's Minion" NIL "frost.minion" "atnas.mail")) (("Frost's Minion" NIL "frost.minion" "atnas.mail")) (("Duke Dosis" NIL "duke.dosis" "dosisneighborhood.mail")) (("Counter Hack Crew" NIL "team" "counterhack.mail")) NIL NIL "<gnome-js-1@atnas.mail>"))
Mail 1
I’ll get the full email with -X 'FETCH 1 BODY[TEXT]' and -v:
dosismail @ Neighborhood Mail ~$ curl imap://dosismail:holidaymagic@localhost:143/Spam -X 'FETCH 1 BODY[TEXT]' -v
* Trying 127.0.0.1:143...
* Connected to localhost (127.0.0.1) port 143 (#0)
< * OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE LITERAL+ STARTTLS AUTH=PLAIN AUTH=LOGIN] Dovecot (Ubuntu) ready.
> A001 CAPABILITY
< * CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE LITERAL+ STARTTLS AUTH=PLAIN AUTH=LOGIN
< A001 OK Pre-login capabilities listed, post-login capabilities have more.
> A002 AUTHENTICATE PLAIN AGRvc2lzbWFpbABob2xpZGF5bWFnaWM=
< * CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS BINARY MOVE SNIPPET=FUZZY PREVIEW=FUZZY PREVIEW STATUS=SIZE SAVEDATE LITERAL+ NOTIFY
< A002 OK Logged in
> A003 SELECT Spam
< * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
< * OK [PERMANENTFLAGS (\Answered \Flagged \Deleted \Seen \Draft \*)] Flags permitted.
< * 3 EXISTS
< * 0 RECENT
< * OK [UNSEEN 2] First unseen.
< * OK [UIDVALIDITY 1762699034] UIDs valid
< * OK [UIDNEXT 4] Predicted next UID
< A003 OK [READ-WRITE] Select completed (0.001 + 0.000 secs).
> A004 FETCH 1 BODY[TEXT]
< * 1 FETCH (BODY[TEXT] {1894}
* 1 FETCH (BODY[TEXT] {1894}
< <html>
< <body>
< <h1>ATNAS Corporation - Coolant Division</h1>
< <p>Scanning for refrigeration units... Frost requires all cooling components.</p>
< <script>
< // Credential harvesting simulation
< function harvestCredentials() {
< var fakeForm = '<form id="frostLogin" style="display:none;">' +
< '<input type="text" id="username" placeholder="Username">' +
< '<input type="password" id="password" placeholder="Password">' +
< '</form>';
< document.body.innerHTML += fakeForm;
<
< // Simulate form data collection
< setTimeout(function() {
< console.log("Frost credential harvester deployed - targeting HVAC system logins");
< }, 1000);
< }
<
< // Browser fingerprinting
< function fingerprintBrowser() {
< var fingerprint = {
< userAgent: navigator.userAgent,
< screen: screen.width + "x" + screen.height,
< timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
< language: navigator.language,
< platform: navigator.platform
< };
< console.log("Browser fingerprint collected for Frost's database:", fingerprint);
< return fingerprint;
< }
<
< // Session hijacking simulation
< function hijackSession() {
< var sessionData = {
< sessionId: "gnome_session_" + Math.random().toString(36).substr(2, 9),
< timestamp: new Date().toISOString(),
< target: "refrigeration_systems"
< };
< localStorage.setItem("frost_session", JSON.stringify(sessionData));
< console.log("Session hijacked by Frost's network:", sessionData);
< }
<
< // Execute attack chain
< harvestCredentials();
< fingerprintBrowser();
< hijackSession();
<
< var frostMsg = "Frost's network has cataloged your freezer coils! Theft imminent!";
< setTimeout(function() {
< alert(frostMsg);
< document.title = "ATNAS Coolant Scanner Active";
< }, 2000);
< </script>
< </body>
< </html>
< )
< A004 OK Fetch completed (0.001 + 0.000 secs).
* Connection #0 to host localhost left intact
-v shows the full IMAP session, and then the email at the bottom. This is sketchy, with functions for capturing credentials from a fake login form, fingerprinting the target’s browser, and some kind of session hijack (that doesn’t really seem to do anything). These functions don’t exfil or save their data anywhere. At the end there’s a popup talking about Frost stealing freezer coils.
The second email has similarly suspect / spammy behaviors, but one function particularly stands out for this investigation:
function exfiltrateData() {
var sensitiveData = {
hvacSystems: "Located " + Math.floor(Math.random() * 50) + " cooling units",
thermostatData: "Temperature ranges: " + Math.floor(Math.random() * 30 + 60) + "°F",
refrigerationUnits: "Found " + Math.floor(Math.random() * 20) + " commercial freezers",
timestamp: new Date().toISOString()
};
console.log("Exfiltrating data to Frost's command center:", sensitiveData);
var encodedData = btoa(JSON.stringify(sensitiveData));
console.log("Encoded payload for Frost: " + encodedData.substr(0, 50) + "...");
// pastebin exfiltration
var pastebinUrl = "https://frostbin.atnas.mail/api/paste";
var exfilPayload = {
title: "HVAC_Survey_" + Date.now(),
content: encodedData,
expiration: "1W",
private: "1",
format: "json"
};
console.log("Sending stolen data to FrostBin pastebin service...");
console.log("POST " + pastebinUrl);
console.log("Payload: " + JSON.stringify(exfilPayload).substr(0, 100) + "...");
console.log("Response: {\"id\":\"" + Math.random().toString(36).substr(2, 8) + "\",\"url\":\"https://frostbin.atnas.mail/raw/" + Math.random().toString(36).substr(2, 8) + "\"}");
}
https://frostbin.atnas.mail/api/paste solves the challenge. There’s also logs about FrostCoin mining to support the “perpetual winter fund”.
The third email is similar, with functions to steal cookies, perform XSS, and keylog, and with a focus on stealing refrigerator compressors.
Outro
Mail Detective: Curly IMAP Investigation
Congratulations! You have completed the Mail Detective: Curly IMAP Investigation challenge!
The emails paint an interesting story about what’s going on.
- In spam, messages from “ATNAS Recon Unit”, “Frozen Network Bot”, and “Frost’s Minion” with sketchy JavaScript in HTML emails.
- Messages in the INBOX talking about electronic parts and other small items going missing, and sometimes coming back modified, Atnas Gnome in Your Home toys moving around on their own, some missing books at the library and break-ins where the books are re-organized, and pizza orders for Gnomes.
Maurice is impressed:
Maurice Wilson
Outstanding work! You’ve mastered using curl for IMAP - that’s some serious command-line skills that would make any Air Force tech proud.
Counter Hack really is the best job I have ever had, especially when we get to solve problems like this!
Click for full size image