Skip to content
Back to Writeups
Forensics Easy

Hidden in Plain Sight: PCAP Traffic Analysis

Recovering a flag from captured network traffic by identifying an unusual HTTP multipart upload and extracting the base64-encoded payload buried inside it.

NullCTF 2025 November 8, 2025 by Alpet Gexha

We got a single file: capture.pcap. No other context. Classic forensics opener.

First Look in Wireshark

Open the file, check the protocol breakdown under Statistics → Protocol Hierarchy:

  • ~80% TCP
  • ~18% HTTP
  • ~2% DNS

No TLS, which means traffic is cleartext — good start. Filter for HTTP to cut the noise:

http

Around 340 packets, mostly GET requests for static assets. Nothing interesting until packet 187: a POST /upload with a Content-Type: multipart/form-data body that is noticeably larger than the others — about 4.2 KB versus the typical few hundred bytes.

Extracting The Upload

Right-click packet 187 → Follow → HTTP Stream. The raw stream shows:

POST /upload HTTP/1.1
Host: 10.0.0.42
Content-Type: multipart/form-data; boundary=----FormBoundary7MA4YWxk

------FormBoundary7MA4YWxk
Content-Disposition: form-data; name="file"; filename="notes.txt"
Content-Type: text/plain

dGhpcyBpcyBub3QgdGhlIGZsYWcgeW91IGFyZSBsb29raW5nIGZvcg==

------FormBoundary7MA4YWxk
Content-Disposition: form-data; name="metadata"
Content-Type: application/json

{"checksum":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","payload":"TlVMTHtQY2FwX0hpZGRlbl9JblBsYTFuX1MxZ2h0fQ=="}
------FormBoundary7MA4YWxk--
Two base64 blobs, check both

The file field decodes to a decoy string. The interesting one is in the metadata JSON under the payload key — that is the one that matters.

Decoding The Payload

echo "TlVMTHtQY2FwX0hpZGRlbl9JblBsYTFuX1MxZ2h0fQ==" | base64 -d

Output:

NULL{PcaP_Hidden_InPla1n_S1ght}

Done.

Automating It With tshark

For anyone who wants to do this without clicking through Wireshark:

# Export all HTTP POST bodies
tshark -r capture.pcap \
  -Y "http.request.method == POST" \
  -T fields -e http.file_data \
  2>/dev/null | tr -d '\n'

Then pipe through a quick Python snippet to pull the payload field and decode:

import json, base64, re

raw = open("http_bodies.txt").read()
match = re.search(r'"payload":"([A-Za-z0-9+/=]+)"', raw)
if match:
    print(base64.b64decode(match.group(1)).decode())

What Made This Interesting

The challenge worked because most players would fixate on the file field and get the decoy string. The actual data was hidden one level deeper, in a JSON sub-field inside the same multipart body — something Wireshark does not highlight automatically.

The lesson: when a multipart upload looks bigger than it should, expand every part. Do not stop at the first base64 string you find.

Flag
NULL{PcaP_Hidden_InPla1n_S1ght}