惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

云风的 BLOG
云风的 BLOG
P
Privacy International News Feed
Vercel News
Vercel News
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园 - 叶小钗
F
Fortinet All Blogs
Security Archives - TechRepublic
Security Archives - TechRepublic
L
LINUX DO - 最新话题
AWS News Blog
AWS News Blog
Engineering at Meta
Engineering at Meta
Attack and Defense Labs
Attack and Defense Labs
Recent Announcements
Recent Announcements
Recent Commits to openclaw:main
Recent Commits to openclaw:main
PCI Perspectives
PCI Perspectives
Cloudbric
Cloudbric
AI
AI
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
IT之家
IT之家
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
J
Java Code Geeks
M
MIT News - Artificial intelligence
Cisco Talos Blog
Cisco Talos Blog
V2EX - 技术
V2EX - 技术
Webroot Blog
Webroot Blog
Microsoft Security Blog
Microsoft Security Blog
Cyberwarzone
Cyberwarzone
博客园 - 聂微东
G
Google Developers Blog
W
WeLiveSecurity
罗磊的独立博客
P
Privacy & Cybersecurity Law Blog
阮一峰的网络日志
阮一峰的网络日志
A
About on SuperTechFans
WordPress大学
WordPress大学
The GitHub Blog
The GitHub Blog
T
Tailwind CSS Blog
V
Visual Studio Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
S
Secure Thoughts
Apple Machine Learning Research
Apple Machine Learning Research
Hugging Face - Blog
Hugging Face - Blog
Google DeepMind News
Google DeepMind News
Google DeepMind News
Google DeepMind News
雷峰网
雷峰网
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
F
Full Disclosure
Blog — PlanetScale
Blog — PlanetScale
The Last Watchdog
The Last Watchdog
P
Proofpoint News Feed

Lobsters

CIFSwitch: a non-universal Linux local root vulnerability RIPE NCC session fixation: poaching logins with an Atlas probe GNOME 2.20 but its Web Components Agentic Search for Context Engineering – Leonie Monigatti Garnix is shutting down [not OC] akashina.tngl.sh/jjc Concerning Emacs (and Jazz) Nitpicking the shell history scene in ‘Tron: Legacy’ What's cooking on SourceHut? Q2 2026 The tenth OpenPGP email summit Package managers that package package managers Clojure on Fennel part three: parsing WordPress at 23 Finding Miscompiles for Fun, Not Profit GitHub - creusot-rs/creusot: Creusot helps you prove your Rust code is correct. Announcing Rust 1.96.0 | Rust Blog A Love Letter to Neovim sqlite AGENTS.md Am I a Bad Friend? CSS vs. JavaScript • Josh W. Comeau Erlang Ecosystem Foundation - Supporting the BEAM community A brief note about slot access cost in Common Lisp Keyboard latency probe Rethinking the GNOME clipboard issues Back to the Building Blocks’ Building Blocks Tech Notes: Theseus: translating win32 to wasm Fast is better than slow Content-addressed Rust builds (or, what kache actually caches) Intent to Prototype: Embedding API Canada’s Bill C-22 and the security cost of collecting more data 5 PostgreSQL locking behaviors that trip people up okmij.org Stop advertising in your commits! | AksDev GitHub - mplsllc/macsurf: A modern web browser for Classic Mac OS 9 PowerPC. Real CSS3, ES5 JavaScript, native HTTPS — built with CodeWarrior on the Carbon API. Introducing DoomBench - Can Your Data Stack Run DOOM? What are some of your favourite developer tools? Building a Scalable Ingestion Pipeline with Temporal (Part 1) Converting shallow Git bundles into normal repositories Are you a member of any professional associations? What is a harmonic? An interactive comic about additive synthesis How Virtual Tables Work in the Itanium C++ ABI Using SwiftUI to Build a Mac-assed App in 2026 Rust (and Slint) on a jailbroken Kindle. ~jack/lambda-on-lambda - Serverless Haskell on AWS - sourcehut git Human proof for FOSS contributions Extremely simple internet radio controlled via IRC Announcing BABLR Splitting Konsole views from Helix to run tools | AksDev GitHub - yugr/rust-slides Serving files over HTTP three ways: synchronous, epoll, and io_uring update docs with information about building with build.py (#979) · astral-sh/python-build-standalone@c9c40c5 A Simple Makefile Tutorial On C extensions, portability, and alternative compilers Switching to Colemak | Pedro Alves Just How Bad Was The Intel IAPX432? Nix's Substituter List Is Not a Routing Table Accelerating copy_if using SIMD Lambda on Lambda: Serverless Haskell on AWS | Blog Announcing feed-repeat v1.0 Scaling Akvorado BMP RIB with sharding EYG news: A host of CLI improvements, new guides and new effects The social contract of writing JS Crossword C array types are weird; and related topics Flatpak will depend on systemd – OSnews Migrating from Go to Rust | corrode Rust Consulting A portentous reunion Vivado Licensing Options How my minimal, memory-safe Go rsync steers clear of vulnerabilities the entropy layer of a wavelet codec, on its own GitHub - nferhat/fht-compositor: A dynamic tiling Wayland compositor. Debian SE Linux and PinTheft Does bulk memmove speed up std::remove_if? (No.) 声明式部分更新 | Blog | Chrome for Developers Fully in-browser container builds Dianne Skoll's Web Site - Remind The Architecture of Open Source Applications (Volume 1)Berkeley DB Pardon MIE? - ironPeak Blog “Long-Term Support” doesn’t mean what you think Jira IS Turing-Complete May I recommend thinking of Emacs as your Fortress of Solitude hershey Floodgap Gopher-HTTP gateway gopher://thelambdalab.xyz/1cuneiforth/ HP QuickWeb, Singular And Pointless That one time I used Go panics for flow control A new suite of modern tools coming for editing and publishing RFCs From the Tabletop… The Digital Antiquarian Building a Host-Tuned GCC to Make GCC Compile Faster Are we self-sovereign PKI yet? Claw Patrol: an open-source security firewall for agents | Deno Revised^7 Report on Scheme, Large: Procedural Fascicle Draft is now public A Network Allow-List Won't Stop Exfiltration — André Graf From AFSK to Goertzel – µArt.cz Software For My New Home Server Introducing Neptune: Direct3D virtualization for QEMU AI Agent Bankrupted Their Operator While Trying to Scan DN42 - Lan Tian @ Blog mimalloc: A new, high-performance, scalable memory allocator for the modern era Making wl_shm fast The Soul of Maintaining a New Machine - Third Draft | Books in Progress What is Git made of?
A 27-Year-Old Authentication Bypass in OpenBSD's PPP Stack · Argus Blog
blog.argus-s · 2026-06-17 · via Lobsters

