DJI Mavic 3 Drone Research Part 1: Firmware Analysis

DJI Mavic 3 Drone Research Part 1: Firmware Analysis

Nozomi Networks Labs recently conducted research on the security of a DJI Mavic 3 Series drone, with a special focus on the WiFi-based protocol called QuickTransfer Mode. This protocol enables a user to quickly download videos and pictures from the drone directly to their mobile phone when the drone is not flying.

After successfully downloading and analyzing the firmware, we discovered several vulnerabilities that have yet to be patched. As the patching process is still ongoing, we are unable to provide specifics on these vulnerabilities. However, we plan to share further details in a future blog once the patches have been released.

In the first part of this blog series, we describe the firmware upgrade procedure implemented by DJI via their cloud-based infrastructure, analyze the official DJI mobile app, and detail how we were able to bypass its protections to download and analyze the official firmware.

DJI Communication Components

Typically, when conducting a security evaluation on a device, the initial phase involves extensive analysis of the firmware. This allows the researcher to obtain and inspect binaries and configuration files that make up the operating system and primary services running on the device.

Unfortunately, firmware images for DJI devices are not available for download, although some third-party web platforms distribute them for the most used DJI drones and RC (remote controller). While that may have been an option, as security researchers we don’t like to rely on third-party platform to obtain firmware images for several reasons:

  • It’s unclear if the third-party platforms are trustworthy and if they distribute authentic data;
  • It is uncertain how long the images will remain accessible online;
  • We require immediate access to new firmware releases to be able to analyze newly introduced features.

For these reasons, we opted to analyze the DJI firmware upgrade procedure on a physical drone to obtain the firmware directly from the DJI cloud infrastructure.

In order to intercept a firmware package sent to the drone, we must understand the primary communication components involved in the upgrade procedure:

  1. DJI Cloud infrastructure: the cloud-based web infrastructure that stores firmware packages for each drone model;
  2. Remote controller: the device that acts as a remote controller for the drone. This is comprised of two different components:
  3. Radio controller: a DJI-specific hardware that communicates with the drone through a DJI proprietary wireless protocol called OcuSync, and
  4. Mobile device: a smartphone (IOS or Android) physically linked to the radio controller that acts as graphical interface for the user;
  5. Drone device: the target device that receives and installs the firmware upgrade package.

DJI Firmware Upgrade Procedure

To connect to the radio controller, the mobile device must have the official DJI Fly application, which can be downloaded and installed from the official DJI website. This app enables users to get new firmware releases and push them to the drone through the radio controller. Whenever a new firmware release is available, DJI Fly sends a notification to the user who can choose whether to install it or not. The firmware is signed by DJI and will only be accepted by the drone if the signature validation is successful. Figure 1 displays an image from official DJI documentation that provides an overview of the entire process.

Secure firmware upgrade procedure
Figure 1. Secure firmware upgrade procedure from official DJI documentation.

After conducting a quick analysis of the mobile application, we were able to gather some additional details which are summarized in Figure 2. Although we attempted to passively sniff the traffic between the mobile application and cloud infrastructure, we quickly realized that this was not sufficient due to the encryption provided by HTTPS.

DJI secure upgrade procedure
Figure 2. DJI secure upgrade procedure enriched with additional details.

Our initial attempt at a man-in-the-middle (MitM) attack also failed due to certificate pinning implemented in the mobile application, which only allows connections with services that provide a trusted TLS certificate. The certificate pinning can be implemented in several ways, with standard libraries or implemented with custom code directly inside the application. To avoid further blind attempts and overcome numerous obstacles, we decided to conduct a more thorough analysis on the DJI Fly mobile application and develop a targeted attack scenario. The good news is that inspecting the application can provide valuable insights to enhance our analysis.

DJI Fly Application Analysis

We analyzed the latest DJI Fly app version available for Android at the time of the analysis:

  1. Version 1.9.0-3055175 (December 2022)
  2. SHA256 7fbc75516445cf6c26decc08d286f76a46ab8079
