OpenBSD mpls_do_error: Remote Kernel Stack Disclosure via MPLS Label Stack Over-read
Full Disclosuremailing list archivesFrom: shj <shahriyar () byteray co uk>Date 2026-6-21 03:54:3 Author: seclists.org(查看原文) 阅读量:8 收藏

fulldisclosure logo

Full Disclosure mailing list archives


From: shj <shahriyar () byteray co uk>
Date: Fri, 19 Jun 2026 07:32:14 +0200

------------------------------------------------------------------------
OpenBSD mpls_do_error: Remote Kernel Stack Disclosure via MPLS Label Stack Over-read
------------------------------------------------------------------------

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/

Current thread:

  • OpenBSD mpls_do_error: Remote Kernel Stack Disclosure via MPLS Label Stack Over-read shj (Jun 20)

文章来源: https://seclists.org/fulldisclosure/2026/Jun/17
如有侵权请联系:admin#unsafe.sh