Home > Writeups > DEADROP Network 5 - C2 Beacon

DEADROP Network 5 - C2 Beacon

A PCAP containing C2 beacon traffic with a polyglot payload hiding a second flag. Identify the beaconing pattern, extract and decode the C2 communications, then analyse the payload for the embedded flag.

C2 Beacon

Overview

C2 beaconing traffic over HTTP to port 8080. Commands are encoded with base64+XOR. One server response contains a polyglot file: a valid PNG that is also a valid ZIP archive containing the flag.

Step 1: Identify the Beacon

Filter to the C2 channel:

tcp.dstport == 8080

Plot timestamps of POST requests to measure the interval. The beacon fires every ~30 seconds with up to 5 seconds of jitter, a classic C2 pattern.

Step 2: Decode Commands

Each POST body has the form d=<encoded>&t=<type>. The encoding is:

1. XOR each byte with 0x4A
2. base64 encode

To decode:

import base64

XOR_KEY = 0x4A

def decode(s):
    return bytes(b ^ XOR_KEY for b in base64.b64decode(s)).decode()

# Example: decode the 'd' parameter from each POST body

Decoded commands reveal the agent checking in, receiving tasking, executing whoami and ipconfig, then receiving a file.

Step 3: Extract the Polyglot

The fifth beacon response is much larger than the others (~11KB). To get it out: in Wireshark, right-click any packet in the conversation to 203.0.113.42 and choose Follow > TCP Stream. In the stream dialog, set Show and save data as: Raw, switch the direction dropdown to show only the server side (203.0.113.42 -> 10.10.14.52), then click Save as response.bin.

Strip the HTTP headers and find the PNG:

with open('response.bin', 'rb') as f:
    data = f.read()
body = data[data.find(b'\r\n\r\n')+4:]
png_start = body.find(b'\x89PNG')
payload = body[png_start:]
with open('extracted.png', 'wb') as f:
    f.write(payload)

The file starts with the PNG magic bytes \x89PNG.

It opens as a valid PNG showing an agent status screen. However, it is also a valid ZIP archive , the ZIP end-of-central-directory record is appended after the PNG IEND marker:

# Both of these work on the same file:
eog extracted.png
unzip extracted.png

Unzipping reveals exfil_doc.txt, an intelligence report on the aftermath of the March 2024 breach. The flag is the verification token at the bottom.

import zipfile, io
zf = zipfile.ZipFile(io.BytesIO(open('extracted.png','rb').read()))
print(zf.read('exfil_doc.txt').decode())

Flag: DEADROP{c2_traffic_analysis_and_a_polyglot_bonus}

Key Takeaway

Polyglot files exploit the fact that different file parsers read from different positions: PNG readers stop at IEND, ZIP readers search from the end of the file for the end-of-central-directory record. The same bytes satisfy both.

< Back to All Writeups