GHSA-6c8g-7p36-r338 is a medium-severity (CVSS 5.9) vulnerability in SharpCompress. O3 Security confirms whether GHSA-6c8g-7p36-r338 is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.
SharpCompress has directory traversal via directory entries in WriteToDirectory (zip slip variant)
Real-World Exposure
SharpCompressReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects NuGet packages — download data is not available via public APIs for these ecosystems.
Description
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:
-
Symlink entry —
link→../evil_outside/TheSymbolicLinkHandlercreates/safe/extract/linkpointing outside the extraction root. -
File entry —
link/secret.txtExtractionMethods.WriteEntryToDirectorycomputes: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.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| .NETNuGet | SharpCompress | all versions | 0.48.0 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for SharpCompress. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.
Fix
Update SharpCompress to 0.48.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-6c8g-7p36-r338 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 pinpoints whether GHSA-6c8g-7p36-r338 is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.
Tailored to GHSA-6c8g-7p36-r338. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-6c8g-7p36-r338 in your dependencies?
O3 detects GHSA-6c8g-7p36-r338 across NuGet dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.