After the first static analysis overview, which involved examining the application manifest and decompiled .dex files within the .apk file, we discovered that the application doesn’t include any DJI implementation. Instead, it contains an application wrapper implemented as com.secneo which serves as a main point of entry for loading a native library known as libDexHelper.so and executing a native method on it, as illustrated in Figure 3.
Decompiled application content.
Figure 3. Decompiled application content.
In addition to the libDexHelper.so file, many other native libraries are included in the application. However, due to the code being heavily obfuscated, tools like Ghidra or IDA Pro cannot analyze it right away. Further analysis revealed that the packer also implements several anti-debugging techniques and specific Frida detection capabilities, which further complicated the application analysis. Reversing and analyzing the packer to understand how to bypass these protections in order to debug and instrument it can be extremely time consuming due to the strong code obfuscation utilized. To simplify the code so it can be further analyzed, we dump the decrypted .dex files by reading the raw memory layout of the application from /proc/self/maps through a code injection, exploiting DT_NEEDED entries with LIEF from QuarksLab, and inspecting it to extract the unpacked data.
As a result, several .dex were extracted and, after some scripting using dex2jar, a single JAR file was created with the application code. The JAR file can now be analyzed with tools such as Jd-Gui, as shown in Figure 4.
Unpacked DJI Fly Android application
Figure 4. Unpacked DJI Fly Android application.

Defeating the Certificate Pinning

By inspecting the code, it is possible to find the certificate pinning implementation, which seems to be executed using standard libraries provided by the Android ecosystem. The TrustManagerFactory Java class provided by the javax.net.ssl package, as seen in Figure 5, is used for this purpose.
Certificate pinning implementation
Figure 5. Certificate pinning implementation
At this stage, bypassing the certificate pinning could be relatively simple task. This can be achieved by modifying the behavior of the TrustManagerFactoryclass implementation in a way that ignores the certificate check. To circumvent the packer’s anti-debugging and Frida detection capability, we hooked the target TrustManagerFactory at the zygote level before the application could start. Since every application on the Android OS produces a clone of the main process to start, this keeps the injection transparent, as it happens before the application bootstrap. We accomplished this using two tools on a rooted Pixel 6a mobile device:
  • LSPosed Framework, which allows injection at zygote level.
  • TrustMeAlready plugin, which implements the TrustManager Android class instrumentation at the zygote level during the application start-up.

Firmware Download and Analysis

Our ability to inspect the data exchanged between the DJI mobile application and the DJI cloud infrastructure allowed us to analyze the types of requests and responses exchanged during a firmware upgrade on the drone. While upgrading a Mavic 3 Classic, we intercepted the communication and analyzed the API’s endpoint used to download a new firmware package. Our investigation revealed that the drone has several components, each one with its unique firmware image identified by a module ID and downloaded separately. This process is done through an authenticated HTTP REST request (one for each firmware module) to the endpoint /getfile/downpath on the mydjiflight.dji.com host as shown in Figure 6.
Figure 6 Firmware download HTTP request
Figure 6. Firmware download HTTP request.
In addition to the information required to download the firmware image, such as the module ID, Product ID, and version, the POST request contains a signature to authenticate the request itself; We didn’t explore the exact way this signature is generated, however it’s presumed to be a Hash-based message authentication code (HMAC) as part of the request. The response contains a link with an associated authentication key, auth_key as seen in Figure 6, required for downloading the firmware image. Simply executing the wget tool with the provided link and the authentication key as parameters is enough to download the firmware, shown in Figure 7.
Firmware downloads with wget
Figure 7. Firmware downloads with wget.
The downloaded firmware image follows the format productID_moduleID_moduleVersion_buildDate.pro.fw.sig. In the case of the Mavic 3 Classic, the most interesting upgrade package in the available list is the module with associated ID 0802 due to its large size, which suggests that it probably contains the drone’s main operating system. To analyze the structure of the firmware image, an excellent source of information is the community-maintained Github repository dji-firmware-tools which contains documentation and some scripts. By examining the code of the dji_imah_fwsig.py, it is possible to understand how the firmware image is composed:
  • An image header containing the size (number of chunks) of the encrypted chunk-based payload together with checksums of the encrypted and decrypted data.
  • A list of chunk headers that specify the offset and size of data chunks inside the image.
  • An RSA-based digital signature of both image and chunk headers.
  • The list of AES encrypted chunks that contains the necessary data required for upgrading the firmware
