CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-PCHF-49FH-W34R
8.10.17%

Soft Serve, Hard Landing: The SSH Identity Crisis (CVE-2026-24058)

Alon Barad
Alon Barad
Software Engineer

Feb 15, 2026·7 min read·16 visits

PoC Available

Executive Summary (TL;DR)

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.

The Hook: When Git Meets the Jedi Mind Trick

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.

The Flaw: The 'Offer' vs. 'Sign' Trap

To understand this bug, you need to understand a nuance of the SSH protocol: Public Key Authentication is a two-step process.

  1. The Offer (Query): The client says, "Hey, I have this public key (Key A). Do you accept it?" The server checks if Key A is in its authorized_keys list. If yes, it says "Sure, prove you own it."
  2. The Signature: The client then signs a blob of data with the private key corresponding to Key A. The server verifies the signature. If it matches, you are in.

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.

The Code: The Smoking Gun

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.

The Exploit: Identity Theft in 4 Steps

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:

  1. Recon: You find a Soft Serve instance. You look up the admin's GitHub profile (often public) and grab their .keys (e.g., https://github.com/torvalds.keys). Now you have the Admin's public key.
  2. The Bait: Connect to the target SSH port. Send an 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.
  3. The Switch: The server asks for a signature. You send a dummy/invalid signature (or just move to the next key). The server rejects the key but keeps the session open for the next method.
  4. The Entry: You now send an 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.

The Impact: Why You Should Panic

This is a Critical severity issue (CVSS 8.1). In a Git server context, identity is everything.

  • Data Integrity: An attacker can overwrite history, inject backdoors into codebases, or delete critical data.
  • Confidentiality: If the server hosts private repositories, the attacker can clone them all.
  • Supply Chain Risk: If you use Soft Serve to host code that builds into production artifacts, an attacker can inject malicious code into your build pipeline without triggering standard "unauthorized push" alerts, because the push is authorized—as the Admin.

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 Fix: Remediation & Mitigation

The patch is straightforward but essential. You must upgrade Soft Serve to v0.11.3 or later immediately.

Remediation Steps

  1. Upgrade: Pull the latest docker image or update the binary.
    go install github.com/charmbracelet/soft-serve/cmd/soft@v0.11.3
  2. Audit: Check your Git logs. Look for commits made by administrators at unusual times or from IP addresses usually associated with lower-privilege users.
  3. Rotate Keys: If you suspect a breach, rotating keys is good practice, though this specific exploit doesn't steal private keys—it just impersonates the owner.

Developer Takeaway

For 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.

Official Patches

CharmbraceletCommit fixing the authentication middleware logic
CharmbraceletOfficial Security Advisory

Fix Analysis (1)

Technical Appendix

CVSS Score
8.1/ 10
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
EPSS Probability
0.17%
Top 62% most exploited

Affected Systems

Soft Serve <= 0.11.2

Affected Versions Detail

Product
Affected Versions
Fixed Version
Soft Serve
Charmbracelet
<= 0.11.20.11.3
AttributeDetail
CWE IDCWE-289
Attack VectorNetwork (SSH)
CVSS Score8.1 (Critical)
ImpactAuthentication Bypass / Privilege Escalation
Exploit StatusPoC Available (Regression Test)
Affected ComponentPublicKeyHandler / Wish Middleware

MITRE ATT&CK Mapping

T1078Valid Accounts
Defense Evasion
T1556Modify Authentication Process
Credential Access
CWE-289
Authentication Bypass by Alternate Name

Authentication Bypass by Alternate Name

Known Exploits & Detection

GitHub (Official Patch)Regression test case demonstrating the bypass via mock context

Vulnerability Timeline

Patch Committed
2026-01-21
Version 0.11.3 Released
2026-01-22
CVE-2026-24058 Published
2026-01-22

References & Sources

  • [1]GHSA-pchf-49fh-w34r Advisory
  • [2]NVD Entry for CVE-2026-24058
Related Vulnerabilities
CVE-2026-24058

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.