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.