Skip to main content
8 min read

When the SSH Server Attacks the Client: libssh2 CVE-2026-55200

When the SSH Server Attacks the Client: libssh2 CVE-2026-55200

Almost everything you know about securing SSH is about the server. Disable password auth, rotate host keys, put sshd behind a bastion, rate-limit with fail2ban. The threat model is always the same: an attacker out on the internet trying to get in to a box you run.

CVE-2026-55200 turns that around. It is a critical, pre-authentication memory-corruption bug in libssh2, and the victim is the SSH client. The attacker is the server. If a piece of software using a vulnerable libssh2 connects out to a host an attacker controls, that host can corrupt the client's memory and run code inside it before any credentials are exchanged. None of your sshd hardening applies, because sshd was never in the picture. This post is what the bug actually is, why it is a DevOps problem rather than a sysadmin footnote, the one distinction that decides whether you are exposed, and how to find libssh2 in your stack.

TL;DR

  • CVE-2026-55200 is a pre-auth heap overflow in libssh2's ssh2_transport_read(). It does not bound-check the packet_length field, so a malicious server can trigger an out-of-bounds write and, plausibly, remote code execution. Critical (CVSS 9.2), no credentials or interaction required.
  • It affects the SSH client, not the server. The attacker is whatever host your client connects to.
  • Vulnerable: libssh2 through 1.11.1. Fixed upstream in commit 97acf3d (released as 1.11.2); at disclosure distros were shipping patched 1.11.1 builds ahead of a formal tag.
  • OpenSSH is not libssh2. Your ssh, sshd, and the plain git CLI use OpenSSH's own code and are not affected by this CVE. The exposure is libssh2-linked clients: curl doing scp/sftp, libgit2-based git tooling, PHP/Python SSH bindings, backup agents, embedded devices.
  • libssh2 is frequently statically linked or embedded, so apt upgrade does not always reach it. You have to go looking.

Prerequisites

  • A working idea of the SSH client/server split (who initiates, who listens)
  • Comfort auditing packages and shared-library dependencies on Linux
  • A container or two whose contents you are responsible for

The bug, precisely

Every SSH connection is a stream of binary packets. Each packet is framed by a length field, packet_length, that tells the receiver how many bytes to read next. In libssh2, the function ssh2_transport_read() reads that field and uses it to size a buffer.

The flaw (CWE-680, an integer overflow leading to a buffer overflow) is that it does not enforce an upper bound on packet_length. A malicious server sends a crafted packet with an enormous length value, the size calculation overflows, libssh2 allocates less memory than it goes on to write, and the result is a heap out-of-bounds write. This happens during the transport-layer read, which runs before authentication, so the attacker never needs a valid key or password. A controlled heap overflow of this kind is the classic path to remote code execution, and a public proof-of-concept already exists. No in-the-wild exploitation had been confirmed at the time of writing, but that gap tends to close fast once a PoC is public.

your client  ──TCP connect──▶  attacker's SSH server
             ◀─ crafted packet with a huge packet_length ─
   ssh2_transport_read() under-allocates, then overwrites the heap
             ▶ memory corruption → potential RCE, pre-auth

The mental flip worth internalizing: the dangerous direction here is outbound. A connection your own automation initiates is the attack surface.

Why this is a pipeline problem

"An SSH client bug" sounds like it belongs to humans typing ssh in a terminal. It does not, because those humans are almost all running OpenSSH, which is a separate codebase (more on that in a second). The software that actually links libssh2 is the automation:

  • curl built with libssh2 handles scp:// and sftp:// URLs. Plenty of CI jobs, health checks, and download steps shell out to curl.
  • libgit2-based git tooling. libgit2 can provide SSH transport through libssh2. That covers language bindings like pygit2 and nodegit, and some desktop and CI git integrations that do not shell out to the system git.
  • Language SSH libraries. PHP's ssh2 extension and Python's ssh2-python wrap libssh2 directly. Anything that does programmatic SFTP through them is in scope.
  • Backup and file-transfer agents, and a long tail of embedded and IoT firmware, which often bundle libssh2 statically.

Put together, that is a lot of outbound SSH originating from inside your infrastructure, from processes nobody thinks of as "an SSH client." And the realistic trigger is not exotic: a job that pulls an artifact over sftp, clones from a mirror, or connects to a host resolved from configuration an attacker can influence (a compromised mirror, a typosquatted hostname, or a man-in-the-middle on a flat build network). Pre-auth means the connection does not have to succeed for the payload to land.

