Search
Find a vulnerability
Search criteria
ⓘ
Use this form to refine search results.
Full-text search supports keyword queries with ranking and filtering.
You can combine vendor, product, and sources to narrow results.
Enable “Apply ordering” to sort by date instead of relevance.
1 vulnerability found for mpls_do_error by Openbsd
GCVE-1988-2026-0320
Vulnerability from gna-1988 – Published: 2026-09-11 07:55 – Updated: 2026-09-11 11:31
VLAI
EPSS
VEX
Title
OpenBSD mpls_do_error: Remote Kernel Stack Disclosure via MPLS Label Stack Over-read
Summary
------------------------------------------------------------------------
------------------------------------------------------------------------
Affected: OpenBSD -current prior to 2026-06-18 (fixed in -current)
Vendor: OpenBSD
Severity: Medium
Reporter: Argus Systems
Date: 2026-06-12
CVE: CVE-2026-56099
1. SUMMARY
==========
The mpls_do_error() function in sys/netmpls/mpls_input.c parses an
incoming MPLS label stack into a fixed-size local array,
struct shim_hdr stack[MPLS_INKERNEL_LOOP_MAX] (16 entries). When the
parse loop completes without encountering the Bottom-of-Stack (BoS)
label, nstk reaches MPLS_INKERNEL_LOOP_MAX (16). Several subsequent
code paths then compute a copy length of (nstk + 1) * sizeof(*shim)
-- 17 entries -- and use it with icmp_do_exthdr(), M_PREPEND(), and
m_copyback() against the 16-entry stack object. This reads one
struct shim_hdr (4 bytes) past the end of the array, and that data is
reflected back to the sender inside the generated ICMP/MPLS error
response.
2. AFFECTED VERSIONS
====================
The (nstk + 1) length computations against the 16-entry stack[] array
were introduced with the ICMP/MPLS error path on 2010-09-13 (commit
201d6983add, "First shot at ICMP error handling inside an MPLS path.
Currently only TTL exceeded errors for IPv4 are handled."). The parse
loop was bounded by MPLS_INKERNEL_LOOP_MAX (16), but nothing rejected
a stack that ran to completion without a BoS bit, so nstk could reach
16 and the subsequent (nstk + 1) reads accessed stack[16].
Affected: OpenBSD -current prior to 2026-06-18 (mpls_input.c pre
v1.82).
3. DETAILS
==========
Vulnerable code (sys/netmpls/mpls_input.c, mpls_do_error):
struct shim_hdr stack[MPLS_INKERNEL_LOOP_MAX]; /* 16 entries */
...
for (nstk = 0; nstk < MPLS_INKERNEL_LOOP_MAX; nstk++) {
...
stack[nstk] = *mtod(m, struct shim_hdr *);
m_adj(m, sizeof(*shim));
if (MPLS_BOS_ISSET(stack[nstk].shim_label))
break;
}
/* no guard: with no BoS bit set, nstk == 16 here */
shim = &stack[0];
...
case IPVERSION:
...
if (icmp_do_exthdr(m, ICMP_EXT_MPLS, 1, stack,
(nstk + 1) * sizeof(*shim)))
return (NULL);
...
MPLS_INKERNEL_LOOP_MAX is defined as 16 and sizeof(struct shim_hdr) is
4. With nstk == 16, each of these copies 17 * 4 = 68 bytes from a
64-byte stack[] object, reading stack[16] -- one struct shim_hdr (4
bytes) of adjacent kernel stack -- and including it in the response.
The same (nstk + 1) length is later used to prepend and m_copyback()
the stack back onto the reflected packet:
M_PREPEND(m, (nstk + 1) * sizeof(*shim), M_NOWAIT);
...
m_copyback(m, 0, (nstk + 1) * sizeof(*shim), stack, M_NOWAIT);
so the leaked entry also travels on the wire as the 17th MPLS shim
header of the returned frame.
4. REACHABILITY
===============
The path is reachable remotely via mpls_input() -> mpls_do_error() on
systems that have MPLS enabled on an interface. The trigger is a
crafted MPLS frame (EtherType 0x8847) carrying 16 labels with no BoS
bit set and an outermost label TTL of 1, so the TTL-exceeded error
path is taken:
mpls_input (ttl <= 1)
-> mpls_do_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0)
The inner payload must be IPv4 so the IPVERSION branch is reached.
5. IMPACT
=========
Each crafted packet leaks 4 bytes of kernel stack memory adjacent to
the stack[] array. The leak is carried in the ICMP/MPLS extension
object of the error response reflected back to the sender, so an
attacker can harvest the leaked bytes.
6. PROOF OF CONCEPT
===================
A Python/Scapy PoC sends a 16-label MPLS frame with no BoS bit set
and an outermost label TTL of 1, then captures the reply. On a
vulnerable kernel the reply carries 17 MPLS shim headers on the wire;
the 17th (stack[16]) is the leaked kernel stack data.
PoC:
https://pop.argus-systems.ai/attachments/poc-008-mpls-stack-leak.py
7. FIX
======
Fixed in -current by mvs on 2026-06-18. The fix adds a guard that
drops a label stack which runs to completion without a BoS bit, so
nstk can no longer reach MPLS_INKERNEL_LOOP_MAX:
if (nstk >= MPLS_INKERNEL_LOOP_MAX) {
m_freem(m);
return (NULL);
}
Fix commit (mpls_input.c v1.82):
https://github.com/openbsd/src/commit/6a23123ec05f1eb29cfcaae0f3a468b2e1983cfd
8. TIMELINE
===========
2026-06-12 Reported to security () openbsd org with PoC
2026-06-18 Fix committed to -current
9. CREDIT
=========
Discovered and reported by Argus Systems (https://byteray.co.uk/).
10. REFERENCES
==============
Advisory:
https://pop.argus-systems.ai/advisory/adv-040.html
Proof of concept:
https://pop.argus-systems.ai/attachments/poc-008-mpls-stack-leak.py
Fix commit:
https://github.com/openbsd/src/commit/6a23123ec05f1eb29cfcaae0f3a468b2e1983cfd
_______________________________________________
Sent through the Full Disclosure mailing list
https://nmap.org/mailman/listinfo/fulldisclosure
Web Archives & RSS: https://seclists.org/fulldisclosure/
Severity
No CVSS data available.
Assigner
References
8 references
Impacted products
1 product
| Vendor | Product | Version | |
|---|---|---|---|
| Openbsd | mpls_do_error |
Affected:
unknown
|
Relationships
analysis
GCVE-1988-2026-0320 (this record)
- related CVE-2026-56099
{
"containers": {
"cna": {
"affected": [
{
"product": "mpls_do_error",
"vendor": "Openbsd",
"versions": [
{
"status": "affected",
"version": "unknown"
}
]
}
],
"credits": [
{
"lang": "en",
"type": "finder",
"value": "shj"
}
],
"descriptions": [
{
"lang": "en",
"value": "------------------------------------------------------------------------\n\n------------------------------------------------------------------------\n\nAffected:\u00a0 OpenBSD -current prior to 2026-06-18 (fixed in -current)\nVendor:\u00a0 \u00a0 OpenBSD\nSeverity:\u00a0 Medium\nReporter:\u00a0 Argus Systems\nDate:\u00a0 \u00a0 \u00a0 2026-06-12\nCVE:\u00a0 \u00a0 \u00a0 \u00a0CVE-2026-56099\n\n\n1. SUMMARY\n==========\n\nThe mpls_do_error() function in sys/netmpls/mpls_input.c parses an\nincoming MPLS label stack into a fixed-size local array,\nstruct shim_hdr stack[MPLS_INKERNEL_LOOP_MAX] (16 entries). When the\nparse loop completes without encountering the Bottom-of-Stack (BoS)\nlabel, nstk reaches MPLS_INKERNEL_LOOP_MAX (16). Several subsequent\ncode paths then compute a copy length of (nstk + 1) * sizeof(*shim)\n-- 17 entries -- and use it with icmp_do_exthdr(), M_PREPEND(), and\nm_copyback() against the 16-entry stack object. This reads one\nstruct shim_hdr (4 bytes) past the end of the array, and that data is\nreflected back to the sender inside the generated ICMP/MPLS error\nresponse.\n\n\n2. AFFECTED VERSIONS\n====================\n\nThe (nstk + 1) length computations against the 16-entry stack[] array\nwere introduced with the ICMP/MPLS error path on 2010-09-13 (commit\n201d6983add, \"First shot at ICMP error handling inside an MPLS path.\nCurrently only TTL exceeded errors for IPv4 are handled.\"). The parse\nloop was bounded by MPLS_INKERNEL_LOOP_MAX (16), but nothing rejected\na stack that ran to completion without a BoS bit, so nstk could reach\n16 and the subsequent (nstk + 1) reads accessed stack[16].\n\nAffected: OpenBSD -current prior to 2026-06-18 (mpls_input.c pre\nv1.82).\n\n\n3. DETAILS\n==========\n\nVulnerable code (sys/netmpls/mpls_input.c, mpls_do_error):\n\n\u00a0 struct shim_hdr stack[MPLS_INKERNEL_LOOP_MAX];\u00a0 \u00a0/* 16 entries */\n\u00a0 ...\n\u00a0 for (nstk = 0; nstk \u003c MPLS_INKERNEL_LOOP_MAX; nstk++) {\n\u00a0 \u00a0 \u00a0 ...\n\u00a0 \u00a0 \u00a0 stack[nstk] = *mtod(m, struct shim_hdr *);\n\u00a0 \u00a0 \u00a0 m_adj(m, sizeof(*shim));\n\u00a0 \u00a0 \u00a0 if (MPLS_BOS_ISSET(stack[nstk].shim_label))\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 break;\n\u00a0 }\n\u00a0 /* no guard: with no BoS bit set, nstk == 16 here */\n\n\u00a0 shim = \u0026stack[0];\n\u00a0 ...\n\u00a0 case IPVERSION:\n\u00a0 \u00a0 \u00a0 ...\n\u00a0 \u00a0 \u00a0 if (icmp_do_exthdr(m, ICMP_EXT_MPLS, 1, stack,\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 (nstk + 1) * sizeof(*shim)))\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 return (NULL);\n\u00a0 \u00a0 \u00a0 ...\n\nMPLS_INKERNEL_LOOP_MAX is defined as 16 and sizeof(struct shim_hdr) is\n4. With nstk == 16, each of these copies 17 * 4 = 68 bytes from a\n64-byte stack[] object, reading stack[16] -- one struct shim_hdr (4\nbytes) of adjacent kernel stack -- and including it in the response.\n\nThe same (nstk + 1) length is later used to prepend and m_copyback()\nthe stack back onto the reflected packet:\n\n\u00a0 M_PREPEND(m, (nstk + 1) * sizeof(*shim), M_NOWAIT);\n\u00a0 ...\n\u00a0 m_copyback(m, 0, (nstk + 1) * sizeof(*shim), stack, M_NOWAIT);\n\nso the leaked entry also travels on the wire as the 17th MPLS shim\nheader of the returned frame.\n\n\n4. REACHABILITY\n===============\n\nThe path is reachable remotely via mpls_input() -\u003e mpls_do_error() on\nsystems that have MPLS enabled on an interface. The trigger is a\ncrafted MPLS frame (EtherType 0x8847) carrying 16 labels with no BoS\nbit set and an outermost label TTL of 1, so the TTL-exceeded error\npath is taken:\n\n\u00a0 mpls_input\u00a0 (ttl \u003c= 1)\n\u00a0 \u00a0 -\u003e mpls_do_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0)\n\nThe inner payload must be IPv4 so the IPVERSION branch is reached.\n\n\n5. IMPACT\n=========\n\nEach crafted packet leaks 4 bytes of kernel stack memory adjacent to\nthe stack[] array. The leak is carried in the ICMP/MPLS extension\nobject of the error response reflected back to the sender, so an\nattacker can harvest the leaked bytes.\n\n\n6. PROOF OF CONCEPT\n===================\n\nA Python/Scapy PoC sends a 16-label MPLS frame with no BoS bit set\nand an outermost label TTL of 1, then captures the reply. On a\nvulnerable kernel the reply carries 17 MPLS shim headers on the wire;\nthe 17th (stack[16]) is the leaked kernel stack data.\n\nPoC:\nhttps://pop.argus-systems.ai/attachments/poc-008-mpls-stack-leak.py\n\n\n7. FIX\n======\n\nFixed in -current by mvs on 2026-06-18. The fix adds a guard that\ndrops a label stack which runs to completion without a BoS bit, so\nnstk can no longer reach MPLS_INKERNEL_LOOP_MAX:\n\n\u00a0 if (nstk \u003e= MPLS_INKERNEL_LOOP_MAX) {\n\u00a0 \u00a0 \u00a0 m_freem(m);\n\u00a0 \u00a0 \u00a0 return (NULL);\n\u00a0 }\n\nFix commit (mpls_input.c v1.82):\nhttps://github.com/openbsd/src/commit/6a23123ec05f1eb29cfcaae0f3a468b2e1983cfd\n\n\n8. TIMELINE\n===========\n\n\u00a0 2026-06-12\u00a0 Reported to security () openbsd org with PoC\n\u00a0 2026-06-18\u00a0 Fix committed to -current\n\n\n9. CREDIT\n=========\n\nDiscovered and reported by Argus Systems (https://byteray.co.uk/).\n\n\n10. REFERENCES\n==============\n\nAdvisory:\n\u00a0 https://pop.argus-systems.ai/advisory/adv-040.html\n\nProof of concept:\nhttps://pop.argus-systems.ai/attachments/poc-008-mpls-stack-leak.py\n\nFix commit:\nhttps://github.com/openbsd/src/commit/6a23123ec05f1eb29cfcaae0f3a468b2e1983cfd\n\n_______________________________________________\nSent through the Full Disclosure mailing list\nhttps://nmap.org/mailman/listinfo/fulldisclosure\nWeb Archives \u0026 RSS: https://seclists.org/fulldisclosure/"
}
],
"providerMetadata": {
"dateUpdated": "2026-09-11T11:31:51Z",
"orgId": "4e2abfbf-4a2a-4b76-a4e0-d77c18ba156c",
"shortName": "VULNARCHIVE"
},
"references": [
{
"tags": [
"technical-description",
"exploit"
],
"url": "https://vuln.freearchive.org/archive/full-disclosure/2026/Jun/17"
},
{
"tags": [
"technical-description"
],
"url": "https://seclists.org/fulldisclosure/2026/Jun/17"
},
{
"url": "https://byteray.co.uk/"
},
{
"url": "https://github.com/openbsd/src/commit/6a23123ec05f1eb29cfcaae0f3a468b2e1983cfd"
},
{
"url": "https://nmap.org/mailman/listinfo/fulldisclosure"
},
{
"url": "https://pop.argus-systems.ai/advisory/adv-040.html"
},
{
"url": "https://pop.argus-systems.ai/attachments/poc-008-mpls-stack-leak.py"
},
{
"url": "https://seclists.org/fulldisclosure/"
}
],
"source": {
"defect": [
"https://seclists.org/fulldisclosure/2026/Jun/17"
],
"discovery": "EXTERNAL"
},
"title": "OpenBSD mpls_do_error: Remote Kernel Stack Disclosure via MPLS Label Stack Over-read",
"x_gcve": [
{
"recordType": "analysis",
"relationships": [
{
"destId": "CVE-2026-56099",
"type": "related"
}
],
"vulnId": "GCVE-1988-2026-0320",
"x_vulnarchive": {
"archiveUrl": "https://vuln.freearchive.org/archive/full-disclosure/2026/Jun/17",
"automated": true,
"contentSha256": "e24b08f3e25ef3705050bb024d004359122d254f9f38f3497b113aec7f34dbd7",
"evidenceScore": 9,
"messageId": "",
"originalUrl": "https://seclists.org/fulldisclosure/2026/Jun/17",
"policy": "vulnarchive-1",
"sourceFormat": "text/html",
"sourcePublishedAt": "2026-06-19T05:32:14Z"
}
},
{
"recordType": "advisory",
"vulnId": "gcve-1988-2026-0320"
}
]
}
},
"cveMetadata": {
"assignerOrgId": "4e2abfbf-4a2a-4b76-a4e0-d77c18ba156c",
"assignerShortName": "VULNARCHIVE",
"datePublished": "2026-09-11T07:55:54Z",
"dateUpdated": "2026-09-11T11:31:51Z",
"state": "PUBLISHED",
"vulnId": "GCVE-1988-2026-0320"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}