---
id: "CVE-2026-76787"
kind: "vulnerability"
aliases: ["CVE-2026-76787"]
title: "Zephyr UpdateHub: malformed CoAP probe reply crashes the device on each poll"
severity: "medium"
cvss: 5.9
epss: null
exploited: false
status: "open"
category: "runtime"
products: []
ecosystems: []
tags: ["embedded", "firmware-update", "iot", "runtime", "updatehub", "zephyr"]
not_affected_if: ["dtls-enabled", "no-polling", "updatehub-not-used"]
published_at: "2026-10-19T21:18:01+00:00"
first_seen_at: "2026-10-23T21:57:19+00:00"
updated_at: "2026-10-25T04:08:44+00:00"
sources: [{"source": "nvd", "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-76787", "first_seen_at": "2026-10-23T21:57:19+00:00"}]
---

# Zephyr UpdateHub: malformed CoAP probe reply crashes the device on each poll

The UpdateHub firmware-update client in Zephyr dereferences a NULL pointer when a CoAP probe response omits the sha256sum field. A network peer that sends such a response can crash the device every time it polls.

_Summary, check and fix written by a model from the sources; labelled as such._

## How to check

- Check whether the build enables UpdateHub, for example with `CONFIG_UPDATEHUB` in the project configuration.
- Check whether `CONFIG_UPDATEHUB_DTLS` is unset, which leaves the probe on plain CoAP over UDP port 5683.
- Check the Zephyr version in the tree with `west list zephyr` or the `VERSION` file.

## Fix

- Move Zephyr to a release that includes the sha256sum NULL check in subsys/mgmt/updatehub/updatehub.c; the exact fixed version is unknown from the sources.
- Enable `CONFIG_UPDATEHUB_DTLS=y` and point the client at a trusted update server to remove the on-path trigger.
- Until patched, disable UpdateHub polling or the updatehub autohandler if the product allows it.

## Timeline

- 2026-10-23T21:57:19+00:00 nvd https://nvd.nist.gov/vuln/detail/CVE-2026-76787

## From the sources

The UpdateHub firmware-over-the-air client parses the CoAP probe response with Zephyr's JSON library in z_impl_updatehub_probe() (subsys/mgmt/updatehub/updatehub.c). The decoder treats every descriptor field as optional — json_obj_parse() returns a bitmask of the fields it actually decoded and never fails on a missing one — so a response that omits the "sha256sum" string leaves the corresponding const char * in the zero-initialised struct resp_probe_any_boards / struct resp_probe_some_boards at NULL. Both metadata branches then computed sha256size = strlen(...objects.sha256sum) + 1 before any validation, dereferencing that NULL pointer; the SHA256_HEX_DIGEST_SIZE test that follows operates on a value the bad read has already produced and cannot prevent it.

The metadata is supplied entirely by the network peer: probe_cb() accepts the datagram, runs coap_packet_parse() and copies the payload with no token matching and no cryptographic check. With CONFIG_UPDATEHUB_DTLS unset — the default — start_coap_client() uses plain CoAP over UDP on port 5683 with no peer authentication and resolves the server via unauthenticated DNS, so an on-path attacker, a DNS spoofer, or an off-path attacker who wins the race with a forged source address can deliver the malformed metadata. A probe response as simple as "objects":[[{}],[{}]] suffices, and no user interaction is required because updatehub_autohandler() polls on CONFIG_UPDATEHUB_POLL_INTERVAL. When CONFIG_UPDATEHUB_DTLS=y the trigger narrows to a malicious or compromised update server.

The primitive is a read at address zero, not a write, so the impact is availability only. On targets where the low address range is unmapped or protected — MMU-based architectures, builds with CONFIG_NULL_POINTER_EXCEPTION_DETECTION_DWT/_MPU/_PMP, TrustZone non-secure images, and SoCs such as nRF54L whose 0x0–0x400 window is unmapped — the load faults and Zephyr's default fatal handler halts the system, giving a remotely re-triggerable denial of service on every poll. On classic Cortex-M targets where the vector table is readable at address zero the walk usually terminates immediately and the request is rejected by the length check, so the outcome is configuration- and SoC-dependent. There is no memory write, and the copied bytes are only compared against the locally computed image hash, so there is no information-disclosure channel.

The fix adds an explicit sha256sum == NULL test that returns UPDATEHUB_METADATA_ERROR ahead of the strlen() in both metadata branches. Debug builds (CONFIG_UPDATEHUB_LOG_LEVEL=4) still format the same pointers with %s in a LOG_DBG() loop that runs before the new checks, so that variant remains in debug configurations only.

---

_Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai_
