SecPod

Learn Search

Search across all Learn content

← Back to Security Research
Inside CVE-2026-55200: How a Missing Bounds Check in libssh2 Becomes Remote Code Execution

Inside CVE-2026-55200: How a Missing Bounds Check in libssh2 Becomes Remote Code Execution

A malicious SSH server can corrupt heap memory on any client using libssh2, before authentication and without any user interaction. This blog walks through the exact arithmetic behind the bug, why it lives in ssh2_transport_read(), how the exploitation path works conceptually, and what to check in your own environment.

Jul 13, 2026By Yash Raj

Summary

SSH has long been trusted to securely access remote systems, but what if the server you're connecting to is the attacker? The disclosure of CVE-2026-55200 and CVE-2026-55199 in libssh2, a client-side library that implements the SSH2 protocol, demonstrates exactly that risk. Together, these vulnerabilities let a malicious SSH server trigger a client crash before authentication and, under certain conditions, potentially achieve remote code execution. The release of a public proof-of-concept, in a GitHub archive titled "exploitarium" on June 29, 2026, has further increased the urgency for organizations to patch affected systems.

This is not a new bug class for libssh2. A near-identical integer overflow in the same function was fixed in 2019 as CVE-2019-3855. The recurrence, seven years later, is a useful case study in how easily an unchecked length field creeps back into transport layer parsing code after refactors.

What libssh2 is, and what it is not

libssh2 implements the client side of SSH2. It is embedded, often statically, inside curl builds with SCP/SFTP support, Git GUI clients, PHP's ssh2 extension (common in hosting control panels and deployment scripts), backup agents, and network appliances that automate outbound SSH connections. Static linking is the operational sting in this disclosure: a distribution patching its system libssh2 package does nothing for a tool that compiled its own copy in and never touches the system library again.

libssh2 versus libssh

These are two separate, unrelated projects that share a confusingly similar name.

libssh2libssh
RoleClient-only libraryClient and server library
Typical embeddercurl, Git, PHP, backup and automation toolsStandalone SSH servers, some tunnel and VPN software
Relation to this CVEAffectedNot affected, unrelated codebase

Vulnerability Details

CVE IDCVSS ScoreEPSS ScoreAffected ProductsVulnerability Type
CVE-2026-552008.3 (High)0.73%libssh2 v1.11.1 and belowHeap-based Buffer Overflow
CVE-2026-551997.5 (High)0.41%libssh2 v1.11.1 and belowInfinite Loop / Resource Exhaustion

Root Cause Analysis

The bug sits in ssh2_transport_read(), the function responsible for parsing raw SSH packets off the wire. During the unauthenticated part of the handshake, a malicious server can send a packet with a crafted packet_length field. Because that field is not validated against an upper bound before it is used in size calculations, the arithmetic wraps around, producing an undersized memory allocation that later packet processing code still treats as if it were the original, much larger size. The result is an out-of-bounds heap write, and depending on allocator behavior and binary layout, a path to remote code execution.

The SSH packet header and the trust boundary

Before any encryption or authentication is established, an SSH client and server exchange packets in a fixed binary format defined by RFC 4253. Every packet begins with the same header:

FieldSizeNotes
packet_length4 bytesAttacker controlled during the unauthenticated handshake
padding_length1 byteNumber of padding bytes appended
payloadvariableThe actual message content
paddingvariableRandom padding bytes
MACoptionalNot present until encryption is active

packet_length is a plain 32-bit unsigned integer, and it is the very first piece of information the client reads for a given packet. Nothing upstream has validated it yet, because nothing upstream exists at that point in the handshake. A correct implementation has to treat that field as hostile and check it against a sane maximum, RFC 4253 specifies 35,000 bytes, before doing any arithmetic with it. CVE-2026-55200 exists because that check either ran too late in ssh2_transport_read(), or did not run at all on the code path a crafted packet could reach.

The wraparound arithmetic

The underlying bug pattern is straightforward integer overflow. Somewhere in the allocation path, a fixed amount of struct or header overhead is added to the attacker-supplied packet_length to compute how large a buffer to allocate. That addition happens in a 32-bit unsigned integer, which can only represent values from 0 up to 4,294,967,295 (2 to the power of 32, minus 1). Add anything past that ceiling and the value does not raise an error. It silently wraps back around to a small number, because unsigned integer overflow is defined, well-behaved arithmetic in C, not a fault the language will catch for you.