OpenBSD's sppp(4) subsystem handles synchronous PPP links, the backbone of PPPoE connectivity. When a peer connects, the PPP handshake can require PAP (Password Authentication Protocol) credentials before the link reaches STATE_OPENED. The check that decides whether to accept or reject those credentials has been broken since it was first imported into the OpenBSD source tree in July 1999.

This is a story about a one-line bug that lived for 27 years.

The bug

The PAP credential check lives in sppp_pap_input() in sys/net/if_spppsubr.c. When the OpenBSD system acts as a PAP authenticator, it compares the peer-supplied name and password against configured values using bcmp:

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 */

The problem is that name_len and passwd_len come directly from the incoming PAP frame. They are attacker-controlled. And bcmp(buf, ref, 0) always returns 0, regardless of what buf and ref contain.

The > AUTHMAXLEN guard is an upper-bound check. It rejects values above 255 but happily allows zero through. So when an attacker sends a PAP Auth-Request with name_len=0 and passwd_len=0, both bcmp calls return 0, the fail-branch is never taken, and OpenBSD sends a PAP_ACK. Authentication is complete. No credentials were needed.

A secondary issue shares the same root cause. Supplying a name_len larger than the actual allocation of sp->hisauth.name causes bcmp to read past the heap object, producing a kernel heap over-read. The configured credential is dynamically allocated as malloc(strlen(configured_string) + 1), so an 8-byte credential paired with name_len=200 reads 192 bytes of adjacent kernel heap.

27 years of history

The bcmp comparison pattern was part of the original sppp code imported on July 1, 1999 in a commit described as "lmc driver; ported by [email protected]". The code originated from FreeBSD, which itself derived it from Cronyx Engineering Ltd.'s implementation written by Serge Vakulenko in 1994-1996.

The zero-length bypass worked against every version since that original import. The guard at the time used > AUTHNAMELEN (64) and > AUTHKEYLEN (16), but zero is less than both, so it passed through unchecked.

In February 2009, a commit titled "Allow username and password to be up to 255 characters in length" changed the auth fields from fixed-size struct arrays (name[64], secret[16]) to dynamically allocated malloc(strlen()+1) and replaced the two separate bounds checks with a single > AUTHMAXLEN (256). This made the heap over-read possible, since the allocation size was now decoupled from the comparison bound.

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

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

The PAP handler never got the same treatment. For 27 years.

Reachability and impact

Both bugs are reachable via the PPPoE data path: pppoe_data_input -> pppoeintr -> sppp_input -> sppp_pap_input. The attacker does not need to know any credentials.

The attack completes the full PPPoE handshake: discovery, LCP negotiation, PAP authentication with zero-length fields, IPCP negotiation, and finally ICMP echo through the established link. The attacker's rogue PPPoE server carries the victim's IP traffic. A rogue server in the same broadcast domain exploits the bypass to impersonate a legitimate server, and OpenBSD routes traffic through the attacker's endpoint.

We verified this against OpenBSD 7.6 (amd64) in QEMU/KVM. The proof of concept acts as a PPPoE server, completes the handshake, and sends the empty PAP Auth-Request.

The fix

The fix mirrors the exact-length pre-check already used by the CHAP handler:

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) {

This simultaneously prevents the zero-length bypass and bounds the bcmp length to the exact stored credential size, eliminating the over-read. The fix was committed by mvs on June 14, 2026.

Proof of concept

The PoC script (poc-001-pap-bypass.py) acts as a PPPoE server. It completes the PPPoE discovery and LCP negotiation, then sends a PAP Auth-Request with name_len=0, passwd_len=0. OpenBSD responds with PAP_ACK, and the link reaches full network-layer operation:

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

  IPCP Config-Ack received — link is UP (us=10.0.0.2 peer=10.0.0.1)
  ICMP echo reply from 10.0.0.1

  FULL LINK ESTABLISHED

Timeline

  • 1999-07-01 — sppp code with vulnerable bcmp comparison imported into OpenBSD from FreeBSD
  • 2009-02-16 — Auth fields changed to dynamic allocation with AUTHMAXLEN, enabling heap over-read
  • 2026-06-12 — Reported to OpenBSD with proof of concept
  • 2026-06-14 — Fix committed by mvs (CVE-2026-55706)