We are given a `.pcapng` [capture file](http://pomo-mondreganto.me/drive/writeups/aeroctf-2020/hidden_transmission/log.pcapng). Opening it in Wireshark shows a lot of useless/encrypted traffic, but sorting by protocol one can find a few interesting UDP packets: 
![udp_packets](http://pomo-mondreganto.me/drive/writeups/aeroctf-2020/hidden_transmission/udp_packets.png)
Filtering even futher to drop everything except the conversation between `192.168.1.101` and `192.168.1.107`, we can start looking into the transmission. 
Glancing through the packet data yields some JPEG signatures, which is expected from the task description (there were some pictures sent), so we confirm moving in the right direction. 
The first two packets of transmission look like connection initiation (16 bytes `21 37 91 de 17 89 ab c0 08 c9 ff d2 fe da 01 02` and 4 bytes of answer `c0c0c0c0`), and we can confirm it as there're 4 packets of each type. 
![conn_init](http://pomo-mondreganto.me/drive/writeups/aeroctf-2020/hidden_transmission/conn_init.png)
![conn_pong](http://pomo-mondreganto.me/drive/writeups/aeroctf-2020/hidden_transmission/conn_pong.png)
And then the real data follows. There also are connection termination packets, containing bytes `deaddeaddeaddead` (which is quite guessable). 
Now we can split the data into transmissions based on the protocol described above and start looking into the data. 
Here're two example packets: 
![data_packet1](http://pomo-mondreganto.me/drive/writeups/aeroctf-2020/hidden_transmission/data_packet1.png)
![data_packet2](http://pomo-mondreganto.me/drive/writeups/aeroctf-2020/hidden_transmission/data_packet2.png)
Other packets follow the same format. One can see that the first 4 bytes look like random data, so it's most likely the checksum, let's skip it. The next 4-byte block always start with `0000` and the value is small if the packet is small, like in the following example:
![data_packet_small](http://pomo-mondreganto.me/drive/writeups/aeroctf-2020/hidden_transmission/data_packet_small.png)
The length of the data is 42 bytes, and in this 4-byte block contains `0000001a`, which is exactly `42 - 16`. Checking this guess shows the same rule for all other packets, so we've found the data length. The next two 4-byte blocks contain values of approximately the same magnitude as the length, and in the last (small) packet we can see their values quite close to each other. More precisely, they differ by `0x1a` bytes, which is the packet length, so we conclude that these bytes are the range in the original file being transmitted in the packet. This guess could also be made looking and the consecutive packets of one transmission and noticing they're growing, the second value of one packet being close to the first value of the following one. 
The full protocol is reversed, all that's left if to construct the data from the packets. To do so, I wrote a python script using `scapy`: 
```pythonimport osimport shutilimport struct
from scapy.all import *from scapy.layers.inet import IP, UDP
try:	shutil.rmtree('files')except:	pass
# make a directory to dump files intoos.mkdir('files')
cap = rdpcap('log.pcapng')print(cap)
# store current file infomationcur_file = 0# 0x10FFFF is bigger than any transmission sizecur_data = [b'_'] * 0x10FFFF
for pkt in cap:	# filter only transmission packets	if not (UDP in pkt and IP in pkt and pkt[IP].src == '192.168.1.101' and pkt[IP].dst == '192.168.1.107'):		continue	pl = bytes(pkt[UDP].payload)	# the end of transmission, need to dump the current file	if pl == b'\xde\xad\xde\xad\xde\xad\xde\xad\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00':		with open(f'files/{cur_file}.data', 'wb') as f:			f.write(b''.join(cur_data).rstrip(b'_'))		cur_file += 1		cur_data = [b'_'] * 0x10FFFF		continue 	# the start of transmission, skipping	if pl == b'!7\x91\xde\x17\x89\xab\xc0\x08\xc9\xff\xd2\xfe\xda\x01\x02\x00\x00':		continue
	print(pl, len(pl))	# data length	ln = pl[6:8]	# range start 	start = pl[8:12]	# range end	end = pl[12:16]
	ln = int(ln.hex(), 16)	start = int(start.hex(), 16)	end = int(end.hex(), 16)	print(start, end) 	# copy the data to current file	for i in range(ln):		cur_data[start + i] = bytes([pl[16 + i]])```
This yields 5 files: 
![result_files](http://pomo-mondreganto.me/drive/writeups/aeroctf-2020/hidden_transmission/result_files.png)
And the last one contains the flag:
![flag](http://pomo-mondreganto.me/drive/writeups/aeroctf-2020/hidden_transmission/flag.jpg)