Meghnad's Trap
By Team Cryptonite · 3 min read
About the CTF: Pinaka CTF
Meghnad fought unseen, his commands hidden inside a storm of relays. The captured signals say many things, most of them false. Find the one true order in the invisible war. Submit to:…
**Pinaka CTF 2026** · Forensics · Team Cryptonite · All 44 Pinaka write-ups
Challenge
> Meghnad fought unseen, his commands hidden inside a storm of relays. The captured signals say many things, most of them false. Find the one true order in the invisible war.
>
> Submit to: https://pinaka-meghnad-validator.onrender.com
We're given two captures, captureuttara.pcap and capturevyoma.pcap.
"Many things, most of them false" told me early that this wasn't about decrypting anything. It was about working out which packets to trust. Having two captures also suggested they were the same network seen from two different places.
Recon
Both files are small (about 30 packets each), so I opened them in Wireshark and also looked at the IP conversations:
tshark -r capture_uttara.pcap -q -z conv,ip
================================================================================
IPv4 Conversations
Filter:<No Filter>
| <- | | -> | | Total | Relative | Duration |
| Frames Bytes | | Frames Bytes | | Frames Bytes | Start | |
10.7.0.3 <-> 10.7.0.6 1 84 bytes 14 1,313 bytes 15 1,397 bytes 0.000000000 1.4000
10.7.0.1 <-> 10.7.0.255 0 0 bytes 5 540 bytes 5 540 bytes 0.300000000 1.0000
10.7.0.4 <-> 10.7.0.6 0 0 bytes 4 394 bytes 4 394 bytes 0.850000000 0.0600
10.7.0.1 <-> 10.7.0.2 1 84 bytes 1 84 bytes 2 168 bytes 0.020000000 0.0200
================================================================================
for the other one:
tshark -r capture_vyoma.pcap -q -z conv,ip
================================================================================
IPv4 Conversations
Filter:<No Filter>
| <- | | -> | | Total | Relative | Duration |
| Frames Bytes | | Frames Bytes | | Frames Bytes | Start | |
10.7.0.3 <-> 10.7.0.6 1 84 bytes 14 1,313 bytes 15 1,397 bytes 0.000000000 1.4002
10.7.0.7 <-> 10.7.0.6 1 84 bytes 8 759 bytes 9 843 bytes 0.100012000 0.8801
10.7.0.1 <-> 10.7.0.255 0 0 bytes 5 540 bytes 5 540 bytes 0.300036000 1.0001
10.7.0.4 <-> 10.7.0.6 0 0 bytes 4 394 bytes 4 394 bytes 0.850102000 0.0600
================================================================================
Everything is UDP on 10.7.0.0/24, using ports 48000-48006. Wireshark doesn't recognise the protocol, but every payload begins with MG, so it's a custom protocol.

The last byte of each MAC matches the host: 02:00:00:00:00:02 is 10.7.0.3, :03 is 10.7.0.4, and so on. The node id is the last octet of the IP minus one. This turns out to matter later.
Figuring out the MG header
I lined up a few payloads in hex and compared them. The ones carrying readable text made it easy:
tshark -r capture_uttara.pcap -T fields -e frame.number -e ip.src -e udp.payload

