Defrosting PolarEdge’s Backdoor Open original source
This post was originally distributed as a private FLINT report to our customers on 15 July 2025.
Introduction
In early 2025, we published a blogpost reporting on a botnet we dubbed PolarEdge, first detected in January 2025, when our honeypots logged suspicious network activity. Analysis revealed an attempt to exploit CVE-2023-20118, resulting in remote code execution (RCE) that deployed a web shell on the target router.
On February 10, 2025, we observed a second exploitation of the same vulnerability. The attacker used a remote command to download and execute a script, which ultimately installed an undocumented implant. Our initial analysis indicates this implant is a TLS-based backdoor. We also uncovered related payloads from the same family targeting other devices—Asus, QNAP, and Synology routers.
Where our first publication focused on the botnet’s infrastructure, this follow-up provides an in-depth technical analysis of the undocumented implant, which we refer to as the PolarEdge Backdoor.
Technical Analysis
Recap of Previous Findings
On 10 February 2025, our honeypots monitoring Cisco routers detected multiple, simultaneous exploitations of CVE-2023-20118. These attacks originated from several IP addresses across different countries, all using the same User-Agent HTTP header:
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.85 Safari/537.36
Exploiting the vulnerability, the attacker downloaded via FTP a shell script named q. Once run, q downloads, and launches the PolarEdge backdoor on the compromised system.

Based on our first analysis we identified numerous other samples of this threat, affecting various devices—including Asus, QNAP, and Synology.
In this blogpost, we present our in-depth analysis on a single sample targeting QNAP NAS.
Overview of the PolarEdge Backdoor
The sample under analysis targets QNAP NAS devices and has the following SHA256 hash: a3e2826090f009691442ff1585d07118c73c95e40088c47f0a16c8a59c9d9082
This 1.6 MB binary is an ELF 64-bit executable, stripped and statically linked. The code itself is not obfuscated, although it employs several anti-analysis techniques that are described in a dedicated section of this blogpost.
The backdoor’s primary function is to send a host fingerprint to its command-and-control server and then listen for commands over a built-in TLS server implemented with mbedTLS. It also supports two alternative modes of operation:
- Connect-back mode: the backdoor acts as a TLS client to download a file
- Debug mode: an interactive mode that alters its configuration and logging behavior
Server Mode
When executed without arguments, the backdoor enters its default (server) mode. In this mode, it:
- Launches a TLS server to listen for incoming commands
- Spawns a dedicated thread that sends a daily host fingerprint to the C2
At startup, the backdoor moves and deletes certain files on the device. These files don’t appear to be directly related to the backdoor, and the purpose of these filesystem changes remains unclear. We think that’s to prevent other threat actors from accessing the systems with the same vulnerabilities. Here are the executed commands:
mv /usr/bin/wget /usr/bin/wget_w;mv /sbin/curl /sbin/curl_c
mv /share/CACHEDEV1_DATA/.qpkg/CMS-WS/cgi-bin/q_play.cgi /share/CACHEDEV1_DATA/.qpkg/CMS-WS/cgi-bin/q_play22.cgi
mv /share/CACHEDEV1_DATA/.qpkg/CMS-WS/cgi-bin/library.cgi /share/CACHEDEV1_DATA/.qpkg/CMS-WS/cgi-bin/library22.cgi
rm -f /share/CACHEDEV1_DATA/.qpkg/CMS-WS/cgi-bin/library.cgi.bak
Configuration
The backdoor’s configuration is embedded in the final 512 bytes of the binary. The configuration is separated into three parts, each part identified by a marker and separated by 8 null bytes. The content is then decrypted using a simple XOR with the single-byte key 0x11:
- The first part is identified by a 8-byte marker:
41 82 01 67 42 22 04 17and contains the following value :GLyzaagK. - This value is used to construct the path of a file
/tmp/GLyzaagK, referred to by the developer as the “Filter-file.” - Its exact role remains undetermined, but the backdoor checks for its presence before applying configuration updates in debug mode (see “Debug Mode”).
- The second part is identified by a 4-byte value,
12 02 11 77and contains the TLS server parameters: - The first field is a value used in the custom communication protocol (
fWbmufIFB) - The second field specifies the listening port (
49254). - The third part is identified by another 8-byte marker,
21 12 01 47 51 13 81 15, and contains the list of the C2 servers.