total_num += packet_length + mac_len + auth_len
packet_length   1,000 3,000,000,000 4,294,967,267 4,294,967,280
mac_len   20
auth_len   8
total_num = 1,028 3,000,000,028 4,294,967,295 12 (wrapped from 4,294,967,308)

Try clicking the buttons above. When packet_length is small or even really large, the math just works normally, everything adds up fine. But push it to 4,294,967,267, and the total lands exactly on 4,294,967,295, the biggest number a 32-bit field can hold. Add even one byte more, and something strange happens: instead of getting a bigger number, the total suddenly drops all the way down to 12

That's the bug. The computer tried to store a number too big to fit, so it "wrapped around" back to a tiny value. Now, code further down thinks the incoming data is only 12 bytes long and sets aside a tiny buffer for it, even though the attacker is actually about to send billions of bytes. Whatever handles that data has no idea it was given the wrong size, and that mismatch is where things go wrong.

Inside ssh2_transport_read()

ssh2_transport_read() in src/transport.c is the function responsible for turning raw bytes off the socket into a parsed SSH packet. It runs for the life of the connection, but the interesting window is the handshake, before encryption keys exist, because that is the only point where the client is guaranteed to process attacker-supplied data with no prior verification. Conceptually, the function:

  1. 1. Reads the 4-byte packet_length field from the socket.
  2. 2. Computes how large a buffer is needed to hold the incoming payload, padding, and any bookkeeping overhead.
  3. 3. Allocates or reuses a buffer sized for that computation.
  4. 4. Reads the payload, padding, and MAC into that buffer.
  5. 5. Passes the parsed packet up to the key exchange or transport layer.

The flaw is that step 2 and step 4 disagree after a wraparound. Step 2 computes a buffer size using the wrapped, tiny value. Step 4 still has the original, oversized packet_length in scope and uses it, or a value derived from it, to decide how many bytes to copy. Neither line of code is incorrect by itself. The vulnerability lives entirely in the missing comparison that should have rejected an oversized packet_length before either step ran.

The official fix, in commit 97acf3d, adds exactly that comparison: reject any packet_length above the protocol's permitted maximum before it participates in any arithmetic or allocation sizing.

How CVE-2026-55200 Can Be Exploited

Attack prerequisites

  • • A target application linked against a vulnerable version of libssh2 (1.11.1 or earlier).
  • • That application initiates an outbound SSH connection to a server the attacker controls, or the attacker can intercept the TCP connection before the legitimate server responds.
  • No valid credentials are needed. No user interaction beyond the application making its normal connection is needed.

Conceptual exploitation flow

  1. 1. The client application calls into libssh2 to begin a handshake with the target server, typically via a function like libssh2_session_handshake().
  2. 2. The attacker's server responds during the unauthenticated key exchange phase with a crafted packet whose packet_length header is set to a value near the top of the 32-bit range.
  3. 3. ssh2_transport_read() computes an allocation size using that value plus fixed overhead, and the addition wraps around to a small number.
  4. 4. A small buffer is allocated based on the wrapped value.
  5. 5. Later logic in the same read path still operates using the original, oversized packet_length, and writes payload data into the undersized buffer past its real boundary.
  6. 6. The out-of-bounds write corrupts adjacent heap memory. Whether this is exploitable for code execution, rather than just a crash, depends on the target binary, the allocator implementation, active mitigations such as ASLR and heap hardening, and what memory is adjacent to the corrupted buffer.

Reliable remote code execution is not guaranteed just because the memory corruption primitive exists. It depends heavily on allocator behavior, compiler mitigations, and how the embedding application links and uses libssh2. Treat this as a critical memory safety bug with a credible path to RCE, not as a universal, drop-in remote exploit.

Lab Validation — Confirmed RCE in an Isolated Sandbox

sandbox — libssh2-cve-2026-55200-poc
root@a52aa74b44d1:/app/cve-2026-55200# gcc -O0 -g -Wall -Wextra -o poc/libpwn_local_rce_harness poc/libpwn_local_rce_harness.c
root@a52aa74b44d1:/app/cve-2026-55200# python3 poc/libpwn_local_rce_exploit.py --harness ./poc/libpwn_local_rce_harness --proof ./poc/libpwn_rce_proof.txt
LEAK exec_callback=0x5a32ad8f41d3 callback_offset=24 command_offset=32 ptr_size=8
accepted packet_length=0xffffffff allocation=19 copy_len=117 body_len=117
exec_callback command=/bin/sh -c 'echo libpwn-rce-verified > /app/cve-2026-55200/poc/libpwn_rce_proof.txt'
system_rc=0
process_rc=0
payload_len=117
proof_path=/app/cve-2026-55200/poc/libpwn_rce_proof.txt
RCE_PROOF=PASS
root@a52aa74b44d1:/app/cve-2026-55200# cat poc/libpwn_rce_proof.txt
libpwn-rce-verified

