VULNARCHIVE

GCVE-1988-2026-0362

OpenBSD sppp_pap_input: PAP authentication bypass

Description (en)

------------------------------------------------------------------------
OpenBSD sppp_pap_input: PAP Authentication Bypass via Zero-Length bcmp
------------------------------------------------------------------------

Affected:  OpenBSD all versions through 7.6 (fixed in -current)
Vendor:    OpenBSD
Severity:  High
Reporter:  Argus
Date:      2026-06-16


1. SUMMARY
==========

The sppp_pap_input() function in sys/net/if_spppsubr.c uses the
attacker-controlled name_len and passwd_len fields from the incoming
PAP frame directly as the comparison length for bcmp() against
configured credentials.

When both fields are set to zero, bcmp() returns 0 unconditionally
(bcmp with length 0 always succeeds). The existing upper-bound guard
(> AUTHMAXLEN) allows zero through. As a result, a PAP Auth-Request
with name_len=0 and passwd_len=0 passes credential validation and
triggers a PAP_ACK, authenticating the peer without any knowledge of
the configured username or password.

A secondary kernel heap over-read exists via the same root cause:
supplying a name_len larger than the allocation of the stored
credential causes bcmp to read past the heap object.


2. AFFECTED VERSIONS
====================

The bcmp comparison pattern was introduced with the original sppp
code import on 1999-07-01 (commit bda3414e, "lmc driver; ported by
chris () dqc org"). The zero-length bypass has been exploitable since
that date.

In February 2009 (commit 9c2f3d605fc), auth credential fields were
changed from fixed-size struct arrays to dynamically allocated
malloc(strlen()+1), and the bounds check was changed to
> AUTHMAXLEN (256). This decoupled the allocation size from the
comparison bound, enabling the heap over-read.

Confirmed against OpenBSD 7.6 (amd64) in QEMU/KVM.


3. DETAILS
==========

Vulnerable code (sys/net/if_spppsubr.c, sppp_pap_input):

  if (name_len > AUTHMAXLEN ||
      passwd_len > AUTHMAXLEN ||
      bcmp(name, sp->hisauth.name, name_len) != 0 ||
      bcmp(passwd, sp->hisauth.secret, passwd_len) != 0) {
          /* authentication failed */

name_len and passwd_len are parsed directly from the PAP frame
payload. bcmp(a, b, 0) always returns 0. The > AUTHMAXLEN guard
rejects values above 255 but permits zero.

The CHAP handler in the same file already had the correct pattern
with an exact-length pre-check:

  if (name_len != strlen(sp->hisauth.name)
      || bcmp(name, sp->hisauth.name, name_len) != 0) {

The PAP handler never received the same treatment.


4. REACHABILITY
===============

Both bugs are reachable via the PPPoE data path:

  pppoe_data_input -> pppoeintr -> sppp_input -> sppp_pap_input

Precondition: the target system must be configured as a PAP
authenticator (e.g. ifconfig pppoe0 peerproto pap peername <x>
peerkey <y>). The attacker does not need to know any credentials.


5. IMPACT
=========

An attacker on the same network segment can authenticate to a PPPoE
interface without credentials, establishing a full network-layer
link (LCP -> PAP -> IPCP -> IP).

When OpenBSD acts as a PPPoE client with mutual authentication, a
rogue server in the same broadcast domain can exploit the bypass to
impersonate a legitimate server, causing OpenBSD to route traffic
through the attacker's endpoint.


6. PROOF OF CONCEPT
===================

A Python PoC acts as a PPPoE server, completes discovery and
LCP negotiation, then sends a PAP Auth-Request with name_len=0 and
passwd_len=0.

Result:

  PAP_ACK received with empty credentials
  VM accepted name_len=0, passwd_len=0 as valid auth.

  IPCP Config-Ack received - link is UP
  ICMP echo reply from 10.0.0.1

  FULL LINK ESTABLISHED

PoC and full technical report:
  https://blog.argus-systems.ai/blog/openbsd-pap-27-year-auth-bypass.html


7. FIX
======

Fixed in -current by mvs on 2026-06-14. The fix mirrors the CHAP
handler's exact-length pre-check:

  if (name_len != strlen(sp->hisauth.name) ||
      passwd_len != strlen(sp->hisauth.secret) ||
      bcmp(name, sp->hisauth.name, name_len) != 0 ||
      bcmp(passwd, sp->hisauth.secret, passwd_len) != 0) {

Fix commit:
https://github.com/openbsd/src/commit/076e2b1c1fc4ac0883a72d3544131ad5cee7adf8


8. TIMELINE
===========

  2026-06-12  Reported to security () openbsd org with PoC
  2026-06-14  Fix committed to -current


9. CREDIT
=========

Discovered and reported by Argus (https://byteray.co.uk/).


10. REFERENCES
==============

Advisory:
  https://pop.argus-systems.ai/advisory/adv-038.html

Blog post:
https://blog.argus-systems.ai/blog/openbsd-pap-27-year-auth-bypass.html

Proof of concept:
  https://pop.argus-systems.ai/attachments/poc-001-pap-bypass.py

_______________________________________________
Sent through the Full Disclosure mailing list
https://nmap.org/mailman/listinfo/fulldisclosure
Web Archives & RSS: https://seclists.org/fulldisclosure/

References

Raw JSON
{
  "containers": {
    "cna": {
      "affected": [
        {
          "product": "sppp_pap_input PAP",
          "vendor": "Openbsd",
          "versions": [
            {
              "status": "affected",
              "version": "unknown"
            }
          ]
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "finder",
          "value": "shj"
        }
      ],
      "descriptions": [
        {
          "lang": "en",
          "value": "------------------------------------------------------------------------\nOpenBSD sppp_pap_input: PAP Authentication Bypass via Zero-Length bcmp\n------------------------------------------------------------------------\n\nAffected:  OpenBSD all versions through 7.6 (fixed in -current)\nVendor:    OpenBSD\nSeverity:  High\nReporter:  Argus\nDate:      2026-06-16\n\n\n1. SUMMARY\n==========\n\nThe sppp_pap_input() function in sys/net/if_spppsubr.c uses the\nattacker-controlled name_len and passwd_len fields from the incoming\nPAP frame directly as the comparison length for bcmp() against\nconfigured credentials.\n\nWhen both fields are set to zero, bcmp() returns 0 unconditionally\n(bcmp with length 0 always succeeds). The existing upper-bound guard\n(> AUTHMAXLEN) allows zero through. As a result, a PAP Auth-Request\nwith name_len=0 and passwd_len=0 passes credential validation and\ntriggers a PAP_ACK, authenticating the peer without any knowledge of\nthe configured username or password.\n\nA secondary kernel heap over-read exists via the same root cause:\nsupplying a name_len larger than the allocation of the stored\ncredential causes bcmp to read past the heap object.\n\n\n2. AFFECTED VERSIONS\n====================\n\nThe bcmp comparison pattern was introduced with the original sppp\ncode import on 1999-07-01 (commit bda3414e, \"lmc driver; ported by\nchris () dqc org\"). The zero-length bypass has been exploitable since\nthat date.\n\nIn February 2009 (commit 9c2f3d605fc), auth credential fields were\nchanged from fixed-size struct arrays to dynamically allocated\nmalloc(strlen()+1), and the bounds check was changed to\n> AUTHMAXLEN (256). This decoupled the allocation size from the\ncomparison bound, enabling the heap over-read.\n\nConfirmed against OpenBSD 7.6 (amd64) in QEMU/KVM.\n\n\n3. DETAILS\n==========\n\nVulnerable code (sys/net/if_spppsubr.c, sppp_pap_input):\n\n  if (name_len > AUTHMAXLEN ||\n      passwd_len > AUTHMAXLEN ||\n      bcmp(name, sp->hisauth.name, name_len) != 0 ||\n      bcmp(passwd, sp->hisauth.secret, passwd_len) != 0) {\n          /* authentication failed */\n\nname_len and passwd_len are parsed directly from the PAP frame\npayload. bcmp(a, b, 0) always returns 0. The > AUTHMAXLEN guard\nrejects values above 255 but permits zero.\n\nThe CHAP handler in the same file already had the correct pattern\nwith an exact-length pre-check:\n\n  if (name_len != strlen(sp->hisauth.name)\n      || bcmp(name, sp->hisauth.name, name_len) != 0) {\n\nThe PAP handler never received the same treatment.\n\n\n4. REACHABILITY\n===============\n\nBoth bugs are reachable via the PPPoE data path:\n\n  pppoe_data_input -> pppoeintr -> sppp_input -> sppp_pap_input\n\nPrecondition: the target system must be configured as a PAP\nauthenticator (e.g. ifconfig pppoe0 peerproto pap peername <x>\npeerkey <y>). The attacker does not need to know any credentials.\n\n\n5. IMPACT\n=========\n\nAn attacker on the same network segment can authenticate to a PPPoE\ninterface without credentials, establishing a full network-layer\nlink (LCP -> PAP -> IPCP -> IP).\n\nWhen OpenBSD acts as a PPPoE client with mutual authentication, a\nrogue server in the same broadcast domain can exploit the bypass to\nimpersonate a legitimate server, causing OpenBSD to route traffic\nthrough the attacker's endpoint.\n\n\n6. PROOF OF CONCEPT\n===================\n\nA Python PoC acts as a PPPoE server, completes discovery and\nLCP negotiation, then sends a PAP Auth-Request with name_len=0 and\npasswd_len=0.\n\nResult:\n\n  PAP_ACK received with empty credentials\n  VM accepted name_len=0, passwd_len=0 as valid auth.\n\n  IPCP Config-Ack received - link is UP\n  ICMP echo reply from 10.0.0.1\n\n  FULL LINK ESTABLISHED\n\nPoC and full technical report:\n  https://blog.argus-systems.ai/blog/openbsd-pap-27-year-auth-bypass.html\n\n\n7. FIX\n======\n\nFixed in -current by mvs on 2026-06-14. The fix mirrors the CHAP\nhandler's exact-length pre-check:\n\n  if (name_len != strlen(sp->hisauth.name) ||\n      passwd_len != strlen(sp->hisauth.secret) ||\n      bcmp(name, sp->hisauth.name, name_len) != 0 ||\n      bcmp(passwd, sp->hisauth.secret, passwd_len) != 0) {\n\nFix commit:\nhttps://github.com/openbsd/src/commit/076e2b1c1fc4ac0883a72d3544131ad5cee7adf8\n\n\n8. TIMELINE\n===========\n\n  2026-06-12  Reported to security () openbsd org with PoC\n  2026-06-14  Fix committed to -current\n\n\n9. CREDIT\n=========\n\nDiscovered and reported by Argus (https://byteray.co.uk/).\n\n\n10. REFERENCES\n==============\n\nAdvisory:\n  https://pop.argus-systems.ai/advisory/adv-038.html\n\nBlog post:\nhttps://blog.argus-systems.ai/blog/openbsd-pap-27-year-auth-bypass.html\n\nProof of concept:\n  https://pop.argus-systems.ai/attachments/poc-001-pap-bypass.py\n\n_______________________________________________\nSent through the Full Disclosure mailing list\nhttps://nmap.org/mailman/listinfo/fulldisclosure\nWeb Archives & RSS: https://seclists.org/fulldisclosure/"
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-09-11T11:31:48Z",
        "orgId": "4e2abfbf-4a2a-4b76-a4e0-d77c18ba156c",
        "shortName": "VULNARCHIVE"
      },
      "references": [
        {
          "tags": [
            "technical-description",
            "exploit"
          ],
          "url": "https://vuln.freearchive.org/archive/full-disclosure/2026/Jun/15"
        },
        {
          "tags": [
            "technical-description"
          ],
          "url": "https://seclists.org/fulldisclosure/2026/Jun/15"
        },
        {
          "url": "https://blog.argus-systems.ai/blog/openbsd-pap-27-year-auth-bypass.html"
        },
        {
          "url": "https://byteray.co.uk/"
        },
        {
          "url": "https://github.com/openbsd/src/commit/076e2b1c1fc4ac0883a72d3544131ad5cee7adf8"
        },
        {
          "url": "https://nmap.org/mailman/listinfo/fulldisclosure"
        },
        {
          "url": "https://pop.argus-systems.ai/advisory/adv-038.html"
        },
        {
          "url": "https://pop.argus-systems.ai/attachments/poc-001-pap-bypass.py"
        },
        {
          "url": "https://seclists.org/fulldisclosure/"
        }
      ],
      "source": {
        "defect": [
          "https://seclists.org/fulldisclosure/2026/Jun/15"
        ],
        "discovery": "EXTERNAL"
      },
      "title": "OpenBSD sppp_pap_input: PAP authentication bypass",
      "x_gcve": [
        {
          "recordType": "advisory",
          "relationships": [],
          "vulnId": "GCVE-1988-2026-0362",
          "x_vulnarchive": {
            "archiveUrl": "https://vuln.freearchive.org/archive/full-disclosure/2026/Jun/15",
            "automated": true,
            "contentSha256": "e9970fb07c7e498304d6f1ffb1e4aa8eb8fc2d3e988893327f0a72abe1bfbdc1",
            "evidenceScore": 9,
            "messageId": "",
            "originalUrl": "https://seclists.org/fulldisclosure/2026/Jun/15",
            "policy": "vulnarchive-1",
            "sourceFormat": "text/html",
            "sourcePublishedAt": "2026-06-16T19:27:44Z"
          }
        }
      ]
    }
  },
  "cveMetadata": {
    "assignerOrgId": "4e2abfbf-4a2a-4b76-a4e0-d77c18ba156c",
    "assignerShortName": "VULNARCHIVE",
    "datePublished": "2026-09-11T07:55:57Z",
    "dateUpdated": "2026-09-11T11:31:48Z",
    "state": "PUBLISHED",
    "vulnId": "GCVE-1988-2026-0362"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}