How SecMate Discovered Vulnerabilities in libcoap
We discovered two memory-safety issues in libcoap, a widely deployed IoT library. The first is an out-of-bounds write in a fixed buffer reachable via proxy requests that can crash servers with a single UDP packet. The second is an out-of-bounds read in OSCORE configuration parsing that can crash the parser on malformed input. Both vulnerabilities have amplified impact on the constrained embedded devices the library was built for.
About SecMate
SecMate combines static analysis with AI to find security vulnerabilities in source code, specifically in embedded devices and low-level code.
So far, we have reported more than 60 vulnerabilities across embedded and systems projects, most still under coordinated disclosure.
Today, we are sharing two of our findings in libcoap (the first two CVEs uncovered with SecMate), a widely-used CoAP implementation for IoT devices.
What is libcoap?
libcoap [1] is a C implementation of the Constrained Application Protocol (CoAP), standardized as RFC 7252 [2]. Think of CoAP as “HTTP for IoT”: a lightweight protocol for devices that can’t afford the overhead of full HTTP/TCP stacks.
The library is mature and widely deployed:
- Supports multiple TLS backends (OpenSSL, GnuTLS, Mbed TLS, wolfSSL)
- Runs on everything from Linux servers to ESP32 microcontrollers
- Used in smart home devices, industrial sensors, and critical infrastructure
Here is the catch: the devices running libcoap are often constrained. Limited memory, no MMU, no ASLR, no stack canaries. When you find a memory-safety issue in this context, the impact is amplified.
Vulnerability #1: Static Buffer Overflow in Address Resolution (CVE-2025-34468 [3])
Location: coap_resolve_address_info() in src/coap_address.c
The Bug
This vulnerability affects libcoap servers running in proxy mode. The vulnerable function resolves hostnames for CoAP proxy requests, copying the hostname into a fixed 256-byte static buffer without checking the length:
1static char addrstr[256]; // Fixed-size static buffer
2memset(addrstr, 0, sizeof(addrstr));
3if (address && address->length)
4 memcpy(addrstr, address->s, address->length); // No bounds check!
5else
6 memcpy(addrstr, "localhost", 9);
7getaddrinfo(addrstr, NULL, &hints, &res);
The address->length field comes directly from parsing CoAP options. An attacker controls this value.
The Attack
When a CoAP server runs in proxy mode, it accepts Proxy-Uri options that specify where to forward requests. The hostname from this URI flows directly into the vulnerable memcpy():
- A CoAP request arrives with a
Proxy-Urioption containing an oversized hostname (>256 bytes) - The proxy extracts the hostname and passes it to
coap_resolve_address_info() - The function calls
memcpy(addrstr, address->s, address->length)without bounds checking - The fixed-size buffer overflows, corrupting adjacent memory
- The server crashes or misbehaves; the exact impact depends on layout and mitigations
Impact
On a standard Linux system, this bug is likely to crash the server (Denial of Service). On embedded devices without memory protection, memory corruption can have broader consequences, but practical exploitation depends on layout and mitigations. One UDP packet, no authentication required when proxy mode is enabled.
Vulnerability #2: Out-of-Bounds Read in OSCORE Parsing (CVE-2025-59391 [4])
Location: get_split_entry() in src/coap_oscore.c
The Bug
OSCORE (Object Security for Constrained RESTful Environments) is a security layer for CoAP. libcoap parses OSCORE configuration files that include boolean fields. The parsing code uses memcmp() with a user-controlled length:
1case COAP_ENC_BOOL:
2 if (memcmp("true", begin, end - begin) == 0)
3 value->u.value_int = 1;
4 else if (memcmp("false", begin, end - begin) == 0)
5 value->u.value_int = 0;
The problem: end - begin comes from the configuration file. If an attacker provides a value like trueAAAAAAAA... (thousands of characters), the memcmp() reads past the static string "true" into adjacent memory.
Impact
This issue requires the ability to provide malicious OSCORE configuration input, which is typically a local or trusted provisioning path rather than a network-facing vector. The realistic outcome is a crash or parse failure on malformed configs.
Theoretical Oracle Attack
In theory, if a local attacker could repeatedly provide different config files and observe success/failure signals from the parser, they could use this as an oracle to infer adjacent .rodata bytes one at a time. The diagram below illustrates what such an attack would look like:
However, this attack is impractical: OSCORE configuration is loaded from local files, so an attacker would need repeated local access to provide different config files and observe results. At that point, they likely have access that makes this leak scenario moot.
Why This Bug is Easy to MissThis vulnerability is subtle: the out-of-bounds read in
memcmp()may not crash or produce obvious symptoms during normal operation. Spotting it requires reasoning about how data flows through multiple operations, something traditional static analysis tools typically miss.
The Embedded Security Challenge
Both vulnerabilities share a common theme: they are worse on the devices libcoap is designed for.
Desktop and server systems have decades of hardening:
- ASLR randomizes memory layout
- Stack canaries detect buffer overflows
- DEP/NX prevents code execution from data segments
- Sandboxing limits blast radius
Constrained embedded devices often have none of these. A microcontroller running FreeRTOS or bare metal has:
- Flat, predictable memory layout
- No memory protection unit (MPU) or it is disabled for performance
- No operating system to enforce isolation
- Direct hardware access for any code that runs
This isn’t a criticism of libcoap. It is the reality of their target environment. But it means bugs like these deserve extra scrutiny.
Key InsightWhen auditing code for constrained devices, assume the worst-case exploitation scenario. Mitigations you take for granted on desktop systems simply don't exist.
Disclosure and Fix
We reported both vulnerabilities to the libcoap maintainers through coordinated disclosure. The team responded quickly and professionally, and fixes are now available in version 4.3.5a.
Disclosure Timeline
We want to thank the libcoap team, particularly Jon Shallow, for their responsiveness in addressing these issues. The maintainer responded on the same day for both reports. Maintaining open-source security infrastructure is often thankless work, and their quick turnaround made the ecosystem safer.
What’s Next
These two CVEs are just a sample of what SecMate has found. We’re continuing to analyze embedded codebases and report vulnerabilities through coordinated disclosure.
If you’re curious about our other findings, check out our disclosure page. And if you’re building embedded systems and want to find vulnerabilities before attackers do, reach out.
References
[1] libcoap. “libcoap - C-Implementation of CoAP” GitHub. Repository
[2] IETF. “RFC 7252 - The Constrained Application Protocol (CoAP)” IETF Datatracker, June 2014. RFC
[3] NVD. “CVE-2025-34468 - Static Buffer Overflow in libcoap” National Vulnerability Database. CVE
[4] NVD. “CVE-2025-59391 - Out-of-Bounds Read in libcoap” National Vulnerability Database. CVE
The SecMate Team