Taking off the blindfold:
Detecting persistent threats on Draytek
edge devices
About Us: Faraday research team
Octavio Gianatiempo Gaston Aznarez
Security Researcher Security Researcher
● Extensive experience working with firmware
● Strong foundation in computer science
● Active participants in CTF competitions
Talk Overview
Motivation for this research
Reverse Engineering Draytek Firmware
Vulnerabilities and Attack Vectors
Mitigation Strategies
Conclusions
Takeaways
Motivation for this
Research
Case study: Compromise of a client's infrastructure
● We had a call:
○ A client was compromised
○ Outdated Draytek equipment at the Edge
○ Mostly SOHO models (RTOS)
● Incident analysis:
○ Previous vulns with proven exploitation in high-end
models (Linux)
○ Are they exploitable in RTOS models?
○ Other unknown vulns?
○ Were these routers the attackers' entry point?
Increasing trend of • ZuoRAT: A sophisticated malware targeting SOHO
routers, allowing attackers to intercept traffic and
advanced attacks move laterally within networks.
targeting edge devices
• HiatusRAT: A recent malware targeting Draytek
business-grade routers, enabling attackers to
intercept network traffic and exfiltrate data while
maintaining stealth and persistence.
• BenignCertain: An NSA hacking tool targeting
Cisco routers, exploiting a vulnerability in the IKE
protocol to extract VPN credentials. It was leaked
by the Shadow Brokers.
Attack surface
● Over 500k exposed devices: Mainly in the UK
● Previous vulns affecting Draytek routers:
○ Information Disclosure (CVE-2024-23721)
○ Format string (CVE-2023-31447)
○ Cross-Site Scripting (CVE-2023-23313)
○ Unauthenticated RCE (CVE-2022-32548)
○ RCE (CVE-2020-19664)
○ Stack-based buffer overflow (CVE-2020-14473 /
CVE-2020-14993)
○ RCE (CVE-2020-14472 / CVE-2020-15415)
○ Stack-based buffer overflows (CVE-2020-10823 -
CVE-2020-10828)
○ ...
Understanding Draytek firmware for security research and assessment
● The problem: Analyzing closed-source firmware ● Target: Vigor2925 - MIPS32 -
is crucial for identifying vulnerabilities and RTOS
improving security measures.
● Our goal:
○ Reverse engineer the firmware format
○ Extract the firmware
○ Understand previous vulnerabilities
○ Look for new ones
○ Determine the attack vector
Reverse Engineering
Existing tools for extracting Draytek firmware
Draytools:
● Doesn’t work with new firmwares
● Last commit 12 yr ago: “The plan is to
update this to work with newer firmwares,
but that hasn't happened yet”
Previous work
Philippe Laulheret's at Hexacon 2022:
● Emulates Draytek’s bootloader to extract the RTOS
kernel
● This is a manual process that needs to be tweaked for
each firmware version
● Firmware images have other executable components
apart from the bootloader and the kernel
● Firmware images have filesystems
Our approach (1ro Argentina):
● Reverse engineer format and compression
● Write a tool that can be reused for different firmware
versions which:
○ Extracts filesystems
○ Extracts all executable components
Our tool: draytek-arsenal
A collection of tools to help Draytek firmware research
● Easy to install Python package
● Open-source: github.com/infobyte/draytek-arsenal
● Support for newer firmware versions
● We will show its features along this presentation
Firmware format:
● Bin Section
○ Header
○ Bootloader
○ Compressed RTOS image
○ FS with dynamic kernel modules (DLMs)
● Webfs Section:
○ Header
○ FS with web contents
Kaitai FTW
Compression algorithm
• Normal decompression: During boot, the RTOS is
decompressed to the same memory space as the
bootloader using bootloader defined functions.
• Decompression at runtime: By mapping the bootloader
and the RTOS together we saw that other files are
decompressed using the same bootloader functions.
• Error messages to the rescue: Is it LZ4? Why wasn’t
extracted by binwalk?.
Compression algorithm
● Custom LZ4: LZ4 standard block format wrapped in a
custom frame format.
● Frame magic: 0xaa1d7f50
● Frame format: [magic] + n x ([block_size][block_data])
● We wrote a compressor and decompressor
Dynamic kernel modules (DLMs)
● What is a DLM? ● During boot: Modules are loaded from the FW image:
○ Provides extra functionality to the RTOS ○ vr9geoip.dlm: Geolocation of IPs
○ Has firmware independent updates ○ vr9appe.dlm: Used to block traffic
○ Are dynamically loaded (boot or runtime) ○ vr9ssh.dlm: SSH server
Dynamic kernel modules (DLMs)
● Module upload/update:
○ APPE (and SSH) modules can be updated
○ Updates are saved to a persistent flash
FS
○ Modules form the FS are loaded first
○ Prevent loading of default DLM version
Dynamic kernel modules (DLMs)
● Module compression: Using the same algorithm as the
RTOS.
● Module encryption: We reverse engineered the module
decryption, which uses a modified version of XTEA/TEA
● We wrote a decrypter and encrypter
● DLM => Decryption => Decompression => Relocatable
ELF
How are DLMs loaded?
● ELF Loader: Loads relocatable ELFs
● Symbol table:
○ Used to resolve external dependencies present on
the RTOS image
○ Previously described by Philippe Laulheret only as
an aid for reversing
○ Now we know it is for linking modules with the
kernel.
● We can call many firmware functions from a module!
draytek-arsenal
● draytek-arsenal parse_firmware ● draytek-arsenal extract
❯ draytek_arsenal parse_firmware ❯ python3 -m draytek_arsenal extract --rtos
./v2925_394.all ./rtos.bin --fs ./fs --dlm ./dlms
./v2925_394.all
[+] Firmware information: [+] Extracting RTOS from firmware
bin: . . .
checksum: '0xb754a725' [*] All done..
header:
adjusted_size: '0xb6ca38' ❯ tree -L 3
bootloader_version: 5 .
next_section: '0x906c0b8' ├── dlms
product_number: 0 │ ├── vr9appe.dlm
size: '0xb6ca3c' │ ├── vr9geoip_data.dlm
version_info: '0x50' │ └── vr9ssh.dlm
rtos: ├── fs
size: '0x90173d' │ └── V2000
webfs: │ ├── act_sta.htm
checksum: '0xa3049ceb' │ ├── applet.htm
header: │ ...
adjusted_size: '0x5c7a94' │
next_section: '0x5c7a8c' │ └── WEBLOGIN.HTM
size: '0x5c7a98' ├── rtos.bin
└── v2925_394.all
Firmware is extracted, now what?
● To start reverse engineering we need the loading
address
● All Draytek bootloaders start by jumping to the
RTOS decompression function
● This function is located near the start
● Since the loading address is aligned, we look for
the decompression function and round down its
address.
● For example: 0x80028108 => 0x80028000
draytek-arsenal
● draytek-arsenal find_loading_addr
❯ python3 -m draytek_arsenal find_loading_addr ./rtos.bin
[*] First jump found
[*] Kernel decompression function at 0x80028108
[+] Loading address is 0x80028000
Vulnerabilities and
Attack Vectors
Some examples of vulnerabilities we found
● Insecure Password Storage: Passwords are
stored in plaintext.
● Non-Constant Time Comparison: Use of non-
constant time strcmp and memcmp for
password checking.
Some examples of vulnerabilities we found
• Predictable 2-Step Authentication Codes: The
codes used for two-step authentication derived
from the time elapsed since boot
deterministically.
• Null dereference in DHCP server: Can be
triggered from WAN
• Combined => 2FA Bypass
APP Enforcement signature update process
● Update process
○ Manual
○ Automatic
● Format? Json? Yaml?....
APP Enforcement signature update process
● No! APP Enforcement signatures are DLMs!!!
Module related vulnerabilities
● No Certificate Checking or SSL Enforcing
● Postauth Endpoints:
■ APPE signatures upload: Allows
uploading an APPE DLM
■ Configuration upload: Allows
uploading arbitrary DLMs (SSH
maybe? 🤔)
Three ways for an attacker to install a malicious module
● Post-auth endpoints: Attackers can exploit known credentials or other vulnerabilities to gain access to the
web UI.
● Module Update Process: Hijacking the process via configuration modification or DNS control.
● Supply Chain Attacks: Compromising device integrity through supply chain attacks.
How can we build a module?
● draytek-arsenal mips_compile
❯ python3 -m draytek_arsenal mips_compile ./mem_tools.dlm ./mem_tools.c
[+] Image 'draytek-arsenal' is present.
[*] Running mips_tools with: 'compile ./mem_tools.dlm ./mem_tools.c'
[+] Compiled with success. Bye.
● draytek-arsenal elf_to_dlm
❯ python3 -m draytek_arsenal elf_to_dlm --web-header ./mem_tools.dlm “key1” “key2”
./vr9appe.dlm
[*] Crating ./vr9appe.dlm from ./mem_tools.dlm
[*] Packing data
[*] Adding web header
[*] WH - Data len: 2472
[*] WH - Checksum: 0x94e6b7e1
[+] ./vr9appe.dlm creation success
SSH Backdoor
● Replace auth function with hook
Roadmap
SSH Backdoor:
● Reverse engineering the SSH module
○ Hook target: ssh_account_and_ldap_auth
● Writing and compiling a hook
● Merging the hook with the original module
● Modify target functions relocations
● Upload the DLM
Writing and compiling a hook
❯ python3 -m draytek_arsenal mips_compile
./hook.dlm ./hook.c
[+] Image 'draytek-arsenal' is present.
[*] Running mips_tools with: 'compile
./hook.dlm ./hook.c'
[+] Compiled with success. Bye.
Merging the hook with the original module
❯ python3 -m draytek_arsenal mips_merge ./vr9ssh.elf ./hook.elf ./backdoor.elf
[+] Image 'draytek-arsenal' is present.
[*] Running mips_tools with: 'merge ./vr9ssh.elf ./hook.elf ./backdoor.elf
[+] Compiled with success. Bye.
Modify relocations
● Find all relocations to the target symbol
● Replace all with our hook
SSH DEMO
Mitigation Strategies
Mitigation Strategies
● The problem: Persistent threats are facilitated by
dynamic module loading.
● First approach: Patched Firmware to remove the
feature
○ Avoid module loading at boot and APPE
update.
○ Disadvantage: Feature loss
Mitigation Strategies
● Designing a better solution
○ A mechanism for checking in-memory
integrity of other loaded modules
○ Should fetch updated signatures from a
trusted server (checking certificates this
time)
○ Check against other loaded module
signatures
○ Should be included with the firmware
○ Should notify users
Mitigation Strategies
● A PoC DLM Checker:
○ We will replace the APPE module with
our checker for ease of development.
○ Calculate signatures for valid DLMs
○ Check against other loaded modules
signatures when the DLM Checker is
loaded
○ Limitations:
■ Does not check signatures each
time a DLM is loaded.
■ Does not notify users
PoC DLM Checker:
MIPS J-Format
● Calculating DLM hashes:
○ One hash per section (MD5)
○ We only hash static sections
■ Static data
■ Code (only opcodes)
PoC DLM Checker:
● Dynamically finding DLMs:
○ When a module is loaded, an object is created and allocated by using linear_malloc.
○ This object holds the DLM name, a pointer to the text section, and a pointer to the main function
among other things.
○ The rest of the sections are copied after the text section
○ The order is usually defined by the compiler and should not change (text, rodata, data, bss).
DLM Checker: DEMO
Conclusions and takeaways
About these vulnerabilities
● We found these vulnerabilities analyzing Vigor2925 but they are present in several models:
○ Vigor165 and Vigor166 versions before 4.2.7
○ Vigor2620 and VigorLTE200 versions before 3.9.8.9
○ Vigor2860 and Vigor2925 versions before 3.9.8
○ Vigor2862 and Vigor2926 versions before 3.9.9.5
○ Vigor2133, Vigor2762 and Vigor2832 versions before 3.9.9
○ Vigor2135, Vigor2765 and Vigor2766 versions before 4.4.5.1
○ Vigor2865, Vigor2866 and Vigor2927 versions before 4.4.5.3
○ Vigor2962 and Vigor3910 versions before 4.3.2.8/4.4.3.1
○ Vigor3912 versions before 4.3.6.1
● The vulnerabilities are now patched but CVE assignment is still pending
Conclusions
On the offensive side:
● The RTOS is complex and has a lot of code: There are probably more vulns in these routers.
● Try our tools:
○ https://github.com/infobyte/draytek-arsenal
● And please let us know if you find new vulns!
Conclusions
On the defensive side:
● We showed that it’s possible to create a defensive module to verify the integrity of other modules
○ In an ideal situation the firmware should include this functionality
○ It should fetch valid module signatures from a trusted server
○ The device should implement a secure boot chain
Takeaways
● Closed source firmware is just another case of "security" by obscurity.
● Hides vulnerabilities that would otherwise be reported sooner by the community:
○ Plaintext password storage
○ Use of non time-constant comparison functions for sensitive data
○ Predictable 2FA code generation
○ Lack of SSL certificate validation
● Given the increase in edge device attacks, it would be important for the vendors to:
○ Facilitate observability and vulnerability research
○ Implement threat detection solutions
Thanks for your
time
Questions?
github.com/infobyte/draytek-arsenal
@faradaysec /company/faradaysec www.faradaysec.com