Original text by Vincent Lee

Back In February, Ubiquiti released a new firmware update for the Ubiquiti EdgeRouter, fixing CVE-2021-22909/ZDI-21-601. The vulnerability lies in the firmware update procedure and allows a man-in-the-middle (MiTM) attacker to execute code as root on the device by serving a malicious firmware image when the system performs an automatic firmware update. The vulnerability was discovered and reported to the ZDI program by the researcher known as awxylitol.

This vulnerability may sound contrived; a bad actor gives bad firmware to the device and bad things happen. However, insecure download vulnerabilities have been the backbone of multiple Pwn2Own winning entries in the router category since its inception. The impact of this vulnerability is quite nuanced and worthy of further discussion.

How exactly does the router perform a firmware update?

According to Ubiquiti documentation, the new templated operational command add system image can be used to update the firmware of the router through the command line interface (CLI). A templated operational command allows the user to quickly modify the operational state of the router without fiddling with complex command-line parameters. This simplifies the process for day-to-day operations and minimizes user errors. I am sure we have all heard of horror stories of system administrators who accidentally deleted critical files, locked themselves out of equipment that is far away from civilization, and so forth. Templated commands attempt to mitigate these issues.

The templating system used by the Ubiquiti EdgeRouter is provided by the vyatta-op package. The command add system image is defined in the /opt/vyatta/share/vyatta-op/templates/add/system/image/node.def file.

$ cat node.def
help: Add a new image to the system
run: sudo /usr/sbin/ubnt-fw-latest —upgrade

view rawCVE-2021-22909-snippet-1.console hosted with ❤ by GitHub

By running this operational command, the user is effectively invoking the ubnt-fw-latest script with the --upgrade option. This option causes the ubnt-fw-latest script to run the upgrade_firmware() function, which will check with a Ubiquiti update server to get information about the latest firmware release, including the firmware download URLs.

