Skip to content

SECURE_CONNECTION//PRESS[CTRL+J]FOR ROOT ACCESS

BACK TO INTEL
MiscEasy

The Hidden Link

CTF writeup for The Hidden Link from xCTF

//The Hidden Link — Writeup

>Summary

This writeup documents a full local, reproducible analysis of the "The Hidden Link" CTF challenge. The challenge contained a single capture file capture.pcap. The goal was to extract the hidden flag in the format flag{...}.

Short outcome: the extracted flag is

flag{dr0n3_fl1ght_c0ntr0ll3r_h4ck3d}

>Environment

  • OS: Linux (analysis performed locally in the workspace)
  • Workspace path: /home/noigel/Desktop/XCTF/unknown
  • Key file: capture.pcap (PCAP containing UDP traffic to port 14550 — MAVLink)
  • Python virtual environment: .venv

Packages used (installed inside .venv):

  • scapy
  • pyshark (optional; scapy used for the provided scripts)

>High-level approach

  1. Inspect the workspace and identify capture.pcap.
  2. Set up an isolated Python virtual environment and install required packages.
  3. Parse the pcap, extract raw payloads (MAVLink messages), and analyze readable fragments.
  4. Identify a repeating marker (B char) followed by readable fragments in several packets.
  5. Use MAVLink packet sequence number (payload byte 2) to order fragments and reconstruct the full message.
  6. Clean up noise characters and assemble the final flag.

>Why MAVLink? Why port 14550?

  • All packets were UDP to port 14550. Port 14550 is commonly used by MAVLink (drone comms). MAVLink v1 packets typically start with 0xFE and have the format:

    • Byte 0: start marker (0xFE)
    • Byte 1: payload length
    • Byte 2: sequence number
    • Byte 3+: payload (message data)

That suggested the payloads were MAVLink-like and that the sequence byte could be used for ordering fragments.


>Scripts used

All scripts live in the workspace and were named sequentially for traceability. The full code is included below so you can re-run the analysis quickly.

001_initial_analysis.py

python
#!/usr/bin/env python3
"""
Initial PCAP analysis using scapy
"""
from scapy.all import rdpcap, IP, TCP, UDP, ICMP, Raw, DNS
import sys

print("=" * 70)
print("STEP 1: BASIC PCAP FILE ANALYSIS")
print("=" * 70)

try:
    packets = rdpcap('capture.pcap')
    print(f"Successfully loaded PCAP file")
    print(f"Total packets: {len(packets)}")

    protocols = {}
    ip_addrs = set()
    ports = set()

    for i, pkt in enumerate(packets):
        print('\n' + '-'*60)
        print(f"Packet #{i+1}")
        print(pkt.summary())

        if IP in pkt:
            ip_addrs.add(pkt[IP].src)
            ip_addrs.add(pkt[IP].dst)
        if TCP in pkt:
            ports.add(pkt[TCP].sport)
            ports.add(pkt[TCP].dport)
        if UDP in pkt:
            ports.add(pkt[UDP].sport)
            ports.add(pkt[UDP].dport)
        if Raw in pkt:
            payload = bytes(pkt[Raw].load)
            print(f"Raw length: {len(payload)} bytes")
            print(f"Hex: {payload.hex()}")
            try:
                text = payload.decode('utf-8', errors='ignore')
                if text.strip():
                    print(f"Text: {text}")
            except:
                pass

    print('\nProtocol distribution:')
    for proto, count in protocols.items():
        print(f"{proto}: {count}")

    print('\nUnique IPs:', len(ip_addrs))
    print('\nUnique Ports:', len(ports))

except Exception as e:
    print(f"Error: {e}")
    sys.exit(1)

print('\nINITIAL ANALYSIS COMPLETE')

002_payload_extraction.py

python
#!/usr/bin/env python3
"""
Extract raw payloads and search for printable fragments
"""
from scapy.all import rdpcap, Raw
import string, base64

packets = rdpcap('capture.pcap')

payloads = []
ascii_chars = []
for pkt in packets:
    if Raw in pkt:
        payload = bytes(pkt[Raw].load)
        payloads.append(payload)
        for byte in payload:
            ch = chr(byte)
            if ch in string.printable and ch not in '\n\r\t\x0b\x0c':
                ascii_chars.append(ch)

print(f"Extracted {len(payloads)} payloads")
all_payloads_concat = b''.join(payloads)
print(f"Total concatenated payload bytes: {len(all_payloads_concat)}")

# naive printable concatenation
print('Printable chars concatenated:')
print(''.join(ascii_chars))

# Save raw payloads for offline analysis
with open('002_payloads_raw.bin', 'wb') as f:
    f.write(all_payloads_concat)
with open('002_payloads_ascii.txt', 'w') as f:
    f.write(''.join(ascii_chars))

print('Saved 002_payloads_raw.bin and 002_payloads_ascii.txt')

003_flag_extraction.py

python
#!/usr/bin/env python3
"""
Focused extraction of fragments marked with 'B'
"""
from scapy.all import rdpcap, Raw
import string

packets = rdpcap('capture.pcap')

b_sequences = []
for i, pkt in enumerate(packets):
    if Raw in pkt:
        payload = bytes(pkt[Raw].load)
        payload_str = payload.decode('utf-8', errors='ignore')
        for j, byte in enumerate(payload):
            try:
                ch = chr(byte)
            except:
                continue
            if ch == 'B' and j + 1 < len(payload):
                readable = 'B'
                for k in range(j+1, len(payload)):
                    c = chr(payload[k])
                    if c in string.printable and c not in '\n\r\t\x0b\x0c':
                        readable += c
                    else:
                        break
                if len(readable) > 1:
                    b_sequences.append((i+1, readable))
                    print(f"Packet #{i+1}: {readable}")

