Feb 15, 2026·7 min read·32 visits
Soft Serve trusted the identity of an 'offered' public key before verifying the signature. An attacker can offer an Admin key (fail signature) then offer their own key (pass signature), but the server retains the Admin identity in the session context. Result: Full Account Takeover.
A critical authentication bypass in Charmbracelet's Soft Serve allows attackers to impersonate administrators by exploiting a logic flaw in the SSH handshake state management. By offering a victim's key before authenticating with their own, an attacker can trick the server into granting the victim's privileges.
Soft Serve is a beautiful piece of software. It’s a self-hostable Git server for the command line, built by Charmbracelet, a team known for making the terminal look better than most GUIs. It uses SSH for everything—authentication, repository access, and even the UI itself (via TUI). It's slick, modern, and written in Go.
But here's the thing about modern SSH servers: managing state is a nightmare. Unlike HTTP, which is stateless (mostly), SSH is a persistent tunnel with a complex handshake dance. If you trip over your own feet during that dance, you faceplant hard. And that is exactly what happened here.
CVE-2026-24058 isn't a buffer overflow. We aren't smashing the stack or spraying the heap. We are gaslighting the server. We are performing a Jedi Mind Trick. We tell the server, "I am the Admin," and even though we can't prove it, the server nods and writes it down. Later, when we actually prove we are "Nobody," the server looks at its notes, sees "Admin," and hands over the keys to the kingdom. It is a classic State Pollution vulnerability, and it is devastatingly simple.
To understand this bug, you need to understand a nuance of the SSH protocol: Public Key Authentication is a two-step process.
authorized_keys list. If yes, it says "Sure, prove you own it."Most SSH clients try multiple keys. They might offer Key A, realize they don't have the passphrase, and then offer Key B.
The vulnerability in Soft Serve (specifically in how it used the wish middleware) was a failure to clean up the workspace. When the client performed Step 1 (The Offer) with a victim's key, the server's PublicKeyHandler immediately resolved that key to a User ID and stuffed it into the session context (proto.ContextKeyUser).
> [!WARNING] > The server committed the sin of premature optimization: it assumed that if you offer a key, you will eventually sign with it.
If the signature verification failed (because you don't actually have the Admin's private key), the SSH protocol correctly rejected that specific authentication attempt. However, the Session Context was already polluted. The variable proto.ContextKeyUser still held the Admin's user object. When the attacker subsequently authenticated with their own valid, low-privilege key, the server verified the crypto correctly but forgot to clear the stale "Admin" user from the context. The code proceeded to authorize the session based on the stale data, not the key that actually passed verification.
Let's look at the logic flow that caused this. The vulnerability lived in the intersection of the middleware chain and the context handling.
In the vulnerable version, the PublicKeyHandler looked something like this (simplified for clarity):
// VULNERABLE LOGIC
func PublicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool {
// 1. Resolve user from the offered key
user, err := backend.UserByPublicKey(key)
if err == nil {
// CRITICAL FLAW: Setting the user in the context during the handshake
// simply because the key exists in the database.
ctx.SetValue(proto.ContextKeyUser, user)
}
return true // Allow the key to proceed to signature check
}The fix involves a philosophical shift: Don't trust the context during the handshake. The patch (Commit 8539f9a) moves the identity resolution to after the connection is fully established, using the key that actually signed the request.
Here is a snippet of the regression test added in the patch, which perfectly illustrates the attack flow:
// From pkg/ssh/middleware_test.go (The Fix)
// Step 1: Attacker offers Admin Key
// The mock context gets polluted
mockCtx.SetValue(proto.ContextKeyUser, adminUser)
// Step 2: Signature Fails
// ... SSH protocol loop continues ...
// Step 3: Attacker offers Own Key (Valid)
// The middleware now MUST ignore the context set in Step 1
// THE FIX:
// Instead of reading ctx.Value(proto.ContextKeyUser),
// we look up the user based on the FINALLY VALIDATED key:
authenticatedUser, err := be.UserByPublicKey(mockCtx, s.PublicKey())The developers also added a fingerprint check (pubkey-fp) to ensure the key used for authorization matches the key used for authentication. If they don't match, it's a dead giveaway of this exact bypass attempt.
Exploiting this requires a custom SSH client or a script, as standard clients like OpenSSH might not give you the granular control needed to force the "Offer-Fail-Offer-Pass" sequence in the exact order without disconnecting. However, writing a script using Python's paramiko or Go's golang.org/x/crypto/ssh is trivial.
The Attack Chain:
.keys (e.g., https://github.com/torvalds.keys). Now you have the Admin's public key.SSH_MSG_USERAUTH_REQUEST offering the Admin's public key. The server sees the key, finds the Admin user in its DB, and sets Context.User = Admin.SSH_MSG_USERAUTH_REQUEST with your own public key (which you previously registered as a normal user). You provide a valid signature. The server verifies it.The Payoff: The server logic runs: "Okay, crypto checks out. Who is this session for? Oh, I see Context.User is set to Admin. Welcome back, boss!"
You now have full administrative access to the Git server. You can push malicious commits to protected branches, delete repositories, or modify server configurations if the TUI allows it.
This is a Critical severity issue (CVSS 8.1). In a Git server context, identity is everything.
The vulnerability is particularly dangerous because it leaves very few logs. The SSH logs will show a failed auth attempt for the Admin (which looks like a typo) followed by a successful auth for the User. Unless you are correlating the internal session state with the auth logs, it looks like standard behavior.
The patch is straightforward but essential. You must upgrade Soft Serve to v0.11.3 or later immediately.
go install github.com/charmbracelet/soft-serve/cmd/soft@v0.11.3For Go developers using charmbracelet/ssh or gliderlabs/ssh: Never store authorization state during the authentication handshake. The handshake is a volatile, untrusted zone. Only establish identity once the cryptographic handshake is fully complete and signed.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:U| Product | Affected Versions | Fixed Version |
|---|---|---|
Soft Serve Charmbracelet | <= 0.11.2 | 0.11.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-289 |
| Attack Vector | Network (SSH) |
| CVSS Score | 8.1 (Critical) |
| Impact | Authentication Bypass / Privilege Escalation |
| Exploit Status | PoC Available (Regression Test) |
| Affected Component | PublicKeyHandler / Wish Middleware |
Authentication Bypass by Alternate Name
An improper authentication vulnerability (CWE-287) exists in the legacy, deprecated Internet Key Exchange version 1 (IKEv1) key exchange protocol implementation in Check Point Security Gateways. The vulnerability is caused by a logic flow weakness during the certificate validation process for Remote Access VPN and Mobile Access (SSL VPN) connections. An unauthenticated remote attacker can exploit this weakness to bypass user authentication entirely, establishing a fully functional Remote Access VPN connection without a valid password.
GeoNode versions prior to 4.4.5 and 5.0.2 are vulnerable to Server-Side Request Forgery (SSRF) in the service registration endpoint. Authenticated attackers with low privileges can exploit insufficient input validation in the Web Map Service (WMS) registration module to force the application server to make outbound network queries to loopback addresses, private RFC1918 subnets, link-local scopes, and cloud metadata endpoints. This technical report details the mechanics of the vulnerability, the underlying architectural flaw, and how to effectively remediate and mitigate the associated security risks.
CVE-2022-0492 is a high-severity missing authorization vulnerability in the Linux kernel's Control Groups (cgroups) v1 implementation. The flaw resides within the cgroup_release_agent_write function in kernel/cgroup/cgroup-v1.c, where the kernel fails to validate if the process writing to the release_agent file possesses administrative capabilities in the initial user namespace. This allows a local attacker inside a container with root privileges (UID 0) to abuse user namespaces, mount a cgroups v1 directory, modify the release_agent parameter, and execute arbitrary commands on the host system as host root, effectively achieving a complete container escape.
NocoDB is subject to an insufficient session expiration vulnerability where OAuth access and refresh tokens are not invalidated or revoked during security-sensitive actions such as password changes, forgot-password requests, or password resets. This allows an attacker possessing an active OAuth token to maintain unauthorized persistence.
A vulnerability in the vantage6 federated learning framework allows unauthenticated remote attackers to gain administrative control of the server via hardcoded default credentials (root/root) when deployed under default configurations in versions 4.2.3 and below.
An improper access control vulnerability in the vantage6 node component allows concurrently running algorithm containers to read and modify sensitive input and output files of other tasks. The lack of strict workspace directory isolation exposes a significant attack surface in multi-tenant or federated environments where untrusted algorithms are executed.