Figure 2: PolarEdge Backdoor configuration
TLS Server
The TLS server runs in the backdoor’s main thread and is implemented with mbedTLS v2.8.0. During initialization, the code calls mbedtls_x509_crt_parse twice, once to load the server’s leaf certificate and once to parse the embedded CA chain. The certificates are the following:
Leaf certificate:
- Subject: C=NL, O=PolarSSL, CN=localhost
- Issuer: C=NL, O=PolarSSL, CN=PolarSSL Test CA
- X509v3 Basic Constraints: CA:False
- Signature Algorithm: sha256WithRSAEncryption (RSA2048)
- X509v3 Authority Key Identifier: B4:5A:E4:A5:B3:DE:D2:52:F6:B9:D5:A6:95:0F:EB:3E:BC:C7:FD:FF
CA Certificate 1:
- Subject: C=NL, O=PolarSSL, CN=PolarSSL Test CA
- C=NL, O=PolarSSL, CN=PolarSSL Test CA
- X509v3 Basic Constraints: CA:TRUE
- Signature Algorithm: sha256WithRSAEncryption
- X509v3 Subject Key Identifier: B4:5A:E4:A5:B3:DE:D2:52:F6:B9:D5:A6:95:0F:EB:3E:BC:C7:FD:FF
- X509v3 Authority Key Identifier: B4:5A:E4:A5:B3:DE:D2:52:F6:B9:D5:A6:95:0F:EB:3E:BC:C7:FD:FF
CA Certificate 2:
- Subject: C=NL, O=PolarSSL, CN=PolarSSL Test CA
- Issuer: C=NL, O=PolarSSL, CN=PolarSSL Test CA
- X509v3 Basic Constraints: CA:TRUE
- Signature Algorithm: sha1WithRSAEncryption (2048)
- X509v3 Subject Key Identifier: B4:5A:E4:A5:B3:DE:D2:52:F6:B9:D5:A6:95:0F:EB:3E:BC:C7:FD:FF
- X509v3 Authority Key Identifier: B4:5A:E4:A5:B3:DE:D2:52:F6:B9:D5:A6:95:0F:EB:3E:BC:C7:FD:FF
CA Certificate 3:
- Subject: C=NL, O=PolarSSL, CN=Polarssl Test EC CA
- Issuer: C=NL, O=PolarSSL, CN=Polarssl Test EC CA
- X509v3 Basic Constraints: CA:TRUE
- Signature Algorithm: ecdsa-with-SHA256 (P-384)
- X509v3 Subject Key Identifier: 9D:6D:20:24:49:01:3F:2B:CB:78:B5:19:BC:7E:24:C9:DB:FB:36:7C
- X509v3 Authority Key Identifier: 9D:6D:20:24:49:01:3F:2B:CB:78:B5:19:BC:7E:24:C9:DB:FB:36:7C
CA Root certificates #1 and #2 are identical; they differ only in the self-signature hash (SHA-1 vs. SHA-256). The third CA certificate uses elliptic-curve cryptography, but its exact purpose is unknown.
Custom Binary Protocol
The backdoor implements a simple custom binary protocol over TLS. It relies on several tokens, some hardcoded in the .rodata section and one stored in the configuration. Here is an example of a valid request packet we sent to the TLS server in our lab.

Figure 3: Example of dissection of a request
Parsing an incoming request involves three main steps:
- Verify fixed magic tokens:
Token1,Token2,Token3(the ASCII space),Token6, andToken7match their hardcoded values. - Verify that
Token5matches the value stored in the backdoor’s configuration (WbmufIFB). - Process the
HasCommand: if it equals the ASCII character1(0x31), the backdoor parses and executes the command field, which consists of: - A two-byte length
- The command string itself
The response carries only the raw output of the executed command, with no additional framing or authentication. Anyone with read access to the installed binary can extract these magic values and issue arbitrary commands—we suppose that each device uses a unique set of tokens.
Fingerprinting
In the backdoor’s main operating mode, the fingerprinting runs in a dedicated thread. Once a day, this thread:
- Retrieves the C2 address from the configuration
- Gathers host information (the fingerprint)
- Creatres a TLS client to request, and potentially download a file from the C2
The following figure illustrates the first step. The function scans the trailing configuration block for the magic bytes marking the C2 section (b”\x21\x12\x01\x47\x51\x13\x81\x15” labeledMAGIC_3). It then decrypts the C2 address with xor_0x11 and reconstructs the URL to send the fingerprint.

