Post

SOC Investigation: HTTP Cookie Data Exfiltration

SOC Investigation: HTTP Cookie Data Exfiltration

Scenario

Platform: TryHackMe
Role: SOC Analyst / Network Forensics Investigator
Objective: Analyze a provided packet capture for a covert communication channel, identify where exfiltrated data was hidden, reconstruct the transmitted values, and decode the recovered plaintext.

The lab supplied an original network capture (traffic.pcapng) and a Python file named updates.py. I treated the Python file as static evidence and did not execute it because the required malicious traffic was already present in the capture.


Executive Summary

Analysis of traffic.pcapng identified a keylogger using repeated HTTP GET requests to exfiltrate captured keystrokes. The data was concealed in the hotel_sess_state HTTP cookie and sent from 192.168.1.141 to the lab server 34.41.103.191 over TCP port 8080.

Each keystroke was XOR-obfuscated, Base64-encoded, and transmitted as a separate request. I filtered and exported the 30 relevant requests into data.pcapng, extracted the cookie values in packet order, reversed the encoding process, and reconstructed a valid TryHackMe flag. The exact flag is intentionally redacted from this public write-up.


1. Evidence Preservation and Scope

I worked from a copy of the original capture and created a smaller evidence set containing only the relevant exfiltration requests.

Original Capture

  • File: traffic.pcapng
  • Size: 556,132 bytes
  • Packets: 1,348
  • SHA-256: b240e95ea42da26011f0ee10b295ad3942ac394e4bba0c486cc5d577c890dd23

Filtered Capture

  • File: data.pcapng
  • Size: 10,044 bytes
  • Packets: 30
  • SHA-256: f59d86466bff518c05f284e42712d176b0916899c6fdaf46a51acf77b66d151a

The exported capture preserved packet order while reducing noise and making reconstruction easier. Wireshark renumbered the exported packets from 1 through 30.


2. Initial Traffic Triage

I began by reviewing HTTP traffic over the non-standard web port used in the capture:

tcp.port == 8080

The original capture showed the internal host requesting a Python file from the lab server:

  • Frame 16: GET /temp/updates.py HTTP/1.1
  • Source: 192.168.1.141
  • Destination: 34.41.103.191:8080
  • Host header: byte-lotus-hotel.thm:8080

The server response contained a Python program that used pynput to capture keyboard input. Static analysis of the file showed that every supported keystroke was:

  1. Converted to UTF-8 bytes
  2. XORed with a hardcoded key
  3. Base64-encoded
  4. Inserted into an HTTP cookie
  5. Sent to the same server with an HTTP GET request

This provided the decoding logic without requiring execution of the keylogger.


3. Covert Channel Identification

The exfiltration requests had two strong identifiers:

1
2
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ByteLotusClient/1.1
Cookie: hotel_sess_state=<encoded value>

The following Wireshark filters isolated the activity:

http.cookie contains "hotel_sess_state"
http.user_agent contains "ByteLotusClient"
tcp.port == 8080 && http.request

The suspicious requests began at frame 391 and continued through frame 1300 in the original capture. I exported the 30 matching requests into data.pcapng using File > Export Specified Packets > Displayed.

The communication was covert at the application layer because the stolen data was carried inside a normal-looking HTTP cookie rather than sent in an obvious request body or file transfer.


4. Data Reconstruction

The filtered capture contained one hotel_sess_state value per packet. The values had to remain in capture order because each request represented the next typed character.

I used TShark to extract the frame number and cookie field:

1
2
3
4
5
6
tshark -r data.pcapng \
  -d tcp.port==8080,http \
  -Y 'http.request && http.cookie contains "hotel_sess_state="' \
  -T fields \
  -e frame.number \
  -e http.cookie

This produced ordered records in the following form:

1
2
3
4
1    hotel_sess_state=HA==
2    hotel_sess_state=AA==
3    hotel_sess_state=BQ==
...

The 30 extracted cookie values matched the 30 packets in the filtered capture, confirming that no relevant request had been omitted.


5. Decoding Logic

The supplied Python source reconstructed the XOR key by joining two strings:

1
H0t3lSt@ff0NlyK3epS3cr3t!

The correct reversal order was:

1
2
3
4
Cookie value
→ Base64 decode
→ XOR with the recovered key
→ Append plaintext in packet order

A subtle implementation detail was important: the malware called its XOR function separately for each captured character. The key index therefore restarted at zero for every HTTP request. Because each request contained a single encrypted byte, each byte was XORed with the first key byte. The decoder still applied the full repeating-key function so it would also work if a request contained multiple bytes.