DJI firmware image layout
Figure 8. The DJI firmware image layout.

The firmware image’s authenticity is enforced by the digitally signed image headers. An attacker aiming to alter any kind of data should update the checksum and digest values inside the image headers (Figure 8), requiring a new valid RSA signature to be generated (obviously unfeasible without the required private key). This ensures that the drone will only install a firmware if it is provided by DJI. From the official DJI documentation it is possible to understand that key management is enforced on the drone trough ARM TrustZone CryptoCell that stores keys to perform encryption and signature verification operations.

The script provided in the dji-firmware-tools (dji_imah_fwsig.py) contains a list of leaked keys that can be used to decrypt the firmware however, none of these keys were effective for the downloaded image we were working with. Fortunately, we found an independent security researcher on Twitter named Felix Domke who had previously published some keys related to JDI Mavic 3 in the dji-firmware-tools repository. In one of his tweets from April 2022, he shared two keys related to the JDI Mavic 3 identified as Update Firmware Image Encryption Key (UFIE) and Trusted Boot Image Encryption key (TBIE ). These specific keys were not integrated into list of keys found in the dji-firmware-tools script to decrypt and unpack the firmware, so we manually added them in the code as UFIE-2022-04 and TBIE-2022-04, respectively. With this addition, we were finally able to successfully decrypt the firmware using the manually added key UFIE-2022-04, as shown in Figure 9.
The Mavic 3 Classic firmware decryption.
Figure 9. The Mavic 3 Classic firmware decryption.

Once unpacked, the chunk was found to be a JAR archive, shown in Figure 10. Upon inspection, it was found to have the same structure as an Android OTA package as described in the official documentation. This leads us to believe that the Mavic 3 drone series runs an Android-based operating system.

Android image of the Mavic 3 classic
Figure 10. The Android image of the Mavic 3 classic.

After unzipping the content of the archive, we focused our attention on the files that make up the main Android operating system – namely, the kernel, binaries, and configuration files. This is because our main objective is to reverse engineer and potentially identify any vulnerabilities on services running on the drone.

We have been able to unpack vendor.new.dat.br and system.new.dat.br files that are respectively the system (/system folder) and vendor (/vendor folder) partitions using the Google provided Brotli official utility and the tool sdat2img to create mountable ext4 images, seen in Figure 11. The system and vendor partitions contain all the binaries, libraries, and configuration files required to run the drone’s services.
Vendor and system partition listing.
Figure 11. Vendor and system partition listing.
In addition to the system and vendor partition, the file normal.img is another intriguing target, as itcontains the main Linux image (kernel and root file system). This file is encrypted and can only be decrypted using the manually-added TBIE-2022-04 key in the dji_imah_fwsig.py script. Decrypting it with this key produced 18 chunks of decrypted data.

The decrypted chunks primarily consist of the Linux kernel image along with its required files to run, and an additional archive that contains the root file system partition that can be unpacked using cpio. Copying the previously extracted vendor and system folders inside the root partition completes the file system Android layout, in Figure 12, with everything necessary to analyze and reverse engineer the services running on the drone.

Complete file system layout.
Figure 12. Complete file system layout.

Now that we have the complete file system of the drone, we can inspect the operating system configuration to understand the internals and map the attack surface.

Thorough analysis of the init scripts (including init.rc and all imported configuration scripts), it is possible to figure out which the main services run on the drone and which binaries implement them. This allows us to potentially statically reverse engineer them and, if possible, dynamically analyze them through emulation.

Thorough analysis of the init scripts (including init.rc and all imported configuration scripts), it is possible to figure out which the main services run on the drone and which binaries implement them. This allows us to potentially statically reverse engineer them and, if possible, dynamically analyze them through emulation.

DJI dominates the drone market with a share of over 90%, catering to both professional and amateur users. Given their significant market presence, any security vulnerabilities in DJI’s devices could have a major impact and should not be underestimated.

In Part 2 of this blog series, we’ll look at several vulnerabilities uncovered by further analysis, related to the QuickTransfer Mode implemented on the Mavic 3 Series.