GHSA-74m3 - zrok WebDAV DriveRoot symlink escape
zrok WebDAV drive shares are vulnerable to a DriveRoot boundary escape when a
symbolic link already exists inside the shared root and points outside that
root. The advisory describes lexical path normalization in the WebDAV backend,
but not enough protection against symlink following. A remote WebDAV consumer
can use the public share endpoint to read files through the symlink and, where
the zrok process has filesystem permission, overwrite symlink targets outside
the intended shared directory.
For SecurityRecipes and production MCP deployments, the important lesson is direct: a context root is not a boundary if path checks happen before symlink resolution. Treat WebDAV shares, agent artifact shares, and temporary context exports as sensitive execution surfaces, not convenience file browsers.
Affected versions
- Vulnerable v1 module:
github.com/openziti/zrok <=1.1.11 - Fixed v1 module: no patched version is listed for the v1 module
- Vulnerable v2 module:
github.com/openziti/zrok/v2 <2.0.2 - Fixed v2 module:
github.com/openziti/zrok/v2 2.0.2+
The GitHub advisory has no assigned CVE at the time this recipe was written.
Indicator-of-exposure
- The repository builds, vendors, deploys, or documents
zrokdrive shares. - A service runs
zrok share public --backend-mode driveor equivalent WebDAV drive backend configuration. - The shared DriveRoot can contain symlinks, including symlinks created by developers, CI jobs, build tools, unpacked archives, agent workspaces, or runtime tasks.
- The zrok process can read secrets, source code, SSH material, service config, build outputs, home directories, mounted volumes, or other files outside the intended DriveRoot.
- The zrok process can write to host paths outside the intended DriveRoot.
- WebDAV shares are unauthenticated, public, shared with broad audiences, or reachable by an agent, browser, CI worker, partner system, or customer.
Quick checks:
rg -n "zrok|openziti/zrok|backend-mode[ =]drive|DriveRoot|WebDAV|webdav|davServer|share public" .
go list -m -json all | jq -r 'select(.Path|test("^github.com/openziti/zrok(/v2)?$"))'
rg -n "zrok.*share|share.*zrok|backend-mode|drive" Dockerfile* docker-compose*.yml charts deploy k8s helm systemd .github .
find . -type l -lsRemediation strategy
- Upgrade every controlled
github.com/openziti/zrok/v2dependency, binary, image, and deployment to2.0.2+. - Remove or replace
github.com/openziti/zrok <=1.1.11drive shares unless a vendor-supported fixed v1 build is explicitly verified. - Disable public unauthenticated drive shares where they are not required.
- Move DriveRoot to a dedicated, empty, low-sensitivity directory owned by the zrok service account; do not point it at source trees, home directories, shared build workspaces, mounted host roots, or MCP context stores.
- Remove symlinks from DriveRoot before publishing a share, and add a startup or CI guard that fails if symlinks are present.
- Run zrok with a low-privilege OS identity, restricted filesystem access, read-only mounts where possible, and no access to SSH keys, cloud credentials, package tokens, model credentials, or production config.
- If this repository owns a fork or wrapper, enforce a real resolved-path boundary: reject symlinks, validate every path component, and ensure the final opened file remains under DriveRoot at the moment it is opened.
The prompt
Model context: this prompt was generated by GPT 5.5 Extra High reasoning.
You are remediating GHSA-74m3-9qvm-rp9h in zrok WebDAV drive shares. Produce
exactly one output:
- A reviewer-ready PR/change request that upgrades, disables, or safely
contains affected zrok drive shares and documents operator cleanup, or
- TRIAGE.md if this repository does not own an affected zrok runtime or cannot
make a safe change.
## Rules
- Scope only GHSA-74m3-9qvm-rp9h.
- Treat SSH keys, cloud credentials, model/API tokens, repository contents,
build artifacts, user files, hostnames, and WebDAV share URLs as sensitive.
- Do not probe public zrok shares, production shares, customer shares, or
shared developer machines.
- Do not create proof-of-concept overwrites outside a temporary test directory.
- Do not treat lexical path cleaning as sufficient protection.
- Do not auto-merge.
## Steps
1. Inventory every zrok asset controlled by this repository: Go modules,
lockfiles, vendored code, binary download scripts, Dockerfiles, compose
files, Helm charts, Kubernetes manifests, Terraform, systemd units, CI
workflows, runbooks, SBOMs, and MCP or agent context-export docs.
2. Determine whether any controlled target uses:
- `github.com/openziti/zrok <=1.1.11`;
- `github.com/openziti/zrok/v2 <2.0.2`;
- a zrok binary or image below the fixed v2 release;
- `zrok share public --backend-mode drive` or equivalent WebDAV drive mode.
3. Determine exposure without contacting real shares:
- Is the share public or unauthenticated?
- Can DriveRoot contain symlinks from source trees, build outputs, archive
extraction, developer workspaces, agent workspaces, or mounted volumes?
- Can the zrok process read or write outside the intended DriveRoot?
- Could agents or MCP workflows use the share as a context or artifact root?
4. If zrok is absent or only appears in unrelated documentation, stop with
`TRIAGE.md` listing files checked and the likely runtime owner.
5. Prefer removal or disablement for any drive share that is not explicitly
required. Remove server registrations, startup commands, manifests, image
references, and runbook steps.
6. If zrok/v2 is required, upgrade to `2.0.2+`, regenerate Go module state,
images, SBOMs, deployment render output, and runtime docs.
7. If a v1 drive share is present, replace it with a supported fixed v2 path or
document why the repository cannot safely remediate it in `TRIAGE.md`.
8. Harden controlled deployments:
- use a dedicated empty DriveRoot;
- fail startup if DriveRoot contains symlinks;
- run zrok as a low-privilege service account;
- remove access to home directories, SSH keys, cloud credentials, package
tokens, model credentials, and production config;
- prefer read-only mounts unless write access is an explicit requirement;
- avoid public unauthenticated shares for agent or MCP context roots.
9. If this repository owns a fork, wrapper, or WebDAV backend code, add a safe
filesystem boundary helper:
- reject symlinks in every path component;
- resolve paths relative to a canonical DriveRoot;
- check the final target remains under DriveRoot after resolution;
- avoid time-of-check/time-of-use gaps for open, stat, mkdir, remove, and
write operations;
- use platform safe-open primitives where available.
10. Add safe tests using temporary directories only:
- a symlink inside DriveRoot pointing outside is rejected for read;
- the same symlink target is rejected for write/truncate;
- stat, mkdir, remove, and recursive remove cannot cross DriveRoot;
- normal files and directories inside DriveRoot still work;
- tests do not contact real zrok infrastructure.
11. Add a PR body section named `GHSA-74m3 operator actions` that states:
- every zrok version, binary, image, and drive share before and after;
- whether shares were public, unauthenticated, agent-facing, or MCP-facing;
- whether DriveRoot could contain symlinks;
- which credentials, files, or host paths were reachable by the zrok
process;
- which share access logs, filesystem audit logs, and deployment logs
should be reviewed for the exposure window;
- which secrets or keys should be rotated if exposure existed.
12. Run relevant validation: Go dependency resolution, unit tests, lint,
container build, deployment render, SBOM refresh, security scan, and any
zrok configuration validation available in this repository.
13. Use PR title:
`fix(sec): remediate zrok WebDAV DriveRoot symlink escape`.
## Stop conditions
- No zrok dependency, binary, image, drive share, or deployment target is
controlled by this repository.
- The repository uses unsupported zrok v1 drive shares and cannot move to a
fixed v2 runtime without an owner decision.
- The only verification path would require probing a real public share or
overwriting files outside a temporary test directory.
- Product requirements depend on sharing a source tree, home directory, or MCP
context store that can contain symlinks; document the risk and require a
security/product decision.
- Validation fails for unrelated pre-existing reasons; document those failures
instead of broadening scope.Verification - what the reviewer looks for
- No controlled
zrokv2 runtime resolves below2.0.2. - No controlled unsupported v1 drive share remains without a documented owner decision and containment plan.
- Public drive shares are removed or use a dedicated low-sensitivity DriveRoot.
- Symlinks in DriveRoot are rejected by startup checks, CI checks, or backend tests.
- Regression tests prove reads, writes, stats, directory creation, and removal cannot cross DriveRoot through symlinks.
- Operator actions identify exposed share URLs, logs to review, and credentials or host paths that may require rotation or cleanup.
Watch for
- Updating Go modules while old zrok binaries or images remain pinned in deployment manifests.
- Sharing a repository checkout, build workspace, home directory, mounted host root, or MCP context directory as DriveRoot.
- Checking only
..traversal and missing symlink traversal. - Removing symlinks once manually but failing to add a repeatable guard.
- Keeping public unauthenticated WebDAV shares for agent artifacts without explicit data classification and access policy.
References
- GitHub Advisory
GHSA-74m3-9qvm-rp9h: https://github.com/advisories/GHSA-74m3-9qvm-rp9h - zrok project: https://github.com/openziti/zrok