6. AI-Assisted Decoder Development

ChatGPT was used to generate the Python/TShark decoder script shown below. I ran the script locally against the filtered capture and validated the recovered output.

The resulting decoder was:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#!/usr/bin/env python3

import base64
import re
import subprocess
import sys

KEY = b"H0t3lSt@ff0NlyK3epS3cr3t!"
PCAP = sys.argv[1] if len(sys.argv) > 1 else "data.pcapng"

command = [
    "tshark",
    "-r", PCAP,
    "-d", "tcp.port==8080,http",
    "-Y", 'http.request && http.cookie contains "hotel_sess_state="',
    "-T", "fields",
    "-e", "frame.number",
    "-e", "http.cookie",
]

result = subprocess.run(
    command,
    capture_output=True,
    text=True,
    check=True,
)

pattern = re.compile(r"hotel_sess_state=([^;\s]+)")
recovered = bytearray()

for line in result.stdout.splitlines():
    match = pattern.search(line)
    if not match:
        continue

    encrypted = base64.b64decode(match.group(1), validate=True)
    decoded = bytes(
        byte ^ KEY[index % len(KEY)]
        for index, byte in enumerate(encrypted)
    )
    recovered.extend(decoded)

print(recovered.decode("utf-8", errors="replace"))

Execution:

1
python3 decode_pcap.py data.pcapng

The output reconstructed the full plaintext in chronological order. The recovered flag was submitted successfully but is omitted here to avoid publishing the lab answer.


7. Indicators and Observable Artifacts

Network

  • Internal host: 192.168.1.141
  • Lab server: 34.41.103.191:8080
  • Host: byte-lotus-hotel.thm:8080
  • Delivery path: /temp/updates.py
  • Protocol: HTTP

HTTP

  • Cookie name: hotel_sess_state
  • User-Agent marker: ByteLotusClient/1.1
  • Method: GET / HTTP/1.1
  • Encoding: XOR followed by Base64

These are lab-specific observables and should not be treated as confirmed real-world malicious infrastructure outside the exercise.


8. MITRE ATT&CK Mapping

  • T1056.001 – Input Capture: Keylogging: The supplied Python program captured keyboard input using a keyboard listener.
  • T1105 – Ingress Tool Transfer: The endpoint downloaded updates.py from the remote server.
  • T1071.001 – Application Layer Protocol: Web Protocols: The malware used HTTP GET requests for communication.
  • T1041 – Exfiltration Over C2 Channel: Captured input was transmitted to the same remote infrastructure used by the malicious client.
  • T1132.001 – Data Encoding: Standard Encoding: Base64 encoded the XOR-obfuscated bytes before transmission.
  • T1132.002 – Data Encoding: Non-Standard Encoding: XOR transformed the captured data before Base64 encoding.

9. Detection Opportunities

This activity could be detected through a combination of signature and behavioral analytics:

  1. Alert on the distinctive ByteLotusClient/1.1 User-Agent.
  2. Monitor for repeated HTTP requests containing hotel_sess_state.
  3. Detect high-frequency GET requests where cookie values change by only a few bytes.
  4. Flag Python processes using keyboard-capture libraries while making outbound connections.
  5. Correlate a script download with immediate repetitive communication to the same host.
  6. Review outbound HTTP traffic over non-standard ports such as 8080.

Example Suricata-style detection logic:

1
2
3
4
5
6
7
8
9
10
alert http $HOME_NET any -> $EXTERNAL_NET 8080 (
    msg:"Possible Byte Lotus keylogger exfiltration";
    flow:established,to_server;
    http.user_agent;
    content:"ByteLotusClient/1.1";
    http.cookie;
    content:"hotel_sess_state=";
    sid:1000001;
    rev:1;
)

10. Assessment

The capture contained a confirmed keylogging and data-exfiltration sequence. The actor used a simple but effective application-layer hiding technique by placing one encoded keystroke in each HTTP cookie. Static source review exposed the transformation process, while packet filtering and automated decoding reconstructed the original data.

The investigation demonstrated several practical SOC and DFIR skills:

  • Network traffic triage in Wireshark
  • Suspicious HTTP header analysis
  • Evidence reduction through filtered packet export
  • Static analysis of a Python keylogger
  • Ordered data reconstruction
  • Base64 and XOR decoding
  • TShark and Python automation
  • Validation of AI-generated analysis tooling

The strongest lesson was that automation should follow understanding. Identifying the carrier field, preserving packet order, and verifying how the XOR key reset were necessary before a decoder could produce a trustworthy result.

This post is licensed under CC BY 4.0 by the author.