Tags: networking forensics protocol 

Rating:

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`:

```python
import os
import shutil
import struct

from scapy.all import *
from scapy.layers.inet import IP, UDP

try:
shutil.rmtree('files')
except:
pass

# make a directory to dump files into
os.mkdir('files')

cap = rdpcap('log.pcapng')
print(cap)

# store current file infomation
cur_file = 0
# 0x10FFFF is bigger than any transmission size
cur_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)