Impact

The direct technical impact is heap memory corruption reachable by an unauthenticated remote server, with a credible path to arbitrary code execution in the context of any process linking the vulnerable libssh2 code. The broader impact comes from how quietly libssh2 spreads through the software supply chain.

CategoryExamplesWhy it matters here
Download and transfer toolscurl builds with SCP/SFTP supportExtremely widely deployed, often in scripts and automation that connect to many different hosts
Developer toolingGit GUI clients, some Git over SSH integrationsDeveloper workstations connecting to a wide range of remote repositories
Web hosting and PHPPHP's ssh2 extensionCommon in hosting control panels and deployment scripts that connect outbound to customer or third party servers
Automation and infrastructureBackup agents, CI/CD runners, firmware updatersFrequently make unattended outbound SSH connections with no human watching the session
Embedded and appliancesNetwork appliances, IoT firmwareLong patch cycles, static linking, and low visibility into what libraries are actually bundled

MITRE ATT&CK Mapping

Technique IDTechnique NameTactic
T1190Exploit Public-Facing ApplicationInitial Access
T1078.001Default AccountsInitial Access
T1027Obfuscated Files or InformationDefense Evasion
T1071.001Web ProtocolsCommand-And-Control
T1499Endpoint Denial of ServiceImpact

Mitigation

  1. 1. Patch to a build containing commit: There is no official tagged libssh2 release containing the fix at the time of writing. The patch exists on the mainline branch as commit 97acf3d, and distributions are backporting it independently.

     Fix commit: 97acf3dfda80c91c3a8c9f2372546301d4a1a7a8
     Upstream pull request: libssh2/libssh2#2052

  1. 2. Restrict outbound SSH connections to trusted, known servers only, especially for automation, CI/CD, backup, and embedded/IoT systems. Use firewall rules and network segmentation to limit which hosts your libssh2-based clients can reach.
  2. 3. Verify host keys rigorously and prioritize hardening for any client that reaches external SSH servers or resolves hosts via DNS names an attacker could potentially redirect.

Instantly Fix Risks with Saner Patch Management

Saner patch management is a continuous, automated, and integrated software that instantly fixes risks exploited in the wild. The software supports major operating systems like Windows, Linux, and macOS, as well as 550+ third-party applications.

It also allows you to set up a safe testing area to test patches before deploying them in a primary production environment. Saner patch management additionally supports a patch rollback feature in case of patch failure or a system malfunction.

Experience the fastest and most accurate patching software here.

Featured Posts

Open One Request, Total Persistence: Inside the SharePoint Flaw Attackers Are Exploiting
One Request, Total Persistence: Inside the SharePoint Flaw Attackers Are Exploiting

CVE Research

One Request, Total Persistence: Inside the SharePoint Flaw Attackers Are Exploiting

A critical SharePoint deserialization flaw, CVE-2026-50522 (CVSS 9.8), is under active exploitation just weeks after its July 2026 patch, following a public PoC. Attackers are using it to steal IIS machine keys in a single request, gaining persistence that survives patching alone. Now on CISA's KEV list, it's the third actively exploited SharePoint flaw in recent months, patch immediately and rotate machine keys.

Jul 24, 2026

Open ENCFORGE Ransomware: Anatomy of an AI-Focused Cyber Attack
ENCFORGE Ransomware: Anatomy of an AI-Focused Cyber Attack

CVE Research

ENCFORGE Ransomware: Anatomy of an AI-Focused Cyber Attack

Jul 22, 2026

Open UTA0533 Weaponizes KNUCKLEBALL: Inside the SonicWall SMA Zero-Day Exploitation Chain
UTA0533 Weaponizes KNUCKLEBALL: Inside the SonicWall SMA Zero-Day Exploitation Chain

CVE Research

UTA0533 Weaponizes KNUCKLEBALL: Inside the SonicWall SMA Zero-Day Exploitation Chain

Jul 20, 2026

Open One Email, Full Session Takeover: Inside Zimbra's Critical Classic Web Client Code Execution Flaw
One Email, Full Session Takeover: Inside Zimbra's Critical Classic Web Client Code Execution Flaw

CVE Research

One Email, Full Session Takeover: Inside Zimbra's Critical Classic Web Client Code Execution Flaw

Jul 20, 2026