Introduction
An incomplete pointer validation flaw in Espressif's ESP-TEE secure service wrappers lets an unprivileged application in the Rich Execution Environment write attacker-controlled data directly into Trusted Execution Environment memory. That breaks the hardware-enforced isolation that is the entire point of having a TEE. Espressif has shipped over 1 billion wireless connectivity chips globally, and the ESP32 module market was valued at $1.8 billion in 2025, so the potential blast radius across consumer and industrial IoT deployments is substantial.
Espressif Systems is a publicly listed, fabless semiconductor company headquartered in Shanghai. It produces the widely adopted ESP32 family of IoT SoCs, and its ESP-IDF (IoT Development Framework) is the primary SDK for building firmware on these chips. The ESP-TEE framework, supported on RISC-V-based ESP32-C6 and ESP32-C5 SoCs, provides hardware-enforced isolation between a secure world (TEE) and a non-secure world (REE). It protects cryptographic accelerators, attestation, OTA updates, and secure storage from untrusted application code.
Technical Information
The REE/TEE Trust Boundary
ESP-TEE partitions CPU execution time into two worlds: the secure TEE world and the non-secure REE world. Hardware-enforced memory isolation separates them. When a user application in the REE needs a TEE-protected peripheral (AES, SHA, ECC, HMAC, SPI, MMU, WDT) or a security feature like attestation or secure storage, it calls a secure service wrapper. These wrappers, implemented in esp_secure_services.c and esp_secure_services_iram.c, move execution into the TEE world and perform the requested operation on the caller's behalf.
The whole architecture rests on one invariant. The TEE must never trust a pointer supplied by the REE without thorough validation. CVE-2026-45328 exists because the wrappers broke that invariant.
Root Cause: Incomplete Pointer Validation
The TEE secure service wrappers used a function called esp_tee_ptr_in_ree() to confirm that caller-supplied pointers sat inside the REE's memory region. They applied that check inconsistently. Many wrappers never validated output pointers, pointers embedded in struct contexts (such as cryptographic context structures), or buffer end addresses. Several functions had zero input validation, including _ss_esp_sha_read_digest_state(), _ss_esp_sha_write_digest_state(), _ss_esp_sha_block(), _ss_esp_ecc_point_verify(), _ss_esp_tee_sec_storage_gen_key(), and _ss_psa_initial_attest_get_token_size().
Where validation did exist, the pattern itself was unsafe. The old code checked the start and end pointers independently:
// BEFORE — only checks start/end pointers, no overflow protection bool valid_addr = ((esp_tee_ptr_in_ree((void *)input) && esp_tee_ptr_in_ree((void *)output)) && (esp_tee_ptr_in_ree((void *)(input + length)) && esp_tee_ptr_in_ree((void *)(output + length))));
That check is open to integer overflow. An attacker who controls input and length can pick values where input + length wraps around to an address inside the REE region, while the actual write extends into TEE memory.
Integer Overflow in OTA Write Bounds Check
A separate but related vulnerability existed in esp_tee_ota_ops.c. The bounds check for OTA writes contained a classic integer overflow:
// BEFORE — rel_offset + size can wrap to a small value, bypassing the check if (rel_offset + size > ota_handle.tee_next.pos.size) { ESP_LOGE(TAG, "Out of region write not allowed!"); return ESP_FAIL; }
An attacker controlling both rel_offset and size could choose values whose sum wraps around to a small number that passes the check. From there they could write arbitrary data outside the intended TEE OTA partition and potentially overwrite secure boot code or other TEE firmware.
Disabled Heap Assertions in the TEE
On top of that, the TEE's internal heap assertion mechanism in multi_heap.c was disabled outright:
// BEFORE — assertion was a no-op inline static void multi_heap_assert(bool condition, const char *format, int line, intptr_t address) { (void)condition; // Explicitly cast to void, effectively ignoring the condition esp_rom_printf(format, line, address); }
When the TEE detected heap corruption, it logged a message and kept running. An attacker could exploit the corrupted heap state for further privilege escalation without ever triggering a defensive halt.
Exploitable Dispatch Table in Writable Memory
The secure service call table (tee_sec_srv_tbl_int_mem[] in esp_secure_service_table.c) held function pointers for every internal trusted service exposed to the normal world. Before the fix, the table sat in TEE DRAM with read/write permissions. An attacker who achieved an arbitrary write inside the TEE through the out-of-bounds write could overwrite entries in this table, redirect secure service calls to attacker-controlled code, and escalate to arbitrary code execution within the TEE.
Attack Flow
-
An attacker gains local code execution in the REE through a malicious application, a compromised firmware update, or a chained vulnerability such as CVE-2026-45542 (heap buffer overflow in protocomm over Bluetooth) or CVE-2026-45541 (WebSocket Server null pointer dereference).
-
The attacker calls a TEE secure service wrapper (for example, an AES operation) with a crafted output pointer that targets TEE-resident DRAM instead of REE memory. The wrapper either skips validation of the output pointer or uses the overflow-susceptible pattern, so the TEE accepts the request.
-
The TEE hardware peripheral writes its output (ciphertext, hash digest, etc.) to the attacker's chosen address in TEE memory, corrupting TEE data structures.
-
Optionally, the attacker first uses the companion vulnerability CVE-2026-45329 (out-of-bounds read in the same wrappers) to leak the TEE memory layout and find the exact address of the secure service dispatch table.
-
The attacker aims the out-of-bounds write at the dispatch table and replaces a function pointer with the address of attacker-controlled code.
-
On the next secure service call, the TEE dispatches to the attacker's code. The attacker now has arbitrary code execution in the secure world, with full access to all TEE-protected peripherals, cryptographic keys, attestation credentials, and secure storage.
CVSS Scoring Context
The CVSS v3.1 base score of 9.3, with vector CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H, reflects the Scope Changed (S:C) metric. The attack starts in the non-secure REE but affects the secure TEE environment, crossing a trust boundary. The v3.1 assessment rates attack complexity as Low and requires no privileges, so exploitation is straightforward once an attacker has local access.
Patch Information
Espressif fixed CVE-2026-45328 in six commits across three branches (master, release/v6.0, and release/v5.5), all authored by Laukik Hase. The fixes ship in ESP-IDF versions 5.5.5 and 6.0.1 and are tracked under GHSA-mmgp-73p4-92xp. No runtime workaround exists. Updating the TEE firmware is the only remediation path.
The changes fall into four logical groups.
1. New Buffer Bounds Validator: esp_tee_buf_in_ree()
The most critical fix is a new inline helper in esp_tee_memory_utils.h that replaces the unsafe habit of checking start and end pointers independently:
FORCE_INLINE_ATTR bool esp_tee_buf_in_ree(const void *p, size_t len) { uintptr_t start = (uintptr_t)p; /* Reject zero-length and overflow */ if (len == 0 || len > (SIZE_MAX - start)) { return false; } return esp_tee_ptr_in_ree(p) && esp_tee_ptr_in_ree((const char *)p + len - 1); }
The function rejects zero-length buffers and arithmetic overflow before it does any pointer arithmetic. The len > (SIZE_MAX - start) check guarantees that start + len can never wrap. The patch also changes the pointer type from intptr_t (signed) to uintptr_t (unsigned), which removes the undefined behavior of signed overflow.
Espressif refactored every secure service wrapper to use the new function. The AES CBC wrapper, for example, went from the old two-call pattern to this:
// AFTER — validates each buffer with its full size, with overflow guard bool valid_addr = (esp_tee_buf_in_ree(ctx, sizeof(esp_aes_context)) && esp_tee_buf_in_ree(iv, 16) && esp_tee_buf_in_ree(input, length) && esp_tee_buf_in_ree(output, length));
The new code also validates the ctx struct pointer and the IV buffer, which the original code never checked. The same change went in uniformly across AES (all modes), SHA (with correct per-algorithm digest/state/block sizes from new helpers like get_sha_digest_size()), HMAC, Digital Signature, ECC, AEAD encrypt/decrypt, attestation, SPI flash operations, MMU mapping routines, and watchdog functions. Each check is followed immediately by ESP_FAULT_ASSERT(), which triggers a secure fault (abort) if validation fails.
2. OTA Write Integer Overflow Fix
The bounds check in esp_tee_ota_write() now uses an overflow-safe, two-step comparison:
// AFTER — checks size first, then uses subtraction to avoid overflow if (size > ota_handle.tee_next.pos.size || rel_offset > ota_handle.tee_next.pos.size - size) { ESP_LOGE(TAG, "Out of region write not allowed!"); return ESP_FAIL; }
It checks size on its own first, then confirms that rel_offset does not exceed the safe remainder. The sum can no longer wrap, which closes the wraparound exploit.
3. Heap Assertion Activation
The disabled multi_heap_assert() now enforces its condition:
// AFTER — actually aborts on heap corruption if (!condition) { esp_rom_printf(format, line, address); abort(); }
Heap corruption inside the TEE now halts execution instead of being silently ignored.
4. Service Call Table Made Immutable
The secure service call table (tee_sec_srv_tbl_int_mem[]) moved from DRAM_ATTR to IRAM_ATTR in esp_secure_service_table.c. TEE IRAM is marked read/execute (R/X) through the Physical Memory Attribution (PMA) unit, while DRAM is read/write (R/W). With the function pointer table in IRAM, nobody can tamper with it at runtime, so an attacker who achieves an arbitrary write inside the TEE still cannot redirect secure service calls.
Commit References
| Commit | Branch | Purpose |
|---|---|---|
| 145ba4c | master | Input validation checks for TEE service calls |
| 764626a | master | Move secure service call table to TEE IRAM |
| 7867f4a | release/v6.0 | Input validation backport |
| 440a5d1 | release/v6.0 | Table relocation backport |
| eebabaf | release/v5.5 | Input validation backport |
| afd14ab | release/v5.5 | Table relocation backport |
Across the three branches, the patch comes to roughly 570 additions and 154 deletions per branch, a measure of how much missing validation had to be added.
Affected Systems and Versions
The vulnerability affects the esp_tee component in the following ESP-IDF versions:
| Affected Version | Patched Version |
|---|---|
| ESP-IDF 5.5.4 | ESP-IDF 5.5.5 |
| ESP-IDF 6.0 | ESP-IDF 6.0.1 |
The vulnerable code paths exist only when the ESP-TEE framework is enabled. ESP-TEE is supported on RISC-V-based Espressif SoCs, specifically the ESP32-C6 and ESP32-C5. Devices using other ESP32 variants (ESP32, ESP32-S2, ESP32-S3, ESP32-C3) are not affected, because they do not support the ESP-TEE framework.
If the firmware configuration does not enable the TEE framework, the vulnerable secure service wrappers are not compiled into the firmware and the device is not exposed to this vulnerability.
Vendor Security History
Espressif maintains a published security policy and a vulnerabilities page in the ESP-IDF Programming Guide that tracks every CVE discovered and fixed in each release. The CVE-2026 disclosure period shows security issues across multiple components:
| CVE ID | Vulnerability | Component |
|---|---|---|
| CVE-2026-45328 | Out-of-Bounds Write | ESP-TEE Secure Service Wrappers |
| CVE-2026-45329 | Out-of-Bounds Read | ESP-TEE Secure Service Wrappers |
| CVE-2026-46532 | Heap Out-of-Bounds Read | Bluedroid |
| CVE-2026-45542 | Heap Buffer Overflow | protocomm over Bluetooth |
| CVE-2026-45541 | Remote Null Pointer Dereference | WebSocket Server |
| CVE-2026-25532 | WPS Enrollee Fragment Integer Underflow | WPS |
| CVE-2025-55297 | BluFi Example Memory Overflow | BluFi Reference Application |
CVE-2026-45328 and CVE-2026-45329 were disclosed together and both affect the same ESP-TEE boundary layer, which points to systemic input validation gaps in the TEE component. Independent security firms have found problems in other Espressif components before. NCC Group (via Fox IT) discovered memory corruption and cryptographic weaknesses in Espressif's BluFi reference application, and Nozomi Networks identified replay vulnerabilities in Espressif's ESP-NOW protocol.
Security researcher Eun0us of the Espilon Worker team reported CVE-2026-45328 under responsible disclosure. Espressif's mahavirj published the advisory on May 13, 2026, with patches available across multiple release branches at the same time, a sign of an effective coordinated disclosure process.
References
- GHSA-mmgp-73p4-92xp: Out of Bounds Write in ESP-TEE Secure Service Wrappers
- CVE-2026-45328 Detail (NVD)
- Out of bounds write in ESP-IDF (cybersecurity-help.cz)
- ESP-TEE User Guide (ESP32-C6)
- ESP-IDF Vulnerabilities Page
- ESP-IDF Security Policy
- GHSA-w82j-7q63-7pqm: Out of Bounds Read in ESP-TEE (companion CVE-2026-45329)
- Out of bounds read in ESP-IDF, CVE-2026-45329 (cybersecurity-help.cz)
- Building Hardware Enforced Trust into Matter Devices with ESP-TEE
- Commit 145ba4c: Add missing input validation checks
- Commit 764626a: Move secure service call table to TEE IRAM
- Commit 7867f4a: Input validation backport (v6.0)
- Commit 440a5d1: Table relocation backport (v6.0)
- Commit eebabaf: Input validation backport (v5.5)
- Commit afd14ab: Table relocation backport (v5.5)
- Espressif Leads the IoT Chip Market with Over 1 Billion Chip Sales
- ESP32 Module Market Research Report 2034
- CISA Known Exploited Vulnerabilities Catalog
- Espressif Security Implementation Guide