while [[ $# -gt 0 ]]
case $key in
-r|—refresh) # Refresh status of latest firmware by
ACTION=»refresh» # fetching it from fw-update.ubnt.com
-s|—status) # Read latest firmware status from cache
-u|—upgrade) # Upgrade to latest firmware
-c|—channel) # Target channel (release or public-beta)
*) # Ignore unknown arguments
# …
upgrade_firmware() {
# Fetch version number of latest firmware
echo -n «Fetching version number of latest firmware… «
refresh_status_file @> /dev/null
# Parse status file
local fw_version=`cat $STATUS_FILE | jq -r .version 2> /dev/null` || fw_version=»»
local fw_url=`cat $STATUS_FILE | jq -r .url 2> /dev/null` || fw_version=»»
local fw_md5=`cat $STATUS_FILE | jq -r .md5 2> /dev/null` || fw_version=»»
local fw_state=`cat $STATUS_FILE | jq -r .state 2> /dev/null` || fw_version=»»
if [ -z «$fw_version» ] || [ «$fw_url» = «$DEFAULT_URL» ]; then
echo «failed»
exit 42
echo «ok»
echo » > version : $fw_version»
echo » > url : $fw_url»
echo » > md5 : $fw_md5″
echo » > state : $fw_state»
if [ «$fw_state» == «can-upgrade» ]; then
echo «New firmware $fw_version is available»
sudo /usr/bin/ubnt-upgrade —upgrade-force-prompt «$fw_url»
elif [ «$fw_state» == «up-to-date» ]; then
echo «Current firmware is already up-to-date (!!!)»
sudo /usr/bin/ubnt-upgrade —upgrade-force-prompt «$fw_url»
elif [ «$fw_state» == «reboot-needed» ]; then
echo «Reboot is needed before upgrading to version $fw_version»
echo «Upgrade is already in progress»
if [ «$ACTION» == «refresh» ]; then
elif [ «$ACTION» == «status» ]; then
elif [ «$ACTION» == «upgrade» ]; then

view rawCVE-2021-22909-snippet-2.bash hosted with ❤ by GitHub

The function proceeds to parse and compare the results from the server with the current firmware version. If an update is available, the script will invoke ubnt-upgrade to fetch the firmware from the fw-download.ubnt.com domain provided by the upgrade server. It will then perform the actual firmware upgrade.

The Bug — ZDI-21-601

The issue lies in the way the /usr/bin/ubnt-upgrade bash script downloads the firmware. The get_tar_by_url() function uses the curl command to perform the fetch. However, the developers specified the -k option (also known as the –insecure option), which disables certificate verification for TLS connections.

get_tar_by_url ()
mkdir $TMP_DIR
if [ «$NOPROMPT» -eq 0 ]; then
echo «Trying to get upgrade file from $TAR»
if [ -n «$USERNAME» ]; then
if [ «$NOPROMPT» -eq 0 ]; then
curl -k $auth -f -L -o $filename $TAR # <——
curl -k $auth -f -s -L -o $filename $TAR # <——
if [ $? -ne 0 ]; then
echo «Unable to get upgrade file from $TAR»
rm -f $filename
exit 1
if [ ! -e $filename ]; then
echo «Download of $TAR failed»
exit 1
if [ «$NOPROMPT» -eq 0 ]; then
echo «Download succeeded»

view rawCVE-2021-22909-snippet-3.bash hosted with ❤ by GitHub

Since /usr/sbin/ubnt-upgrade does not check for the validity of the certificate, an attacker can use a self-signed certificate to spoof the fw-download.ubnt.com domain without triggering any warnings or complaints on the device to alert the user. This vulnerability significantly reduces the skill barrier needed to launch a successful attack.

To exploit this vulnerability, the attacker can modify an existing EdgeRouter firmware image and fix up the checksum contained in the file. In the submitted proof-of-concept, the researcher modified the rc.local file to connect back to the attacker with a reverse shell. A reboot is part of the upgrade process, triggering the rc.local script.


If an attacker inserts themselves as MiTM, they can then impersonate the `fw-download.ubnt.com` domain controlled by Ubiquiti. However, to successfully serve up malicious firmware from this domain, the attackers would normally need to obtain a valid certificate with private key for the domain. To proceed, the attackers would probably need to hack into Ubiquiti or convince a trusted certificate authority (CA) to issue the attackers a certificate for the Ubiquiti domain, which is no insignificant feat. However, due to this bug, there is no need to obtain the certificate.

The heart of the problem is the lack of authentication on the firmware binary. The function of a secure communications channel is to provide confidentiality, integrity, and authentication. In TLS, encryption provides confidentiality, a message digest (or AEAD in the case of TLS 1.3) provides integrity, and certificate verification provides authentication. Without the verification of certificates, clients are foregoing authentication in the communications channel. In this scenario, it is possible for the client to be speaking to a malicious actor “securely”, as it were.

It should also be noted how checksums are not replacements for cryptographic signatures. Checksums can help to detect random errors in transmission but do not provide hard proof of data authenticity.

One final consideration is the possibility that a vendor’s website could become compromised. In that case, the firmware along with its associated hash could both be replaced with malicious versions. This situation can be mitigated only by applying a cryptographic signature to the firmware file itself. Perhaps Ubiquity will make the switch to signing their firmware binaries cryptographically, which would improve the overall security of its customers.

Ubiquity addressed this bug in their v2.0.9-hotfix.1 security update by removing the -k (--insecure) flag from the templated command.

This was the first submission to the program from awxylitol, and we hope to see more research from them in the future. Until then, you can find me on Twitter @TrendyTofu, and follow the team for the latest in exploit techniques and security patches.


Cautious users of the EdgeRouter seeking advice on how to upgrade the device properly should avoid the use of automatic upgrade feature for this update. They may want to download the firmware file manually from a browser and verify the hashes of the firmware before performing a manual upgrade by uploading the firmware file to the device through the web interface.

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.