Saturday, 14 November 2026
0 agent hacks today▼ 4 vs yesterday (4)

Race condition lets russh SSH agent sign after being locked

An advisory published on 13 November 2026 says russh 0.65.0 can complete a signature request after another connection locks the agent.

By The Agentic Times ·

A locked vault door swings open just as a second hand reaches through, completing a signature stamp before security mechanisms fully engage.
A locked vault door swings open just as a second hand reaches through, completing a signature stamp before security mechanisms fully engage. · Illustration: The Agentic Times

A flaw in the Rust SSH library russh allows a private key to be used for signing after the SSH agent has been locked, according to a GitHub security advisory published on 13 November 2026. The advisory, tracked as GHSA-736f-c93q-9fwx and CVE-2026-128088, affects released version 0.65.0 of russh and the current commit at the time of writing. An agent-forwarding peer, meaning a remote party that has been given access to a forwarded agent socket, can obtain a signature while the agent is locked and all new requests are being rejected.

The problem is a time-of-check to time-of-use gap created by an asynchronous pause. According to the advisory, the agent's respond() function reads the shared lock state once and then dispatches a sign request. Inside try_sign(), russh clones the private key out of the shared key store, drops the store lock, and then awaits an asynchronous confirmation callback. Confirmation is the interactive prompt used for keys added with the Confirm constraint, where a human or policy engine must approve each use.

While that confirmation is pending, a second connection to the same agent can lock it successfully. The lock and key stores are shared by all connections, so the state change takes effect immediately for anything new. But the suspended request still holds its own reference-counted copy of the private key. When the confirmation callback eventually returns true, the original connection signs with the captured key. The advisory states plainly that no lock or key-state revalidation occurs after the await.

The advisory cites RFC 9987 section 5.7, which requires a locked agent to suspend private-key signature operations until it is unlocked. It describes the expected behaviour as no signature operation completing after the agent reports a successful lock. Only the request that was already suspended across the await bypasses the lock; fresh requests made in the same locked state are correctly rejected.

There is an awkward lineage here. The advisory says the vulnerable asynchronous call was introduced by commit 62db6e7, which was itself the fix for an earlier advisory, GHSA-vp5q-ff62-jwqv. In other words, making confirmation asynchronous closed one hole and opened this one.

The report includes a self-contained proof of concept that does not need an SSH server, a Unix socket or any attached binary. It uses in-memory duplex streams and a custom agent implementation whose confirm() method blocks on a notification until the test releases it. The sequence is straightforward: an administrator connection adds an Ed25519 key with the Confirm constraint, an attacker connection starts a signature request, the test waits until confirmation has been entered, and the administrator then locks the agent with a password. The proof of concept prints that the agent was locked while confirmation was pending, and includes a control request started after the lock to show that new requests do fail. That control is what demonstrates the shared state transition really took effect.

The advisory lists the affected code in russh/src/keys/agent/server.rs. At the time of the advisory no fixed version is identified in the report. Anyone forwarding a russh-based agent to a remote host should treat the lock command as unreliable against requests already in flight.

Sources

  1. github.comhttps://github.com/advisories/GHSA-736f-c93q-9fwx