Home > Writeups > DEADROP Network 6 - OPERATION NIGHTJAR

DEADROP Network 6 - OPERATION NIGHTJAR

A single PCAP containing a complete attack kill chain, reconnaissance, exploitation, C2 establishment, lateral movement, data staging, and exfiltration. Each stage requires a different analysis technique. Read the whole story from first SYN to final exfil packet.

OPERATION NIGHTJAR

Overview

A full simulated breach captured in a single PCAP. The flag is inside a password-encrypted PDF that you extract from the traffic itself. The password is hiding in the Kerberos packets earlier in the capture. You need to trace the entire attack chain from initial access through to exfiltration.


Step 1: Find the Kerberos Realm (this is your PDF password)

Do this first, it's easy and you'll need it at the end.

In Wireshark, filter:

udp.dstport == 88

Click on the one packet that appears. In the packet detail pane, expand:

Kerberos > as-req > req-body

Embedded here after cname is DEADROP.OPS. Write it down.


Step 2: Initial Access, the Exploit

Filter to find the exploit traffic:

tcp.dstport == 8080

You'll see a GET followed by a POST to /api/update. Click the POST and look at the packet bytes pane, the body contains a base64-encoded blob followed by a long run of A characters (buffer overflow padding). This is the initial access exploit.

Seconds later, a reverse shell connects back. Filter:

tcp.dstport == 4444

Follow that TCP stream (right-click > Follow > TCP Stream) to see the attacker running id, hostname, and uname -a on the compromised host.


Step 3: Lateral Movement, SMB Enumeration

Filter:

tcp.dstport == 445

You'll see connection attempts to four internal hosts: 10.10.10.10, 10.10.10.20, 10.10.10.21, and 10.10.10.30. Two of them respond with SMB negotiate replies. The attacker is mapping the internal network.


Step 4: Data Staging, DNS Exfiltration

Filter:

dns.qry.name contains "nightjar-c2"

This shows a series of queries where the subdomain encodes exfiltrated data as hex (same scheme as Net 2). The first query uses the prefix ffff as a beacon announcing the total chunk count. Subsequent queries carry 16 bytes of data each in the format {seq:04x}{hex_data}.nightjar-c2.net.

To decode what was exfiltrated:

import dpkt

chunks = {}
with open('OPERATION_NIGHTJAR.pcap', 'rb') as f:
    for ts, raw in dpkt.pcap.Reader(f):
        eth = dpkt.ethernet.Ethernet(raw)
        if not isinstance(eth.data, dpkt.ip.IP): continue
        udp = eth.data.data
        if not isinstance(udp, dpkt.udp.UDP) or udp.dport != 53: continue
        try:
            dns = dpkt.dns.DNS(udp.data)
            for q in dns.qd:
                name = q.name
                if 'nightjar-c2' not in name: continue
                label = name.split('.')[0]
                if label.startswith('ffff'): continue
                seq = int(label[:4], 16)
                chunks[seq] = bytes.fromhex(label[4:])
        except Exception:
            pass

print(b''.join(chunks[k] for k in sorted(chunks)).decode())

The staged data reveals VPN credentials: password123 on 10.10.14.1.


Step 5: Extract the Encrypted PDF

Filter to the file transfer:

tcp.dstport == 80

You'll see a GET request to /retrieve?id=DR-2024-INC-001. The server responds with the file using HTTP chunked transfer encoding.

To extract it in Wireshark: right-click the GET packet > Follow > TCP Stream. In the stream dialog, set Show and save data as: Raw. Use the direction dropdown to show only the server side (the 10.10.14.80 -> 10.10.14.52 direction). Click Save as and name it stream_raw.bin.

Then de-chunk and extract the PDF:

def dechunk(data):
    result = b''
    pos = data.find(b'\r\n\r\n') + 4
    while pos < len(data):
        crlf = data.find(b'\r\n', pos)
        if crlf == -1: break
        try:
            size = int(data[pos:crlf], 16)
        except ValueError:
            break
        if size == 0: break
        pos = crlf + 2
        result += data[pos:pos+size]
        pos += size + 2
    return result

with open('stream_raw.bin', 'rb') as f:
    data = f.read()

pdf_bytes = dechunk(data)
idx = pdf_bytes.find(b'%PDF')
with open('breach_review.pdf', 'wb') as f:
    f.write(pdf_bytes[idx:])

Step 6: Decrypt the PDF

Use the Kerberos realm from Step 1 as the password:

# With qpdf (recommended):
qpdf --password='DEADROP.OPS' --decrypt breach_review.pdf decrypted.pdf
# Then open decrypted.pdf in any PDF viewer

# Or with Python:
import pikepdf
with pikepdf.open('breach_review.pdf', password='DEADROP.OPS') as p:
    p.save('decrypted.pdf')

The PDF is the agency's formal internal review of the March 2024 breach. The root cause listed: "Unit 7 (Acting) used 'password123' on the VPN gateway."

The flag is the verification token at the bottom of the document.

Flag: DEADROP{full_chain_analysis_you_beautiful_nerd}


Why the Kerberos Realm?

Kerberos AS-REQ packets include the realm (the domain name of the authentication server) in plaintext, it has to be readable for the KDC to route the request. In this scenario, the analyst who encrypted the breach review used the realm as a passphrase, assuming it would only be known to people with domain access. Anyone with the PCAP and the knowledge to look has it too.

< Back to All Writeups