A Deep Dive Into GarminOS And Its MonkeyC Virtual Machine
Tao Sauvage (Anvil Secure)
HITBSecConf2023 - Amsterdam · Day 2 · Track 1
Overview
In this insightful talk from HITB Amsterdam, Tao Sauvage of Anvil Secure takes attendees on a deep dive into the proprietary operating system and virtual machine powering Garmin's widely popular fitness and outdoor smartwatches. The presentation, titled "A Deep Dive Into GarminOS And Its MonkeyC Virtual Machine," meticulously dissects the architecture, custom language, and potential vulnerabilities within these ubiquitous devices. Sauvage's research uncovers a range of security flaws, from memory corruption to a complete bypass of the permission system, affecting over 100 Garmin device models.

Key moments
- 0:00 Introduction: Garmin devices and talk roadmap
- 4:40 GarminOS, Monkey C language, and Connect IQ store
- 22:50 Vulnerability: Integer overflow in font resource loading
- 29:50 Exploiting a full permission bypass vulnerability
- 36:50 Live demo: Exfiltrating encrypted firmware via exploit
- 40:40 Conclusion: 14 vulnerabilities, 100+ affected devices
- 47:00 Q&A: Extracting encryption key from newer firmware
A Deep Dive Into GarminOS And Its MonkeyC Virtual Machine
Speakers: Tao Sauvage (Anvil Secure)
Conference: HITB Amsterdam
YouTube: https://www.youtube.com/watch?v=KsqLb-l-TjA
Overview
In this insightful talk from HITB Amsterdam, Tao Sauvage of Anvil Secure takes attendees on a deep dive into the proprietary operating system and virtual machine powering Garmin's widely popular fitness and outdoor smartwatches. The presentation, titled "A Deep Dive Into GarminOS And Its MonkeyC Virtual Machine," meticulously dissects the architecture, custom language, and potential vulnerabilities within these ubiquitous devices. Sauvage's research uncovers a range of security flaws, from memory corruption to a complete bypass of the permission system, affecting over 100 Garmin device models.
The significance of this research extends beyond the consumer market. Garmin holds a substantial share in the fitness smartwatch segment, second only to Apple in overall smartwatch sales at one point, and remains a dominant player in its niche. Crucially, Garmin devices, such as the Fenix 3 and D2 models, are known to be issued to U.S. military pilots for their altimeter capabilities, underscoring the critical security implications of any vulnerabilities found within their ecosystem. This talk provides a comprehensive technical breakdown, offering valuable insights for both security researchers and defensive practitioners.
Sauvage’s work builds upon prior research, expanding the attack surface analysis from the Monkey C virtual machine’s opcodes to the broader application loading, resource parsing, permission mechanisms, and native C functions. The presentation culminates in a live demonstration of exfiltrating encrypted firmware from a modern Garmin device, showcasing the practical impact of the discovered vulnerabilities and highlighting significant areas for future security research.
Background
▶ Watch: Introduction: Garmin devices and talk roadmap (0:00)
Garmin, a company with decades of history, has diversified from its original GPS roots into a dominant force in the fitness and outdoor market. Their Forerunner series of devices, first introduced in 2003, now spans 44 different models, ranging from $150 to $1,000. These devices are packed with features, including GPS tracking, wrist-based heart rate and oxygen level monitors, and connectivity to external ANT+ sensors for advanced metrics. Beyond hardware, Garmin offers sophisticated software features like virtual coaches and a vibrant ecosystem of third-party applications.
A striking aspect of Garmin's approach is their commitment to a custom in-house proprietary operating system, known as GarminOS. Unlike many competitors, it's not based on Linux, FreeRTOS, or other common embedded OSes. GarminOS is primarily written in C, with C++ increasingly used for the UI layer. This custom nature means there is little to no public documentation, creating a significant barrier for external analysis. To support third-party applications, Garmin developed its own high-level language, Monkey C, which draws inspiration from Java, JavaScript, and Python. Applications written in Monkey C are published to the Connect IQ store, a bustling marketplace with thousands of apps, from watch faces and music streamers to niche utilities like barbecue timers.
The impetus for this research stemmed from a blog post by John from Arthritech in 2020. This prior work focused on analyzing the Monkey C virtual machine (VM) opcodes, specifically identifying an integer overflow vulnerability in the new_array opcode that could lead to out-of-bounds read/write on the heap. This discovery served as a "Pandora's Box" moment for Sauvage, revealing the rich, unexplored attack surface within Garmin's ecosystem. Key questions arising from the prior work included: how are Connect IQ (CIQ) applications (PRG files) loaded and parsed? How is the permission mechanism implemented? And what about the native C functions that handle performance-critical operations like cryptography, graphics, and Bluetooth?
The overall attack surface for Garmin devices is diverse, encompassing GPS, Bluetooth, ANT+, USB, and Wi-Fi (on some models). However, the focus of this research narrowed down to the application loading process and the Monkey C VM. This included exploring potential vulnerabilities in PRG file parsing (sections, offsets), signature validation, resource parsing (images, animations, fonts, JSON), the app storage mechanism, Monkey C opcodes, and the SDK's native functions. Each of these areas presented opportunities for memory-related vulnerabilities or logical flaws due to the underlying C codebase and custom implementations.
Key Findings
▶ Watch: Vulnerability: Integer overflow in font resource loading (22:50)
Tao Sauvage's extensive analysis of GarminOS and the Monkey C virtual machine yielded significant security discoveries, highlighting systemic vulnerabilities across a wide range of Garmin devices. In total, 14 distinct vulnerabilities were reported to Garmin, demonstrating a pervasive lack of robust security controls in critical areas.
The most impactful findings include:
- Full Permission System Bypass: A fundamental design flaw in the Monkey C VM's permission checking mechanism was identified, allowing an attacker to completely bypass all access controls. This means a malicious application, without requesting any specific permissions, could access restricted modules like GPS coordinates or user profile data. This vulnerability has been present since the very first version of the Connect IQ SDK, released in 2015.
- Arbitrary Code Execution and Memory Corruption: Several vulnerabilities were found that allow for full control over the execution flow of the firmware. These included classic memory safety issues such as stack overflows and heap overflows when parsing resources (e.g., fonts, strings, bitmaps, animations) and handling native C function calls. Furthermore, type confusion vulnerabilities were discovered, enabling attackers to corrupt memory and alter the properties of objects, such as changing an array's size to facilitate out-of-bounds reads and writes.
- Widespread Device Impact: Garmin confirmed that the reported vulnerabilities affected more than 100 different device models. This extensive list includes various fitness watches, outdoor handhelds, and GPS units for bikes, underscoring the critical nature of these flaws across Garmin's product line.
- Lack of Support for Older Devices: A concerning aspect of Garmin's response was their apparent policy of not supporting devices that are "two or three years old." This implies that a significant portion of the 100+ affected devices, despite having patches released for newer models, will remain permanently vulnerable, leaving users exposed to these critical security flaws.
- Developer Signature Weakness: The talk also highlighted a significant weakness in Garmin's application signing process. Users can simply sign their own applications with a developer signature, and the watch will accept them without any warning or option to disable this functionality. This effectively negates the security benefits of signature validation as a defense against unauthorized code execution, as an attacker can easily sign their malicious app.
Collectively, these findings paint a picture of an embedded ecosystem with substantial security debt, particularly concerning memory safety and permission enforcement, warranting immediate attention from both Garmin and its user base.
Technical Deep Dive
▶ Watch: Exploiting a full permission bypass vulnerability (29:50)
GarminOS is a bespoke operating system, primarily implemented in C, with C++ increasingly used for its UI components. Its proprietary nature means a significant lack of public documentation, requiring extensive reverse engineering. At its core, Garmin devices, such as the Forerunner 245 Music, typically run on an ARM Cortex-M3 microcontroller, specifically an NXP Kinetis Connect MCU family type, as identified through string analysis of the firmware.
The custom language, Monkey C, is a high-level, object-oriented language that blends features from Java, JavaScript, and Python. Developers use the Garmin SDK, which includes a Monkey C Java tool, to compile Monkey C source code into bytecode. This bytecode is then packaged into a PRG file, which is essentially a container for the application. The PRG file format is structured into several key sections: a header (containing metadata like application version and type), entry points, data section, code section (holding the Monkey C bytecode), resources (images, fonts, strings, JSON), and a permission section. This PRG file is subsequently interpreted by a virtual machine (VM) running on GarminOS. The Java tool itself is not obfuscated, allowing researchers to use tools like JD-GUI to understand the client-side compilation process and PRG file format.
Firmware analysis began with downloading beta firmware, which for older devices (pre-2021) was unencrypted. The firmware is distributed in a GCD5 format, an archive-like structure that has been previously documented. After extracting the raw firmware image, tools like Ghidra or IDA Pro could be used for reverse engineering. A crucial discovery during this phase was an offset difference in the firmware, where the bootloader occupies the initial 0x3000 bytes, necessitating a rebase of the firmware image to 0x3000 for correct disassembly and function analysis, especially for switch-case statements and inter-module calls. Newer devices (2021 onwards), however, employ encrypted firmware, posing an additional challenge for analysis. The speaker utilized Kaitai Struct, an open-source project for defining file formats using YAML, to create a deserializer for CIQ applications. This allowed for interactive parsing and understanding of the PRG file structure, aiding in the identification of parsing-related vulnerabilities.
Resource Parsing Vulnerabilities
Several memory corruption vulnerabilities were identified in the parsing of application resources:
- String Resource Out-of-Bounds Read: String resources begin with a 1-byte sentinel, a 2-byte length, and then the string bytes. When a virtual pointer to a string resource is converted to a physical pointer (an offset within a PRG section), boundary checks are performed to ensure the pointer is within the section. However, if a string definition is placed at the very end of a section, and its defined length (2 bytes) extends beyond the section boundary, the subsequent read operation to copy the string data will result in an out-of-bounds read. While often limited by null bytes in memory, this highlights a flaw in the boundary checking logic.
- Font Resource Integer Overflow (Heap Overflow): More critical was an integer overflow found when parsing font resources, specifically non-Unicode fonts (though the firmware supports both). The parsing logic calculates the size of the backing buffer needed by multiplying the
glyph_countby thedata_size. For example, aglyph_countof0x4501anddata_sizeof0x108would cause a 32-bit integer overflow, resulting in a much smaller buffer being allocated (e.g.,0x1F0bytes). Subsequently, a loop attempts to copy0x4501glyphs into this undersized buffer, leading to a heap overflow. This type of vulnerability was also found in bitmap and animation resource parsing.
Permission Bypass
Garmin implements a static permission system where applications declare required modules (e.g., Position, UserProfile) in their manifest. These are resolved to module IDs and stored in the PRG file's permission section. Permission checks occur during calls to get_module, get_value, or put_value, which parse class and field definitions to ascertain if a module requires permissions and if the requesting application has them.
Two key bypasses were identified:
- Context Version Bypass: An early edge case in the permission check allows applications running a
context_versionprior to2.3.0to access theSensorHistorymodule without explicit permission. By simply tampering with the PRG file's header to declare an older version (e.g.,2.0), an attacker can gain unauthorized access. - Fundamental Invoke Bypass: The most critical permission bypass exploits a design flaw: permission checks occur before the
invokefunction is called. Theinvokefunction directly executes code at a virtual pointer. By manipulating the field value (virtual pointer) in a class definition, an attacker can redirect an unprivileged function call to a privileged native C function (e.g.,open_web_page) without triggering any permission checks. This can be extended to embed the entire SDK within a malicious PRG file, clear all permission flags, fix relocation offsets, and then call arbitrary modules and functions without any declared permissions, effectively achieving a complete bypass of the permission mechanism.
Native C Functions Vulnerabilities
Garmin's SDK includes over 460 native C functions for performance-critical tasks like graphics, ANT+, Bluetooth, HTTP, and encryption. These functions represent a significant attack surface due to their C implementation.
cipher_initializeStack Overflow: This function, used to set up encryption contexts, takes a cipher type, encryption key, and IV. A vulnerability exists where amemcpyoperation to copy the encryption key (key_data) occurs before thekey_data_lengthis checked against theexpected_key_size(16 bytes for AES-128, 32 bytes for AES-256). By providing akey_datamuch larger than expected (e.g., 200 bytes), an attacker can overflow the stack and gain control over the return address, leading to arbitrary code execution.burst_payload_addVulnerabilities: This function, which stores 8-byte arrays in an internal buffer, exhibited two critical flaws:
- Signed Comparison: The buffer size check (
size < 0x2000) uses a signed comparison. This allows an attacker to provide a negativesizevalue, bypassing the upper bound check and enabling writes prior to the backing buffer, leading to out-of-bounds writes. - Type Confusion: By inheriting from the
burst_payloadobject and overriding itssizefield to a negative value, an attacker can achieve relative memory writes. Furthermore, by overriding theburst_data_blobto an array of integers and carefully crafting the data to be added, an attacker can achieve type confusion. This allows for overwriting object attributes in memory, for example, changing an integer object to a float object, or critically, modifying thesizeattribute of an array in memory. An array initially declared with 10 elements could have its size attribute overwritten to 1000, enabling subsequent out-of-bounds reads and writes.
Despite the complexities of embedded exploitation, Garmin devices notably lack modern exploit mitigations like ASLR (Address Space Layout Randomization), stack canaries, or signed pointers, making stack-based vulnerabilities particularly exploitable for arbitrary code execution.
Demo / Proof of Concept
▶ Watch: Conclusion: 14 vulnerabilities, 100+ affected devices (40:40)
The talk featured a compelling live demonstration showcasing the practical impact of the discovered vulnerabilities. The demo focused on exploiting the cipher_initialize stack overflow vulnerability on a newer Garmin device, the Forerunner 55, which, unlike older models, utilizes encrypted firmware.
The objective of the demonstration was to exfiltrate portions of the encrypted firmware from the device. The setup involved an Android phone paired with the Forerunner 55, running a listener to receive exfiltrated data. A custom-crafted malicious application was launched on the watch. This application, designed to exploit the cipher_initialize flaw, contained a byte array object in memory. Upon triggering the vulnerability, the exploit would hijack execution, locate this byte array in memory, and then overwrite its content with chunks of the firmware image.
To overcome the challenges of storing large amounts of data directly on the device (logs are cleared after 4KB, local storage struggles with large values), the exfiltrated firmware chunks were sent to the Android phone via HTTP requests. The speaker manually iterated through memory ranges, exfiltrating 1,000-byte chunks at a time. On the Android side, this data was then stored in an SQLite database, allowing for offline reconstruction of the firmware image.
The demo successfully displayed the first 1,000 bytes of the firmware on screen, confirming the exfiltration of encrypted firmware data. Sauvage noted that while the demonstration was performed manually, the process could be easily automated to extract larger portions of the firmware. This proof-of-concept effectively illustrated that even with encrypted firmware on newer devices, critical vulnerabilities can still be leveraged to gain access to the underlying system, enabling further analysis and potential compromise.
Defensive Implications
▶ Watch: Q&A: Extracting encryption key from newer firmware (47:00)
The findings presented in this talk carry significant implications for both Garmin and its users, highlighting critical areas where defensive strategies need substantial improvement.
- Prioritize Memory Safety and Input Validation: The prevalence of integer overflows, signed comparison issues, and out-of-bounds access vulnerabilities underscores a fundamental lack of robust memory safety practices in the C/C++ codebase. Garmin must implement rigorous input validation for all data processed from PRG files and external sources, ensuring that buffer allocations and memory operations are secure against overflows and underflows. Adopting safer coding standards and leveraging static analysis tools to identify such patterns proactively is crucial.
- Rethink Permission Model Design: The complete bypass of the Monkey C permission system reveals a critical design flaw. Permissions should be enforced at the point of execution (
invoke) rather than merely at the point of module or field lookup (get_module,get_value). A robust permission model requires granular checks throughout the execution lifecycle, making it difficult for an attacker to redirect execution to privileged functions without proper authorization. - Address Legacy Device Support: Garmin's policy of not patching devices older than two or three years leaves a vast user base vulnerable to known critical exploits. This creates a significant security risk for millions of users and could be leveraged in targeted attacks. Garmin should re-evaluate this policy and consider providing security patches for a longer lifespan, or at least clearly communicate the end-of-life security status for their devices.
- Strengthen Signature Validation: The ease with which applications can be signed with a developer certificate, without any user warning or option to disable developer mode, severely undermines the security value of application signing. Garmin should implement mechanisms to differentiate between official and developer-signed applications, providing clear user warnings for non-official apps and offering an option for users to restrict or disable the loading of developer-signed applications.
- Implement Modern Exploit Mitigations: The absence of exploit mitigations such as ASLR, stack canaries, and signed pointers on Garmin devices makes exploitation significantly easier. Implementing these standard protections would dramatically increase the difficulty and complexity for attackers attempting to achieve arbitrary code execution, even if memory corruption bugs persist.
- Expand Security Audits: The sheer number and variety of vulnerabilities found suggest a need for more comprehensive security audits, particularly focusing on the proprietary GarminOS, Monkey C VM, and the extensive library of native C functions. Fuzzing and dynamic analysis, in addition to static analysis, could uncover further vulnerabilities in less-explored attack surfaces like ANT+, BLE, Wi-Fi, USB, and the notification system.
By addressing these defensive implications, Garmin can significantly enhance the security posture of its devices and better protect its users from sophisticated attacks.
Key Takeaways
- Pervasive Memory Safety Issues: The GarminOS and Monkey C VM contain numerous memory corruption vulnerabilities, including integer overflows and out-of-bounds reads/writes, often stemming from inadequate input validation and unsafe C/C++ coding practices.
- Critical Permission System Flaws: A fundamental design flaw allows for a complete bypass of Garmin's static permission mechanism, enabling unprivileged applications to access sensitive data (e.g., GPS, user profiles) without authorization.
- Widespread Device Impact: Over 100 Garmin device models, including fitness watches, outdoor handhelds, and GPS units, are affected by the discovered vulnerabilities, indicating a systemic issue across their product lines.
- Lack of Exploit Mitigations: The absence of modern exploit mitigations like ASLR and stack canaries makes exploitation of memory corruption vulnerabilities straightforward, simplifying the process for attackers to achieve arbitrary code execution.
- Concerns for Older Device Security: Garmin's policy of not patching devices older than 2-3 years leaves a significant portion of its user base permanently vulnerable to known critical exploits, posing a substantial security risk.
- Weak Application Signing: The ability to easily sign applications with a developer certificate, without user notification or restriction, undermines the security benefits of signature validation and facilitates the deployment of malicious apps.
About the Speaker(s)
Tao Sauvage is a security researcher from Anvil Secure. His work focuses on embedded systems and reverse engineering, as demonstrated by his deep dive into GarminOS and the Monkey C virtual machine. He specializes in uncovering vulnerabilities in proprietary systems and understanding their underlying architecture.
Reviews
Dr. Zero (Offensive Security Researcher) — MUST SEE
This is what device security research looks like when someone actually does the work. Sauvage took a completely undocumented proprietary OS, reverse-engineered the VM, the app format, the permission system, and the native function layer, then dropped 14 vulns affecting 100+ device models. The permission bypass alone—present since 2015—is a masterclass in finding design flaws that code review misses.
Heather Calloway (CISO) — STRONG ACCEPT
Solid embedded systems research with direct enterprise relevance. 14 vulnerabilities across 100+ device models, including a complete permission bypass present since 2015. If your organization issues Garmin devices to executives, field personnel, or pilots, this changes your risk posture today.