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/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"
}