CVE-2021-21401
CVE-2021-21401 — Invalid free() Call in Nanopb
Executive Summary
CVE-2021-21401 is a memory corruption vulnerability in Nanopb, a compact Protocol Buffers implementation written in ANSI C. The flaw allows an attacker to trigger invalid free() or realloc() calls when decoding a specifically crafted protobuf message that contains an oneof field with both pointer and non-pointer subfields. Successful exploitation can lead to memory corruption, denial-of-service (DoS), or potentially arbitrary code execution in applications that parse untrusted protobuf data. The vulnerability is fixed in Nanopb versions 0.3.9.8 and 0.4.5, and affects all prior releases.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Technical Analysis
Nanopb is widely used in embedded systems, IoT devices, and resource-constrained environments where the full Protocol Buffers C++ implementation is impractical. Its design trades features for minimal memory footprint, making it the default protobuf library for platforms like FreeRTOS, Zephyr, and various STM32/ARM targets.
The vulnerability resides in the message decoding logic that handles oneof fields. In Protocol Buffers, an oneof field represents a set of fields where only one member may be set at any given time. Within a single decoded message, all members of an oneof share the same memory space in the generated C struct.
The flaw manifests under the following conditions:
- The message type contains an
oneoffield. - The
oneofdirectly contains both a pointer field (e.g.,string,bytes) and a non-pointer field (e.g., an integer or enum). - The message data first contains the non-pointer field and then the pointer field.
When the decoder encounters the non-pointer field, it writes the field's value into the shared oneof storage in the C struct. However, when the decoder later encounters the pointer field in the same oneof, it treats the existing contents of that shared storage — regardless of what was written there — as a pointer value and passes it to free() or realloc(). Because the non-pointer field's raw integer/boolean value is now misinterpreted as a memory address, the invalid free() or realloc() call corrupts the heap allocator state.
This is a classic type-confusion / state-coherence bug: the decoder fails to clear or reset the oneof storage when switching between field types during message parsing.
Attack Surface:
- Any application that decodes untrusted protobuf data using Nanopb versions prior to 0.3.9.8 or 0.4.5.
- Network-facing services (TCP/UDP servers) accepting protobuf-encoded messages.
- Embedded firmware that receives input over wireless (BLE, Zigbee, LoRa) or serial interfaces.
- File parsers processing protobuf-encoded configuration or data files from untrusted sources.
CVSS Rationale (7.1): The vulnerability scores 7.1 due to network exploitability (AV:N), the requirement for a specific crafted message sequence (AC:H), no authentication needed (PR:N), no user interaction (UI:N), compromised integrity and availability (I:H/A:H), though confidentiality is not directly affected (C:N).
Exploitation Scenarios
Scenario 1: Network-Exposed IoT Device
Step 1 — Reconnaissance/target identification: An attacker identifies a device (smart home hub, industrial sensor, medical device) that exposes a network port accepting protobuf-encoded commands or telemetry. The attacker fingerprints the use of Nanopb by sending malformed protobuf messages and observing parsing errors or device behavior.
Step 2 — Flaw triggering:
The attacker crafts a malicious protobuf message where the target oneof field first encodes the non-pointer option (e.g., an integer field with a carefully chosen value such as a plausible heap address), then encodes the pointer option (e.g., a string field).
Step 3 — Payload/execution method: When the decoder completes parsing, it attempts to free the "pointer" value taken from the non-pointer field's storage. If the integer value approximates a valid heap allocation address, the attacker can cause a heap corruption; if not, an invalid free causes an immediate memory access violation (segfault/abort).
Step 4 — Post-exploitation impact: A successful heap overflow may allow the attacker to overwrite adjacent heap metadata, leading to arbitrary write primitives. On embedded systems with static memory layouts, this could enable arbitrary code execution within the firmware's privilege level. At minimum, repeated triggering causes a denial-of-service condition.
Scenario 2: Protobuf-Configuring File Parser
Step 1: Attacker delivers a specially crafted protobuf file (configuration file, application update package, log replay file) to a system with a Nanopb-based parser.
Step 2: The file contains an oneof field sequence where the non-pointer field appears before the pointer field. On parsing, the decoder performs an invalid free().
Step 3: A crafted value for the non-pointer field can be aligned to a real heap address, enabling heap grooming and exploitation, or left as garbage to cause a crash.
Step 4: In server environments, a crash in the parser process may require a restart, leading to availability loss. In security-critical systems (e.g., TPM, secure boot chain), a parser crash could bypass integrity checks.
Impact Assessment
Business Impact:
- Integrity/Confidentiality: While confidentiality is not directly compromised (CVSS C:N), memory corruption can be escalated to data tampering, authentication bypass, or arbitrary code execution depending on surrounding application logic.
- Availability: The most immediate impact is a crash (invalid free) that halts parsing, potentially crashing the entire application or kernel thread handling the input. For continuously running services or embedded systems, this is a DoS vector.
- Integrity: If heap metadata is corrupted, subsequent allocation/free operations may fail or behave unpredictably, corrupting application state.
Affected Systems:
- Embedded/IoT devices using Nanopb <0.4.5 or <0.3.9.8 (common in smart home, industrial control, automotive, and medical devices).
- Any application (desktop/server) linking a statically or dynamically compiled Nanopb version.
- Systems where the parser is reached via an untrusted network or file source.
Blast Radius:
- Code execution potential: With heap grooming, an attacker may achieve arbitrary write primitives. However, the complexity of precise heap manipulation on bare-metal/RTOS targets varies widely; many embedded systems lack ASLR/guard pages, making exploitation more feasible than on general-purpose OSes.
- Lateral movement: Limited — this is a parser-level flaw, not a network-level compromise. Lateral movement depends on what services the compromised process exposes.
- EPSS Score 0.01811 (76.6th percentile): Low absolute probability of exploitation in the wild, but above the median CVE, indicating attacker interest.
Detection Guidance
Sigma Rule
title: CVE-2021-21401 Nanopb Invalid Free Exploitation Attempt
id: 4f6c9e2d-7a8b-4c5d-9e0f-123456789abc
status: experimental
description: Detects crash or heap corruption indicators related to Nanopb decoding of crafted protobuf messages.
author: Threat Intelligence Team
date: 2025/02/10
logsource:
category: application
product: nanopb_parser
detection:
selection_crash:
EventID:
- 1000 # Application crash
- 1001 # Windows Error Reporting
Message|contains:
- 'free(): invalid pointer'
- 'heap corruption detected'
- 'glibc detected an invalid pointer'
- 'nanopb'
- 'pb_decode'
selection_memfail:
- level: 'error'
message|contains:
- 'invalid free'
- 'Segmentation fault'
- 'SIGSEGV'
condition: selection_crash or selection_memfail
timeframe: 5m
level: high
falsepositives:
- Normal application bugs unrelated to Nanopb
- Memory exhaustion or other system-level faults
tags:
- attack.t1190
- cve.2021.21401
- cwe.590
YARA Rule
rule CVE_2021_21401_Nanopb_Oneof_Corruption {
meta:
author = "Threat Intelligence Team"
description = "Detects nanopb message structures or binaries with suspicious oneof field patterns"
cve = "CVE-2021-21401"
date = "2025-02-10"
strings:
$nanopb_header = "nanopb" ascii nocase
$pb_decode = "pb_decode" ascii
$pb_free = "pb_release" ascii
$oneof = "oneof" ascii nocase
$proto_header = "\x0A\x10" // field 1, wire type 2
condition:
uint16(0) == 0x5A4D or // MZ (PE) or ELF magic
uint32(0) == 0x464C457F or
any of ($nanopb_header*) or
(any of ($pb_decode*) and any of ($oneof*) and any of ($proto_header*))
}
Suricata IDS Rule
alert tcp any any -> any any (msg:"CVE-2021-21401 Nanopb oneof invalid free trigger"; flow:established,to_server; content:"|0A|"; depth:5; pcre:"/\x0A\x10.{0,4}\xA2\x06/"; classtype:attempted-dos; sid:2025021001; rev:1; reference:cve,2021-21401;)
Note: This rule looks for protobuf wire-format patterns where field 1 (tag 0x0A = field 1, wire type 2) is followed within a message by another field within the same oneof region. Exact field numbers vary per application schema; the regex pattern may require adaptation to specific deployments.
Elastic Detection
any where
event.category in ("process", "system") and
(
(process.name != null and
process.args : ("*nanopb*", "*pb_decode*")) or
(
event.action in ("segfault", "coredump", "detected heap corruption") or
message : "*free(): invalid pointer*" or
message : "*heap corruption*" or
message : "*invalid free*"
)
)
Splunk SPL Query
index=* (sourcetype=linux_syslog OR sourcetype=app_log OR sourcetype=windows_eventlog)
| search "free(): invalid pointer" OR "heap corruption" OR "invalid free" OR "SIGSEGV" OR "Segmentation fault"
| table _time, host, source, sourcetype, message
| where message LIKE "%nanopb%" OR message LIKE "%pb_decode%" OR message LIKE "%CVE-2021-21401%"
| stats count by host, source, message
| sort - count
Wazuh OSSEC Rule
<group name="nanopb_cve_2021_21401,">
<rule id="393326" level="12">
<decoded_as>syslog</decoded_as>
<match>free\(\): invalid pointer|heap corruption detected|invalid free</match>
<description>CVE-2021-21401: Nanopb invalid free() detected - possible exploitation attempt</description>
<mitre>
<id>T1190</id>
</mitre>
<group>cve_2021_21401,nanopb,memory_corruption,</group>
</rule>
<rule id="393327" level="10">
<decoded_as>syslog</decoded_as>
<match>Segmentation fault|SIGSEGV|coredump</match>
<description>Process crash potentially related to Nanopb parsing (CVE-2021-21401)</description>
<group>cve_2021_21401,nanopb,availability,</group>
</rule>
</group>
Mitigation & Patch Guidance
Patch Confidence: 3/4 (LIKELY)
Confidence based on two independent sources (CNA and Debian) confirming the fix versions.
1. Immediate Mitigations (if patching is not immediately possible)
- Input sanitization: Validate incoming protobuf message size and structure at an earlier layer, rejecting messages that contain unexpected
oneoffield ordering (non-pointer first, then pointer). - Sandbox/container isolation: Run the Nanopb parser in a sandboxed environment to limit the blast radius of a memory corruption event.
- Rate limiting: Limit the volume of parsed messages to reduce the window for heap grooming and to mitigate DoS scenarios.
- Instrumentation: Compile with AddressSanitizer (ASan) or MemorySanitizer in test environments. In production, consider heap guard pages or a hardened allocator (e.g., Electric Fence, TLSF with integrity checks).
2. Patch / Upgrade Steps
- Upgrade Nanopb to version 0.4.5 or higher (or 0.3.9.8+ if still on the 0.3.x branch), per the official GitHub Security Advisory.
- For package manager users:
pip install nanopb>=0.4.5,apt install nanopb>=0.4.5, or equivalent for your distribution. - For embedded projects: Update the Nanopb submodule or vendored copy in your firmware source tree. Recompile and re-test the entire firmware, as protobuf wire-format compatibility is unchanged but generated C-code may differ.
- Verify patch: After upgrade, confirm
pb_decodebehavior on a crafted test message containing a oneof with mixed pointer/non-pointer fields — the decoder must not attempt a free after switching field types.
3. Long-Term Hardening
- Reduce untrusted data exposure: Route all protobuf parsing through a separate, least-privilege process with a minimal attack surface.
- Pre-validate protobuf schemas: Enforce that schemas requiring
oneoffields avoid mixing pointer types with value types where possible, or document the risk for all downstream consumers. - Memory safety audits: Conduct periodic fuzzing of nanopb decoders using AFL++, libFuzzer, or honggfuzz with coverage-guided input generation.
- Alternative library evaluation: For new projects with higher security requirements, consider Protobuf-C (C++), or FlatBuffers, which handle memory allocation differently.
- CI/CD pipeline integration: Add a build step that runs ASan/UBSan on tests exercising the
oneofdecoding paths.
References
- NVD Entry: https://nvd.nist.gov/vuln/detail/CVE-2021-21401
- GitHub Security Advisory (Nanopb): https://github.com/nanopb/nanopb/security/advisories/GHSA-9b77-9qq7-8cpw
- Debian Security Tracker: https://security-tracker.debian.org/tracker/CVE-2021-21401
- Nanopb Repository (release tags 0.3.9.8 / 0.4.5): https://github.com/nanopb/nanopb/releases
- Protocol Buffers Documentation (oneof fields): https://protobuf.dev/programming-guides/proto3/#oneof
Threat Actor Activity / Exploit-in-the-Wild: No active exploitation campaigns, zero-day exploits, or known threat actor use of CVE-2021-21401 have been identified as of the date of this report. The CVE is not on CISA's Known Exploited Vulnerabilities (KEV) catalog. Exploitation is considered unlikely in typical enterprise environments due to the specific field-ordering requirement and the niche usage of Nanopb, but the high EPSS percentile (76.6%) suggests potential future attacker interest given the memory-corruption class.