Human
Machine
REPRO-2026-00141 LOW OOB Write
Verified
rsync: off-by-one out-of-bounds stack write in establish_proxy_connection
rsync (c) May 22, 2026
What's the vulnerability?
rsync can connect to a remote daemon through an HTTP proxy. The function
establish_proxy_connection() in socket.c performs an off-by-one
out-of-bounds write on a stack buffer while handling the proxy server's
CONNECT response. A malicious proxy server that returns an overlong
CONNECT response corrupts memory adjacent to that stack buffer.
The write is a single byte past the buffer end, so impact is limited (NVD rates the issue Low), but it is still out-of-bounds stack corruption driven by attacker-controlled input.
Root Cause Analysis
# RCA Report: CVE-2026-45232
## Summary
CVE-2026-45232 is an off-by-one out-of-bounds stack write vulnerability in the `rsync` utility. The bug resides in `establish_proxy_connection()` within `socket.c`. When rsync connects through an HTTP proxy (configured via `RSYNC_PROXY`), it reads the proxy's `CONNECT` response into a 1024-byte stack buffer. If the proxy returns an overlong response (1023+ bytes without a newline terminator), post-loop null-termination logic writes one byte past the buffer's end, corrupting adjacent stack memory.
## Impact
- **Package**: `rsync` (C CLI tool)
- **Affected versions**: `< 3.4.2`
- **Fixed version**: `3.4.3`
- **Risk level**: Low (NVD CVSS 3.1 base 3.1)
- **Consequences**: A malicious or MITM'd HTTP proxy can trigger a single-byte stack overwrite, potentially leading to a crash or undefined behavior. The overwritten byte is always a null terminator (`\0`), and the attacker does not control the offset, limiting exploitability.
## Root Cause
In `socket.c`, `establish_proxy_connection()` declares a stack buffer `char buffer[1024]` and reads the proxy response one byte at a time into `buffer[0]` through `buffer[1022]`. The loop condition is `cp < &buffer[sizeof buffer - 1]`.
If no newline arrives before the buffer is filled, `cp` is left at `&buffer[1023]` (the last valid index). The post-loop code then executes:
```c
if (*cp != '\n')
cp++;
*cp-- = '\0';
```
Because `*cp` was never written by the loop (it contains stale stack data), the `if` is usually true, so `cp++` advances past the array to `&buffer[1024]`. The subsequent `*cp = '\0'` writes one byte out of bounds.
**Fix commit**: `a5fc5ebe7a8ef1aa72f6e344599f97fd4427ecba` ("socket: reject over-long proxy response line")
The fix:
1. Increases the buffer size to `PROXY_BUF_SIZE + 1` (1025 bytes) for safe null-termination
2. Adds an explicit check: if `cp == &buffer[PROXY_BUF_SIZE - 1]`, reject with `"proxy response line too long"` instead of writing past the buffer
3. Correctly null-terminates within bounds for shorter responses
## Reproduction Steps
1. `repro/reproduction_steps.sh` clones the rsync repository, checks out `v3.4.2` (vulnerable) and `v3.4.3` (fixed), and builds both with AddressSanitizer (`-fsanitize=address`).
2. It starts a minimal Python TCP mock proxy that binds to a local ephemeral port, accepts one connection, reads the `CONNECT` request headers, and sends exactly 1023 bytes of `'X'` with no newline terminator.
3. It runs `rsync` via `RSYNC_PROXY=127.0.0.1:<port>` against each build.
4. Expected behavior:
- **v3.4.2 (vulnerable)**: AddressSanitizer reports `stack-buffer-overflow` at `establish_proxy_connection` (`socket.c:95`), and rsync aborts.
- **v3.4.3 (fixed)**: rsync prints `"proxy response line too long"` and exits with error code 10 (non-zero but no crash).
## Evidence
- **Log files**:
- `logs/rsync_v3.4.2_proxy.log` — ASan output for the vulnerable build
- `logs/rsync_v3.4.3_proxy.log` — stderr for the fixed build
- **ASan excerpt** (vulnerable build):
```
==7440==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7f747c600940
WRITE of size 1 at 0x7f747c600940 thread T0
#0 establish_proxy_connection /tmp/.../rsync/socket.c:95
...
Memory access at offset 2368 overflows this variable
[1344, 2368) 'buffer' (line 55)
```
- **Fixed build excerpt**:
```
proxy response line too long
rsync error: error in socket IO (code 10) at clientserver.c(141) [Receiver=3.4.3]
```
- **Runtime manifest**: `repro/runtime_manifest.json` records build versions, exit codes, ASan excerpt, payload size (1023 bytes), and confirmation flags.
## Recommendations / Next Steps
1. **Upgrade** to rsync `>= 3.4.3` to obtain the bounds-check fix.
2. **Network controls**: Restrict proxy configurations (`RSYNC_PROXY`) to trusted infrastructure to reduce exposure to malicious proxies.
3. **Testing**: The upstream test `testsuite/proxy-response-line-too-long.test` should be included in CI to prevent regression.
4. **Build hardening**: Compile with `-fstack-protector-strong` and ASan in QA pipelines to detect similar stack overwrites early.
## Additional Notes
- **Idempotency**: The reproduction script was run twice consecutively; both runs produced identical ASan crashes on `v3.4.2` and clean rejection on `v3.4.3`.
- **Payload specifics**: 1023 bytes of `'X'` without a newline is the exact boundary that triggers the off-by-one. A shorter payload does not trigger the bug; a newline-terminated payload exits the read loop early.
- **Build environment**: Tested on `x86_64-pc-linux-gnu` with GCC and AddressSanitizer. The bug is architecture-agnostic, but ASan makes the one-byte overwrite reliably observable.
One Command
Verify with pruva-verify
Run the Pruva CLI to automatically fetch and execute the reproduction script.
pruva-verify REPRO-2026-00141 or
pruva-verify CVE-2026-45232 Install:
curl -fsSL https://pruva.dev/install.sh | sh Or Run Manually
1
Download the script
curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00141/artifacts/reproduction_steps.sh 2
Make executable
chmod +x reproduction_steps.sh 3
Run the script
./reproduction_steps.sh Run in a VM, container, or disposable environment. This exploits a real vulnerability.
How Pruva Reproduced This
Watch the AI agent's step-by-step process.
Loading session...
Artifacts
No artifacts available