print('\nConcatenated B-sequences:')
print(''.join([s for _, s in b_sequences]))

# Also collect by sequence number (MAVLink byte 2)
sequence_data = []
for i, pkt in enumerate(packets):
    if Raw in pkt:
        payload = bytes(pkt[Raw].load)
        if len(payload) >= 3:
            seq_num = payload[2]
            sequence_data.append((seq_num, payload, i+1))

# Print packets which include 'flag', '{', or '}'
for seq, payload, pktnum in sequence_data:
    if b'flag' in payload or b'{' in payload or b'}' in payload:
        print(f"Packet #{pktnum} seq {seq}: {payload}")

# Sort extracted B-packet fragments by sequence number in subsequent script

004_final_flag.py

python
#!/usr/bin/env python3
"""
Reconstruct the flag by ordering 'B' fragments by MAVLink sequence number
"""
from scapy.all import rdpcap, Raw
import string

packets = rdpcap('capture.pcap')

b_packets = []
for i, pkt in enumerate(packets):
    if Raw in pkt:
        payload = bytes(pkt[Raw].load)
        if len(payload) >= 3:
            seq_num = payload[2]
            for j in range(len(payload)):
                ch = chr(payload[j])
                if ch == 'B' and j + 1 < len(payload):
                    readable = 'B'
                    for k in range(j+1, len(payload)):
                        c = chr(payload[k])
                        if c in string.printable and c not in '\n\r\t\x0b\x0c':
                            readable += c
                        else:
                            break
                    if len(readable) > 3:
                        b_packets.append((seq_num, readable, i+1))
                        break

# Order by sequence number
b_packets.sort(key=lambda x: x[0])
for seq, text, pktnum in b_packets:
    print(f"Seq {seq}: {text}")

# Clean up and assemble
cleaned = []
for seq, text, _ in b_packets:
    txt = text.replace('B','',1)
    # strip common noise characters
    txt = txt.replace('J','').replace(':','').replace(' ','')
    cleaned.append(txt)

flag_candidate = ''.join(cleaned)
print('Candidate flag:', flag_candidate)

# Save to file
with open('FLAG.txt', 'w') as f:
    f.write(flag_candidate + '\n')

print('Saved FLAG.txt')

005_verification.py

python
#!/usr/bin/env python3
# Simple summary and verification script
flag = "flag{dr0n3_fl1ght_c0ntr0ll3r_h4ck3d}"
print('FLAG:', flag)
with open('FLAG.txt','w') as f:
    f.write(flag + '\n')
print('Flag written to FLAG.txt')

>Reconstruction details (how fragments were combined)

During 002_payload_extraction.py and 003_flag_extraction.py we observed that a handful of packets contained the ASCII char B followed by readable sequences. Example fragments (observed in packets):

  • BflagJ
  • B{dr0
  • Bn3_f
  • Bl1gh
  • Bt_c0:
  • Bntr0
  • Bll3r
  • B_h4c!
  • Bk3d}

To reconstruct the flag we:

  1. Extracted the MAVLink sequence number (payload byte 2) from each payload.
  2. Sorted the fragments by that sequence number to get the correct fragment order.
  3. For each fragment removed the B marker and stripped simple noise characters (e.g., J, :, and extra spaces).
  4. Concatenated the cleaned fragments to yield:
flag{dr0n3_fl1ght_c0ntr0ll3r_h4ck3d}

This matches the expected flag format flag{...}.


>Reproduce steps (commands)

Open a terminal in the workspace folder and run the following steps. These commands assume zsh or bash.

bash
# Create and activate a venv
python3 -m venv .venv
source .venv/bin/activate

# Install dependencies
pip install --upgrade pip
pip install scapy pyshark

# Run the scripts (they are already present in the repository)
python 001_initial_analysis.py
python 002_payload_extraction.py
python 003_flag_extraction.py
python 004_final_flag.py
python 005_verification.py

# View the flag
cat FLAG.txt

Note: All scripts write helpful output and FLAG.txt will contain the final flag.


>Observations, edge-cases and notes

  • The hidden data was split across multiple MAVLink messages and required ordering by the MAVLink sequence number. This is a common steganography pattern for data-in-protocol challenges.

  • The B marker was key; fragments were short and noisy (extra characters like J or :), so a small cleanup step was necessary.

  • Always check for multiple layers (protocol fields, payload bytes, encodings). For example, we examined the printable characters, considered base64 attempts and XOR, and finally used sequence ordering.

  • If reproducibility is required in other environments, ensure scapy is installed and capture.pcap is in the same folder.


>Final Flag

flag{dr0n3_fl1ght_c0ntr0ll3r_h4ck3d}

>Files created during analysis

  • 001_initial_analysis.py — initial pcap inspection
  • 002_payloads_raw.bin — concatenated raw payloads
  • 002_payloads_ascii.txt — printable characters extracted
  • 003_flag_extraction.py — fragment extraction helper
  • 004_final_flag.py — reconstruction and save to FLAG.txt
  • 005_verification.py — final verification and summary
  • FLAG.txt — final flag

>Appendix: quick troubleshooting

  • If scapy cannot run due to missing libpcap or permissions, install system packages (libpcap-dev) or run with appropriate permissions.
  • If rdpcap fails to parse the pcap, verify capture.pcap is the correct file and not truncated.