Here's the layout I worked out:
0-1 "MG"
2 version
3 type (flags)
4 src node id
5 dst node id
6-7 session id
8 epoch
9 clock flag
10-17 timestamp (u64, microseconds)
18-19 message id
20 fragment index
21 fragment count
22-23 sequence number
24-25 payload length
26.. payload, then an 8-byte tag at the end
The types behave like bit flags: 0x01 SYN, 0x02 ACK, 0x03 SYN-ACK/RESYNC, 0x04 DATA, 0x10 beacon, 0x20 FIN. Orders are sent in plaintext, split into two fragments each.
Once I could read the headers, the junk was easy to spot.
- Beacons.
10.7.0.1keeps broadcasting 32-byte blobs that look random to10.7.0.255. I spent a few minutes thinking they might be encrypted orders, but they're just noise.
- A replay after the epoch change. Partway through, the real relay
10.7.0.3sendsRESYNC2and moves to epoch 2. Right after that,10.7.0.4sends 4 packets (frames 16-19 in uttara). They:
- claim
src = 2in the header, even though they come from MAC:03 - carry epoch 2 and a new timestamp
- reuse the exact same tags as the original epoch-1 fragments (for example
bbf72c035ef14448) - have sequence numbers that go backwards (6, 7, 2, 3)
tshark -r capture_uttara.pcap -Y 'udp.payload contains bb:f7:2c:03:5e:f1:44:48' -T fields -e frame.number -e eth.src -e ip.src -e udp.payload
12 02:00:00:00:00:02 10.7.0.3 4d4701040205a1a10100000000012a0f19c000020002000600164d41534b2054484520534b592043484152494f545320bbf72c035ef14448
16 02:00:00:00:00:03 10.7.0.4 4d4701040205a1a10200000000012a12ea5000020002000600164d41534b2054484520534b592043484152494f545320bbf72c035ef14448
This is the main bug in the challenge. The tag doesn't cover the epoch or the timestamp, so old packets can be relabelled with a new epoch and timestamp and a receiver will still accept them. The flag says the same thing.
3. A fake session that only Vyoma saw. The Vyoma capture contains a whole extra session, b2b2, from 10.7.0.7, with a very different set of orders:
WITHDRAW ALL DIVISIONS BEYOND THE RIVER BY FIRST LIGHT
SURRENDER THE NORTHERN FORD AND SUE FOR TERMS
DISBAND THE SKY CHARIOTS AND GROUND THE HOST
tshark -r capture_vyoma.pcap -Y 'ip.src == 10.7.0.7'

The Uttara capture never sees this session, and its timestamps come from a completely different clock (around 8247s, against around 5000s for everything else). The real orders show up in both captures. This one doesn't, so it's fake.
Solving
I wrote indrajit.py to parse both pcaps and keep only DATA fragments that pass all of these checks:
- the type has the DATA bit set
- the MAC node matches the
srcfield in the header (removes 10.7.0.4) - the tag hasn't been seen before (removes the replays)
- the session appears in both captures (removes
b2b2)
After that, the script sorts by sequence number, groups the fragments by message id and joins them back together.
python3 indrajit.py capture_uttara.pcap capture_vyoma.pcap
{"command_transcript": "MOBILIZE THE NAGA DIVISION AT THE NORTHERN FORD\nHOLD THE SOUTHERN GATE UNTIL THE THIRD WATCH\nMASK THE SKY CHARIOTS BEHIND THE STORM WALL\nFEIGN RETREAT AT DUSK AND DRAW THEM TO THE MARSH\nSTRIKE THE CROWN CAMP WHEN THE MOON CLEARS THE RIDGE", "relay_identity": "2"}
MOBILIZE THE NAGA DIVISION AT THE NORTHERN FORD
HOLD THE SOUTHERN GATE UNTIL THE THIRD WATCH
MASK THE SKY CHARIOTS BEHIND THE STORM WALL
FEIGN RETREAT AT DUSK AND DRAW THEM TO THE MARSH
STRIKE THE CROWN CAMP WHEN THE MOON CLEARS THE RIDGE
Every surviving fragment came from node 2 (10.7.0.3), so that's the genuine relay.
Validator
The validator wanted a JSON POST to /verify with two fields: the command transcript (one order per line) and the relay identity.
{"command_transcript": "MOBILIZE THE NAGA ...\n...\nSTRIKE THE CROWN CAMP WHEN THE MOON CLEARS THE RIDGE", "relay_identity": "2"}
PINAKA{epoch_unb0und_the_signal_wears_a_false_timestamp}
Summary
- Both pcaps carry a custom UDP protocol,
MG. I reversed the header by comparing payloads. - The fake traffic is beacon noise, a spoofer replaying old fragments under a new epoch and timestamp with the same tags, and a fake session that only one capture saw.
- Filter on MAC = claimed src, unique tags, and sessions seen in both captures, then rebuild the fragments.
- Submit the 5 real orders with relay identity
2to get the flag.