GHSA-6c8g-7p36-r338

Suggest an improvement
Source
https://github.com/advisories/GHSA-6c8g-7p36-r338
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/05/GHSA-6c8g-7p36-r338/GHSA-6c8g-7p36-r338.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-6c8g-7p36-r338
Aliases
  • CVE-2026-44788
Published
2026-05-08T23:50:40Z
Modified
2026-05-09T00:01:26.723108Z
Severity
  • 5.9 (Medium) CVSS_V3 - CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:L CVSS Calculator
Summary
SharpCompress has directory traversal via directory entries in WriteToDirectory (zip slip variant)
Details

Summary

A path traversal vulnerability in IArchive.WriteToDirectory() allows a malicious archive to create directories outside the intended extraction root. For TAR archives, this can be escalated to arbitrary file writes by chaining with a symlink entry, giving a full write primitive on the target filesystem subject to the permissions of the running process.

Details

The vulnerable code is in the directory-entry branch of WriteToDirectoryInternal (sync, IArchiveExtensions.cs:48–61) and WriteToDirectoryAsyncInternal (async, IAsyncArchiveExtensions.cs:70–84):

var dirPath = Path.Combine(destinationDirectory, entry.Key);
Directory.CreateDirectory(Path.GetDirectoryName(dirPath + "/"));

No Path.GetFullPath() normalisation and no bounds check are applied before the Directory.CreateDirectory call. Two .NET Path.Combine behaviours make this exploitable:

  • Relative traversal: Path.Combine("/safe/extract", "../../evil") → the OS resolves .. segments on the raw path, placing the directory outside the extraction root.
  • Absolute path override: Path.Combine("/safe/extract", "/tmp/evil") → returns "/tmp/evil" — the base is discarded entirely for rooted paths.

File entries are not directly affected — they route through ExtractionMethods.WriteEntryToDirectory which applies the correct guard (GetFullPath + StartsWith, see ExtractionMethods.cs:54–65). The directory-entry branch is a separate fast-path that was added without that guard.

Affected archive formats: ZIP and TAR (non-solid). Solid archives and 7-Zip use the reader path which calls the secure method.

Escalation to arbitrary file writes (TAR only)

Path.GetFullPath on .NET does not resolve symlinks — it only normalises . and .. segments. This means the file-entry guard in ExtractionMethods.WriteEntryToDirectory can be bypassed via symlink chaining in TAR archives when the caller supplies a SymbolicLinkHandler:

archive.WriteToDirectory("/safe/extract", new ExtractionOptions
{
    ExtractFullPath = true,
    SymbolicLinkHandler = (linkPath, linkTarget) =>
        File.CreateSymbolicLink(linkPath, linkTarget)  // naive — no validation of linkTarget
});

Attack sequence in a single TAR archive:

  1. Symlink entrylink../evil_outside/ The SymbolicLinkHandler creates /safe/extract/link pointing outside the extraction root.

  2. File entrylink/secret.txt ExtractionMethods.WriteEntryToDirectory computes:

    • destdir = Path.GetFullPath("/safe/extract/link")"/safe/extract/link" — textually inside root, check passes ✓
    • File.Open("/safe/extract/link/secret.txt") — OS follows symlink, file is written to /evil_outside/secret.txt

The library does not validate linkTarget before passing it to the caller's handler, and the XML docs do not warn that it may be a traversal path. The idiomatic handler implementation above is therefore silently exploitable.

ZIP does not support symlinks in SharpCompress (ZipEntry.LinkTarget always returns null), so this escalation is TAR-only.

| Attack | ZIP | TAR | |--------|-----|-----| | Directory traversal (escape extraction root) | Yes | Yes | | Escalate to arbitrary file writes via symlink chain | No | Yes (if caller provides SymbolicLinkHandler) |

Recommended fix — apply the same pattern from ExtractionMethods.WriteEntryToDirectory to both affected files:

var fullDestDir = Path.GetFullPath(destinationDirectory);
if (!fullDestDir.EndsWith(Path.DirectorySeparatorChar))
    fullDestDir += Path.DirectorySeparatorChar;

var dirPath = Path.GetFullPath(Path.Combine(fullDestDir, entry.Key));
if (!dirPath.StartsWith(fullDestDir, PathComparison))
    throw new ExtractionException(
        "Entry is trying to create a directory outside of the destination directory.");

Directory.CreateDirectory(dirPath);

Additionally, the library should validate LinkTarget before invoking the caller's SymbolicLinkHandler, or document clearly that callers must validate it themselves.

PoC

A self-contained .NET console app is available at: https://github.com/svenclaesson/poc-sharpcompress-traversal

git clone https://github.com/svenclaesson/poc-sharpcompress-traversal
cd poc-sharpcompress-traversal
dotnet run

The PoC crafts a ZIP with three directory entries (../../escaped_relative/, /tmp/escaped_absolute/, safe_subdir/) using System.IO.Compression (stdlib), then extracts with SharpCompress. Output shows [ESCAPED] for the two malicious entries and [ok] for the legitimate one, on both sync and async APIs.

Tested against SharpCompress 0.47.4 (latest NuGet).

Impact

This is a path traversal / zip slip vulnerability (CWE-22). Any application that calls archive.WriteToDirectory() on an untrusted archive is affected — which covers the primary documented extraction API.

For ZIP archives the impact is limited to arbitrary directory creation, which can be used to stage privilege escalation (e.g. cron drop-ins, XDG config paths, service spool directories) or shadow expected paths to alter application behaviour.

For TAR archives, callers that implement a SymbolicLinkHandler — which is the only way to faithfully restore a TAR — are exposed to a full arbitrary file write primitive via the symlink chaining described above.

Database specific
{
    "github_reviewed_at": "2026-05-08T23:50:40Z",
    "github_reviewed": true,
    "cwe_ids": [
        "CWE-22"
    ],
    "nvd_published_at": null,
    "severity": "MODERATE"
}
References

Affected packages

NuGet / SharpCompress

Package

Name
SharpCompress
View open source insights on deps.dev
Purl
pkg:nuget/SharpCompress

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0Unknown introduced version / All previous versions are affected
Last affected
0.47.4

Affected versions

0.*
0.2.0
0.3.0
0.4.0
0.5.0
0.6.0
0.7.0
0.8.0
0.8.1
0.8.2
0.9.0
0.10.0
0.10.1
0.10.1.1
0.10.1.3
0.10.2
0.10.3
0.11.0
0.11.1
0.11.2
0.11.3
0.11.4
0.11.5
0.11.6
0.12.0
0.12.1
0.12.2
0.12.3
0.12.4
0.13.0
0.13.1
0.14.0
0.14.1
0.15.0
0.15.1
0.15.2
0.16.0
0.16.1
0.16.2
0.17.0
0.17.1
0.18.0
0.18.1
0.18.2
0.19.0
0.19.1
0.19.2
0.20.0
0.21.0
0.21.1
0.22.0
0.23.0
0.24.0
0.25.0
0.25.1
0.26.0
0.27.0
0.27.1
0.28.0
0.28.1
0.28.2
0.28.3
0.29.0
0.30.0
0.30.1
0.31.0
0.32.0
0.32.1
0.32.2
0.33.0
0.34.0
0.34.1
0.34.2
0.35.0
0.36.0
0.37.0
0.37.1
0.37.2
0.38.0
0.39.0
0.40.0
0.41.0
0.42.0
0.42.1
0.43.0
0.44.0
0.44.1
0.44.2
0.44.3
0.44.4
0.44.5
0.45.0
0.45.1
0.46.0
0.46.1
0.46.2
0.46.3
0.46.4
0.47.0
0.47.1
0.47.2
0.47.3
0.47.4

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/05/GHSA-6c8g-7p36-r338/GHSA-6c8g-7p36-r338.json"