The one distinction that decides everything: OpenSSH is not libssh2

This is where most of the panic should drain away, and where the real audit begins. There are two completely separate SSH implementations in play, and only one of them is affected:

You are using... SSH comes from Affected by CVE-2026-55200?
ssh, scp (OpenSSH), sshd OpenSSH's own code No
The plain git CLI over SSH Shells out to the OpenSSH ssh binary No
curl scp:// / sftp:// libssh2 (if built with it) Yes, if libssh2 ≤ 1.11.1
pygit2 / nodegit / libgit2 tools libgit2's SSH backend Yes, if built against libssh2 ≤ 1.11.1
PHP ssh2, ssh2-python libssh2 Yes, if libssh2 ≤ 1.11.1

The libgit2 row has a wrinkle worth knowing: libgit2's SSH support is a build-time choice. Its USE_SSH option can be set to libssh2 (which links the vulnerable library) or to exec (which shells out to the system OpenSSH binary instead). Two builds of the same tool can land on opposite sides of this table. So "am I affected" is not a question about which tool you use; it is a question about what that tool was linked against.

Note

The everyday git clone [email protected]:... you run in a terminal uses the OpenSSH ssh binary and is not affected through that path. The risk is the tooling that embeds a git implementation rather than calling out to git, and the non-git clients above.

Am I affected? Go and look

Because libssh2 hides inside other binaries, the check is a small hunt rather than one command. Start with the usual suspects:

Then widen the net, because the dynamic-library check misses the worst case:

  • Static linking is the trap. A Go, Rust, or C binary can compile libssh2 straight in, so it will not show up in ldd or your package list, and apt upgrade will never touch it. For suspect binaries, strings ./binary | grep -i 'libssh2' sometimes surfaces an embedded version banner. Container image scanners like Trivy or Grype are better at this than a shell loop.
  • Language bindings pin their own copy. Check pygit2, nodegit, ssh2-python, and the PHP ssh2 extension against the libssh2 they were built with, not the system package.
  • Base images and firmware may ship an old libssh2 you inherited. Rebuild from a patched base rather than assuming the registry did it for you.

Anything you find at 1.11.1 or earlier is in scope.

Fixing it

The fix is upstream commit 97acf3d, which adds the missing bound on packet_length (a LIBSSH2_PACKET_MAXPAYLOAD check), released as libssh2 1.11.2. Practically:

  • Update the package to a build that includes the fix. Distributions began shipping patched 1.11.1 packages before a new upstream tag existed, so trust your distro's advisory version over the raw upstream tag.
  • Rebuild anything that static-links or vendors libssh2. The library upgrade only helps binaries that actually pick it up. Your own images and Go/Rust artifacts need a rebuild against the patched library.
  • Rebuild containers from a patched base, and re-scan, rather than patching a running layer.
  • Constrain egress as defense in depth. This bug needs your client to reach a hostile server. Build and CI networks that can only open SSH to a known allowlist of hosts remove the easy version of the attack, and that control is worth having regardless of this CVE.
  • Prefer OpenSSH-backed transports where you have the choice. If a tool can shell out to the system ssh instead of linking libssh2, that path is not affected here.
Warning

Do not let "we patched libssh2" become a false all-clear. The dangerous copies are the ones your inventory did not know about: a vendored library inside a language binding, a statically linked CLI, an appliance or IoT image you do not rebuild. Patch the package, then go hunting for the copies that a package manager cannot see.

Wrapping up

CVE-2026-55200 is a good reminder that "securing SSH" is two problems, not one. The server side is the one everybody drills, and it is not what this bug touches. The client side, the outbound connections your automation makes through libraries you did not realize were speaking SSH, is the quieter surface, and it is exactly where a pre-auth heap overflow like this one bites. The work is not glamorous: figure out where libssh2 actually lives in your stack, including the static and vendored copies your package manager cannot see, update to 1.11.2 or a patched build, and rebuild what embeds it. The tooling that connects out on your behalf deserves the same scrutiny as the box that accepts connections.

Published: 2026-07-02|Last updated: 2026-07-02T12:00:00Z

Found an issue?

Also worth your time on this topic