Skip to content
Fencer Intel
low 3.8

GHSA-h39g-6x3c-7fq9 json

Zio has SubFileSystem Path Confinement Bypass via Unresolved `..` Segment

published 6mo ago modified 6mo ago

cvss
3.8
version 3.1, GitHub
cisa kev
Not listed
not in CISA's catalog
epss
—
no score, EPSS covers CVEs only
public exploits
0
none known

Summary

# Summary

`SubFileSystem` fails to confine operations to its declared sub path when the input path is `/../` (or equivalents `/../`, `/..\\`). This path passes all validation but resolves to the root of the parent filesystem, allowing directory level operations outside the intended boundary.

# Affected Component

`Zio.UPath.ValidateAndNormalize` `Zio.FileSystems.SubFileSystem`

`UPath.ValidateAndNormalize` has a trailing slash optimisation.

```csharp if (!processParts && i + 1 == path.Length) return path.Substring(0, path.Length - 1); ```

When the input ends with `/` or `\`, and `processParts` is still false, the function strips the trailing separator and returns immediately before the `..` resolution logic runs. The input `/../` triggers this path: the trailing `/` is the last character, `processParts` has not been set (because `..` as the first relative segment after root is specifically exempted), so the function returns `/..` with the `..` segment unresolved.

The resulting `UPath` with `FullName = "/.."` is absolute, contains no control characters, and no colon so it passes `FileSystem.ValidatePath` without rejection.

When this path reaches `SubFileSystem.ConvertPathToDelegate`:

```csharp protected override UPath ConvertPathToDelegate(UPath path) { var safePath = path.ToRelative(); // "/..".ToRelative() = ".." return SubPath / safePath; // "/jail" / ".." = "/" (resolved by Combine) } ```

The delegate filesystem receives `/` (the root) instead of a path under `/jail`.

# Proof of Concept

```csharp using Zio; using Zio.FileSystems;

var root = new MemoryFileSystem(); root.CreateDirectory("/sandbox"); var sub = new SubFileSystem(root, "/sandbox");

Console.WriteLine(sub.DirectoryExists("/../")); // True (sees parent root) Console.WriteLine(sub.ConvertPathToInternal("/../")); // "/" (parent root path) ```

# Impact

The escape is limited to directory level operations because appending a filename after `..` (e.g., `/../file.txt`) causes normal `..` resolution to trigger, which correctly rejects the path as going above root. Only the bare terminal `/../` (which strips to `/..`) survives. This means that exploitability is limited, and this vulnerability does not escalate to file read/write.

Patch / fix

  • nuget Zio 0.22.2

Is GHSA-h39g-6x3c-7fq9 in your dependencies? Fencer shows which of your repositories are affected as soon as an advisory lands, and opens the fix as a pull request.

Check with Fencer

CVSS scores

assigned by version score vector
GitHub 3.1 3.8 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:L/I:N/A:N

Affected packages

ecosystem package affected fixed in
nuget Zio
<0.22.2
0.22.2

Classification

Weakness
  • CWE-179 Incorrect Behavior Order: Early Validation
  • CWE-22 Improper Limitation of a Pathname to a Restricted Directory

References

Sources

Fencer Intel keeps one entry per vulnerability: the CVE id wins, then GHSA, and records that alias each other are merged, except that two CVEs always keep separate entries. The same data is available as JSON.