Return of the Living Dead: How X-TRACK Resurrected a Critical zlib Bug
Jan 28, 2026·6 min read·2 visits
Executive Summary (TL;DR)
FASTSHIFT's GPS bike computers are running a zombie version of zlib. They copied code vulnerable to CVE-2022-37434 (a 4-year-old bug), didn't patch it, and now have a CVSS 10.0 RCE on their hands. To make matters worse, the fix introduces a potential crash loop.
A critical heap buffer overflow in FASTSHIFT X-TRACK firmware caused by an unpatched fork of the zlib library. This vulnerability allows remote code execution via malformed GZIP headers, specifically through the 'extra field' handling logic.
The Hook: Copy, Paste, Pwn
History doesn't repeat itself, but developers certainly repeat their git clones. In the world of embedded systems, "if it ain't broke, don't fix it" is the law of the land. Unfortunately, "if it is broke but compiles, ship it" is often the amendment. FASTSHIFT, the makers of the X-TRACK GPS sports bike computer, learned this the hard way.
At its core, X-TRACK uses a module called PNGdec to handle image rendering—likely for maps or custom UI elements. Buried deep inside Software/X-Track/USER/App/Utils/lv_img_png/PNGdec/src/inflate.c lies a direct copy of the venerable zlib library. But this isn't just any copy; it's a fossil. It lacks the critical security patches from 2022 that addressed CVE-2022-37434.
This isn't a complex logic error unique to the bike computer's proprietary algorithms. This is a supply chain specter. By forking code and failing to maintain upstream parity, FASTSHIFT reintroduced a critical heap buffer overflow that the rest of the world fixed years ago. The result? A CVSS 10.0 vulnerability that turns a high-end sports tracker into a remote shell for anyone who knows how to craft a bad GZIP header.
The Flaw: Trusting the Header
To understand the break, we have to look at how GZIP works. The GZIP file format allows for an optional "extra field" in its header. This field is variable-length and can contain arbitrary data. The inflate() function—the workhorse of decompression—uses a state machine to parse these headers byte by byte.
The vulnerability exists in the state responsible for copying this extra field into memory. The decompressor keeps track of two things: extra_len (the total size of the extra field stated in the header) and length (how many bytes are left to read). It calculates the current write offset (len) by subtracting the remaining length from the total length.
Here is the logic flaw: The code assumes that if you tell it the extra field is 4096 bytes, you have allocated a 4096-byte buffer to hold it. But what if the header claims the extra field is massive, but the destination buffer is tiny? The code calculates the offset, blindly trusts it, and prepares to write. It performs a check to ensure the amount of data being copied doesn't overflow the remaining space from that offset, but it never checks if the offset itself is already out of bounds.
It is the memory corruption equivalent of standing on a cliff edge. The code checks if your step forward is too big, but fails to notice you are already hovering Wile E. Coyote-style over the abyss. Once the memcpy hits, it's writing to the heap at an arbitrary offset relative to the buffer.
The Code: The Smoking Gun
Let's look at the C code responsible for this disaster. This snippet is from inflate.c before the patch. Pay attention to how len is calculated and used immediately as an offset for the destination pointer.
// The Vulnerable Logic in inflate.c
if (state->flags & 0x0400) {
// ... (logic to get next input bytes)
// 1. Calculate the offset into the destination buffer
len = state->head->extra_len - state->length;
if (state->head != Z_NULL && state->head->extra != Z_NULL) {
// 2. WRITE to state->head->extra + len
// The ternary operator checks copy size, but NOT the start position (len)
zmemcpy(state->head->extra + len, next,
len + copy > state->head->extra_max ?
state->head->extra_max - len : copy);
}
}If an attacker provides a GZIP stream where extra_len is significantly larger than extra_max, len becomes a large positive integer. The zmemcpy destination state->head->extra + len now points deep into the heap, far beyond the allocated extra buffer.
Now, look at the fix provided in Pull Request #120. They added a check, but in a rush to fix it, they introduced a new problem:
// The Patched (and slightly broken) Logic
if (copy) {
// NEW BUG: This line runs BEFORE checking if state->head is NULL!
len = state->head->extra_len - state->length;
if (state->head != Z_NULL &&
state->head->extra != Z_NULL &&
len < state->head->extra_max) { // The actual security fix
zmemcpy(state->head->extra + len, next, ...);
}
}The security fix is valid: len < state->head->extra_max prevents the OOB write. However, they moved the calculation of len above the NULL check for state->head. If state->head is NULL (which is valid in zlib usage if headers aren't needed), this code will dereference a NULL pointer and crash the device immediately. It stops the hacker, sure, but it also stops the bike computer.
The Exploit: Grooming the Heap
Exploiting this on an embedded device like the X-TRACK is an exercise in heap feng shui. The goal is to position a critical data structure immediately after the extra buffer in memory. Since we control the size of the allocation (via the GZIP header parameters) and the offset of the write (via the vulnerability), we have high-precision write primitives.
The Attack Chain:
- Preparation: The attacker sends a valid, benign PNG/GZIP to the device to initialize the memory allocator and fragment the heap in a predictable way.
- The Payload: The attacker sends a malformed GZIP stream. The header claims an
extrafield length of0xFFFF(65535), but the internal bufferextra_maxis only allocated for, say, 1024 bytes. - The Trigger: The
inflateloop processes the header. It calculateslenas, for example,4000. It attempts to write data tobuffer_base + 4000. - The Overwrite: At
buffer_base + 4000sits a function pointer for the UI event handler or a return address on the heap (if stack data is spilled there). Thezmemcpyoverwrites this pointer with the address of our shellcode (or a ROP gadget). - Execution: The next time the device refreshes the screen or processes a button press, it jumps to our code. We now control the GPS, the display, and any paired Bluetooth connections.
The Impact: Why Should We Panic?
A CVSS 10.0 isn't handed out lightly. The "Critical" rating comes from the combination of remote accessibility and total compromise. This isn't just a Denial of Service (though it is that, too).
Confidentiality: The X-TRACK stores precise GPS location history. An attacker could exfiltrate the user's home address, daily routes, and workout schedules. Integrity: The firmware can be permanently modified. An attacker could inject a persistent backdoor that survives reboots, turning the device into a botnet node or a localized BLE sniffer. Availability: The device can be bricked permanently by corrupting the bootloader or critical non-volatile storage during the memory corruption event.
Because the attack vector is likely the processing of map data or custom images loaded onto the device, this could be wormable if users share infected map packs or themes.
Official Patches
Fix Analysis (1)
Technical Appendix
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/S:N/AU:Y/R:U/V:C/RE:L/U:RedAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
FASTSHIFT X-TRACK FASTSHIFT | <= 2.7 | 2.8 |
| Attribute | Detail |
|---|---|
| CVE ID | CVE-2026-24823 |
| CVSS v4.0 | 10.0 (Critical) |
| CWE | CWE-787 (OOB Write) |
| Attack Vector | Network (Remote) |
| Affected Component | inflate.c (PNGdec module) |
| Root Cause | Unchecked pointer offset in zmemcpy |
MITRE ATT&CK Mapping
The software writes data past the end, or before the beginning, of the intended buffer.
Known Exploits & Detection
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.