Figure 4: Configuration parsing
The fingerprint includes the following data:
- Local IP addresses via
getifaddrs, excluding private-LAN, loopback, link-local, multicast, and broadcast addresses - MAC addresses by parsing
/proc/net/arp - Current process ID via
getpid - Filter-file path (from configuration)
- Device brand (
qnap) - Module version (
QNAP_2)
Next, the backdoor builds the HTTP GET query string using this format-string:
ip=%s&version=%s&module=%s&cmd=putdata&data=BRAND=qnap,FILTER_FILE=%s,PID=%d,MODULE=%s,MAC=%s
This format-string is encrypted and hardcoded in the sample. But it can also be passed as an argument.
It’s worth noting that the backdoor also reads /proc/uptime to obtain the device’s uptime; however, this value is not incorporated into the fingerprint.
Finally, the function calls set_ssl_client to send the GET request to the C2. If the server responds with a payload, that is written to /tmp/.qnax.sh. Before exiting, the thread checks for /tmp/.qnax.sh and, if present, executes it (see Figure 5).

Figure 5: Execution of /tmp/.qnax.sh
Anti-Analysis Techniques
Encryption Algorithms
In addition to the one-byte XOR used for the configuration, the backdoor employs two simple rotation ciphers to obfuscate section names. For example, these two strings are decrypted at startup to restore the names of the .init_rodata and .init_text sections in memory.
-joju^spebubdecrypts to.init_rodata(rotate letters by –1, special chars by +1)/nsny`yjcydecrypts to.init_text(rotate letters by +5, special chars by –1)
More notably, the sample implements the PRESENT block cipher (published in 2007) to decrypt these two small sections at runtime. The following figure comes from the specification of the PRESENT algorithm.
PRESENT is a 64-bit block cipher with an 80-bit key (128-bit is also supported by the spec). Its key schedule generates 32 round keys (each 80 bits, then truncated to 64 bits), and its core algorithm runs 31 rounds of:
- XOR with round key Kᵢ
- S-box substitution
- Bit-permutation
A final (32nd) round key is used only for key whitening outside the main rounds. Since the specification covers only single-block encryption, the authors of PolarEdge Backdoor chain multiple blocks by taking the full, untruncated output of the key-schedule as the next block’s key.
The following figure outlines the decryption process of this PRESENT implementation.

Figure 6: Section decryption algorithm
The key used in the sample is hardcoded and has the following value: 01 00 02 00 00 00 00 00 00 00
In our sample:
- The decrypted
.init_rodatacontains TLS certificates, magic values for parsing the configuration, and strings such as/tmp/.qnax.sh. - The decrypted
.init_textholds core routines: TLS server setup, fingerprinting logic, and hiding hooks.
A third encryption algorithm is used and combines Base64 encoding with an affine cipher over ASCII letters:
y = (9 x + 15) mod 26
This algorithm applies a per-character affine cipher over the ASCII letters while preserving case, and leaves all non-alphabetic bytes unchanged. For instance the encrypted string fa=%j&stgjfxu=%j&rxqpot=%j&nrq=apmqhmh&qhmh=KGHUQ=duha,WFOMTG_WFOT=%j,AFQ=%q,RXQPOT=%j,RHN=%j is decoded to ip=%s&version=%s&module=%s&cmd=putdata&data=BRAND=qnap,FILTER_FILE=%s,PID=%d,MODULE=%s,MAC=%s
Deceptive Techniques
To evade detection, the backdoor employed process masquerading during its initialization. It randomly picks one name from a predefined list:
igmpproxywscd/sbin/dhcpdhttpdupnpdiapp
It also hides its internals by attempting to mount over its own /proc/<pid> directory, binding /proc/11 or /proc/1 onto it.
Although the backdoor does not ensure persistence across reboots, it calls fork to spawn a child process that, every 30 seconds, checks whether /proc/<parent-pid> still exists. If the directory has disappeared, the child executes a shell command to relaunch the backdoor.

Figure 7: Child process ensuring re-execution of the backdoor
Other modes of operation
In addition to its default server mode, the PolarEdge Backdoor supports two auxiliary modes: the connect-back mode and a debug mode.
Connect-back Mode
In connect-back mode, the backdoor acts as a TLS client to download a file from a remote server. It requires the following command-line arguments:
-m cw -q <query_string> -f <local_filename> -h <host> -e <port>
Using these parameters, it constructs and issues an HTTP GET request over TLS and writes the response body to the specified local file.

Figure 8: Creation of the GET Request of the connect-back mode
D(ebug?) Mode
When the backdoor is executed with the options -m d -d <encrypted and base64 value>, the backdoor enters in a special mode that updates only its C2 address from the value passed in parameters. In other words, this mode provides a way to update its C2. This mode was useful during our analysis to redirect fingerprint requests to our own server. This is why we named this mode the “debug mode”.
In this mode, the backdoor first checks for the presence of the filter file /tmp/GLyzaagK. If that file exists, it:
- Base64-decodes the value passed to
-dparameter - Decrypts the result using the affine cipher described earlier
- Overwrites its C2 configuration with the decrypted result.
For example, to redirect communications to 127.0.0.1:58425, an operator could run:
touch /tmp/GLyzaagK./backdoor -m d -d MTI3LjAuMC4xOjU4NDI1LG94bmhvY3hqbTo1ODQyNQ==
After this command, the backdoor’s C2 list becomes: 127.0.0.1:58425,localhost:58425
Conclusion
Our reverse-engineering of the PolarEdge Backdoor reveals an implant built around a custom TLS server and an unauthenticated binary protocol for command execution. Its configuration is stored in the final 512 bytes of the ELF image, obfuscated by a one-byte XOR, while the .init_rodata and .init_text sections are decrypted at runtime using a chained PRESENT block cipher.
The implant also employs basic anti-analysis measures such as randomized process names, /proc remounting, and a watchdog fork, and it supports auxiliary connect-back and debug modes for file retrieval and on-the-fly C2 updates.
IoCs and Technical Details
Analyzed file: a3e2826090f009691442ff1585d07118c73c95e40088c47f0a16c8a59c9d9082
Yara rule:
rule PolarEdgeBackdoor{
meta:
id = "c3749828-4345-424e-a1f4-d13ed227e6d2"
version = "1.0"
malware = "PolarEdge Backdoor"
description = "Detects PolarEdge Backdoor"
source = "Sekoia.io"
creation_date = "2025-07-10"
classification = "TLP:GREEN"
strings:
$marker1 = {41 82 01 67 42 22 04 17}
$marker2 = {21 12 01 47 51 13 81 15}
$s1 = "mode"
$s2 = "query_str"
$s3 = "server_port"
$s4 = "m:h:e:f:q:d:"
$PresentInvSBOX = {05 00 0E 00 0F 00 08 00
0C 00 01 00 02 00 0D 00
0B 00 04 00 06 00 03 00
00 00 07 00 09 00 0a 00}
condition:
uint32be(0) == 0x7f454c46 and $PresentInvSBOX and
(all of ($marker*) or all of ($s*)) and
filesize < 2MB
}
Feel free to read other Sekoia.io TDR (Threat Detection & Research) analysis here:
- Silent Smishing : The Hidden Abuse of Cellular Router APIs
- APT28 Operation Phantom Net Voxel
- Predators for Hire: A Global Overview of Commercial Surveillance Vendors
- Global analysis of Adversary-in-the-Middle phishing threats
- The Sharp Taste of Mimo’lette: Analyzing Mimo’s Latest Campaign targeting Craft CMS