Soft Serve, Hard Fail: The Context Pollution Authentication Bypass
Jan 21, 2026·6 min read·0 visits
Executive Summary (TL;DR)
Soft Serve versions prior to v0.11.3 contain a CVSS 10.0 critical vulnerability. The application eagerly resolves and stores user identities during the SSH public key 'query' phase (before a cryptographic signature is verified). By offering an admin's public key and then switching to their own valid key, an attacker can trick the server into treating the session as an administrator session. Update to v0.11.3 immediately.
A critical state management logic error in Soft Serve's SSH handling allows unauthenticated attackers to impersonate administrators simply by knowing the administrator's public key. By exploiting the SSH protocol's 'public key offering' phase, an attacker can pollute the session context with a privileged identity before authenticating with their own low-privileged key.
The Hook: TUI Dreams and SSH Nightmares
Soft Serve is a self-hostable Git server for the command-line commandos among us. It's written in Go, uses the Charmbracelet stack, and creates a beautiful TUI (Terminal User Interface) accessible purely over SSH. No web interface, no clutter, just pure terminal bliss. It relies heavily on the gliderlabs/ssh library to handle the heavy lifting of the SSH protocol.
But here is the thing about writing custom SSH servers: the protocol is a stateful beast. Unlike HTTP, which is largely stateless (cookies notwithstanding), an SSH handshake is a conversation. 'Hello.' 'Hi.' 'Here is my key.' 'Prove it.' 'Okay, I proved it.'
This vulnerability, tracked as GHSA-pchf-49fh-w34r, is a classic example of what happens when you trust the conversation before the signature is dry. It turns out that Soft Serve was a little too eager to identify who was knocking at the door, essentially letting attackers walk in wearing a mask of their choosing.
The Flaw: Identity Confusion in the Handshake
To understand this bug, you have to understand a quirk of the SSH protocol (RFC 4252). When a client wants to authenticate with a public key, it doesn't just sign data immediately. That is computationally expensive. Instead, it sends a 'query' first: "Hey server, would you accept this public key if I could prove I own it?"
The server looks at the public key blob and says, "Yes, I know that key," or "No, go away." Only if the server says "Yes" does the client proceed to generate a cryptographic signature.
Soft Serve implemented a PublicKeyHandler to process these queries. In vulnerable versions, when the server received that initial "Do you know this key?" query, it immediately looked up the user associated with that key in its database. That part is fine. The catastrophe happened next: it stored that User object in the shared SSH session context.
This is a critical logic error. The server essentially said, "Oh, you are offering the Admin's key? I'll just write down 'User = Admin' in my notebook right now." It did this before verifying the signature. The session context in the Go SSH library persists for the duration of the connection attempt. This persistence creates a 'State Pollution' vulnerability.
The Code: The Smoking Gun
Let's look at the logic flaw. The developers were using the gliderlabs/ssh context to store the user identity too early. Here represents the logic flow in the vulnerable PublicKeyHandler:
// VULNERABLE LOGIC (Simplified)
func PublicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool {
// 1. Check if the DB knows this key
user, err := db.FindUserByKey(key)
if err != nil {
return false
}
// 2. FATAL ERROR: Storing the user in the context BEFORE signature verification
// The context (ctx) is shared across the entire auth session.
ctx.SetValue("user", user)
return true // We accept the key candidate
}The fix, applied in commit 8539f9ad39918b67d612a35785a2b4326efc8741, moves this logic entirely. Instead of trusting the PublicKeyHandler (which fires during the offer phase), they moved the identity resolution to the AuthenticationMiddleware or post-auth logic, which looks at s.PublicKey()—the key that actually signed the request.
Here is the corrected flow in the patch:
// PATCHED LOGIC
// In middleware.go
pk := s.PublicKey() // Get the key that PASSED auth
if pk != nil {
// Resolve user based on the PROVEN key
user, _ := be.UserByPublicKey(ctx, pk)
ctx.SetValue(proto.ContextKeyUser, user)
}This ensures that ctx.SetValue is only called with the identity of the user who actually possesses the private key.
The Exploit: "I'm the Admin (Just Kidding, I'm Dave)"
This is where the fun begins. We can exploit this state pollution to bypass authentication entirely. We need two things: the public key of a victim (say, the Admin) and a valid key of our own (the Attacker).
- Step 1: The attacker configures their SSH client to offer the Admin's Public Key first.
- Step 2: Soft Serve sees the Admin key, looks up the Admin user, and saves
User=Admininto the session context. - Step 3: The attacker fails to provide a signature for the Admin key (because they don't have the private key). The SSH protocol says, "Okay, try another method."
- Step 4: The attacker now offers Their Own Public Key and provides a valid signature.
- Step 5: Soft Serve validates the signature. The authentication passes.
Here is the kicker: When the application starts the shell, it asks the context: "Who is logged in?". Because the context was polluted in Step 2 and never cleared, it replies: "The Admin is logged in."
Here is a conceptual Go PoC using golang.org/x/crypto/ssh:
// Malicious Client Logic
authMethods := []ssh.AuthMethod{
// 1. Offer Victim's Key (Public only, dummy signer)
ssh.PublicKeys(victimPublicSigner),
// 2. Offer Attacker's Key (Valid)
ssh.PublicKeys(attackerRealSigner),
}
// When Dialing, the library will try the Victim key first.
// The server marks the session as "Victim".
// The signature fails.
// The library tries the Attacker key.
// The signature passes.
// We are now in a shell as the Victim.
ssh.Dial("tcp", target, &ssh.ClientConfig{
User: "git",
Auth: authMethods,
})The Impact: Total Takeover
This is a CVSS 10.0 for a reason. Soft Serve isn't just a Git viewer; it's a management system. Administrative access allows you to create repositories, delete repositories, add new users, and manage permissions.
In a supply chain context, this is devastating. An attacker could impersonate the maintainer of a critical internal library, push malicious code to the repository, and wait for the CI/CD pipeline to pick it up. All of this can be done without ever touching the disk or needing a valid account on the system (if registration is open or if they have a low-level account).
The only prerequisite is knowing the administrator's public key. Since developers famously put their public keys on GitHub (github.com/username.keys) for convenience, this is public information. If you run Soft Serve, your public key is likely your vulnerability.
The Fix: Verification Over Assumption
The remediation is straightforward: Update to Soft Serve v0.11.3.
The patch introduces strict re-verification. It ensures that the fingerprint of the key being used for the session matches the fingerprint of the key that was successfully authorized. It explicitly decouples the 'offering' phase from the 'identity establishment' phase.
If you cannot patch immediately, you must restrict network access to your Soft Serve instance to trusted IPs only, though this defeats the purpose of a distributed Git server. There are no configuration changes that can mitigate this; the flaw is in the compiled binary's logic.
Official Patches
Fix Analysis (1)
Technical Appendix
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:HAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
Soft Serve Charmbracelet | < 0.11.3 | 0.11.3 |
| Attribute | Detail |
|---|---|
| Attack Vector | Network (SSH) |
| CVSS | 10.0 (Critical) |
| CWE | CWE-287 & CWE-840 |
| Prerequisites | Target Public Key (Admin) |
| Exploit Status | Functional PoC Available |
| Root Cause | Session State Pollution |
MITRE ATT&CK Mapping
Improper Authentication and Business Logic Error leading to Identity Confusion.
Known Exploits & Detection
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.