Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-50135

CVE-2026-50135: Gohugo Hugo Path Traversal Vulnerability

CVE-2026-50135 is a path traversal flaw in Gohugo Hugo that allows symlinks to read arbitrary files accessible to the Hugo user. This article covers the technical details, affected versions, impact, and mitigation.

Published:

CVE-2026-50135 Overview

CVE-2026-50135 is a symlink-following vulnerability in Hugo, the popular Go-based static site generator maintained by gohugo. Versions from 0.123.0 through 0.161.1 contain a regression in RootMappingFs.statRoot that calls Stat instead of Lstat, causing the filesystem layer to follow symbolic links during resource resolution. An attacker who can plant a symlink inside a local mount, such as a vendored themes/ directory, can use resources.Get to read arbitrary files accessible to the Hugo user. The issue is tracked under CWE-59: Improper Link Resolution Before File Access and is fixed in Hugo 0.162.0.

Critical Impact

A malicious theme or vendored dependency can exfiltrate arbitrary files readable by the Hugo build process, including source code, SSH keys, and CI secrets.

Affected Products

  • Hugo 0.123.0 through 0.161.1 (inclusive)
  • Local mounts and vendored themes under themes/
  • Any build pipeline invoking resources.Get against local filesystem mounts

Discovery Timeline

  • 2026-07-06 - CVE-2026-50135 published to NVD
  • 2026-07-08 - Last updated in NVD database

Technical Details for CVE-2026-50135

Vulnerability Analysis

Hugo abstracts filesystem access through a layered afero.Fs implementation. The RootMappingFs type maps virtual paths (such as themes/mytheme/...) to real filesystem locations. During resource resolution, RootMappingFs.statRoot previously invoked Stat, which transparently follows symbolic links to their targets. When a build encountered a symlink inside a mount pointing outside that mount, the returned FileInfo and subsequent Open call reflected the target rather than the link itself. A call to resources.Get("malicious-symlink") therefore returned the contents of the arbitrary file the symlink referenced.

Go-module themes fetched from GitHub are unaffected because the module proxy strips symlinks during archive extraction. Directory walks are also unaffected because Hugo uses Lstat-aware traversal in those paths. The exposure is limited to direct resources.Get calls against symlinks planted in local mounts.

Root Cause

The regression removed the Lstat semantics required for safe symlink handling. Stat resolves the target of a symbolic link and returns metadata about the destination, while Lstat returns metadata about the link itself. Using Stat in statRoot allowed the resolver to treat out-of-mount targets as legitimate in-mount resources, breaking the containment guarantee that mounts are supposed to provide.

Attack Vector

Exploitation requires an attacker to place a symlink into a location that Hugo mounts locally. Practical delivery vectors include a malicious or compromised third-party theme installed under themes/, a supply-chain injection into a vendored dependency, or a pull request adding a symlink to a repository that is then built by a CI/CD system. When Hugo processes the site, calling resources.Get against the planted symlink returns the target file's contents, which can then be embedded in generated pages or otherwise exfiltrated.

go
// Security patch in hugofs/decorators.go (Hugo v0.162.0)
// Adds LstatIfPossible so symlinks are not silently followed.
func (fs *baseFileDecoratorFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
    if lstater, ok := fs.Fs.(afero.Lstater); ok {
        fi, ok, err := lstater.LstatIfPossible(name)
        if err != nil {
            return nil, false, err
        }
        fim, err := fs.decorate(fi, name)
        if err != nil {
            return nil, false, err
        }
        return fim.(os.FileInfo), ok, nil
    }
    fi, err := fs.Stat(name)
    return fi, false, err
}

// Source: https://github.com/gohugoio/hugo/commit/f8b5fa09a64950c32b803821ede411ebfe772b7a

Detection Methods for CVE-2026-50135

Indicators of Compromise

  • Symbolic links inside themes/, assets/, static/, or other mounted directories whose targets resolve outside the site repository root
  • Hugo build artifacts containing unexpected file contents such as /etc/passwd, ~/.ssh/id_rsa, or CI environment files
  • Recent pull requests or commits introducing symlinks into vendored theme directories

Detection Strategies

  • Scan repositories with find . -type l -exec ls -l {} \; and verify that every link target stays within the mount boundary
  • Audit hugo version output across build agents to identify installations in the vulnerable range 0.123.0 to 0.161.1
  • Review generated site output for leaked credentials or filesystem paths that should not be public

Monitoring Recommendations

  • Alert on filesystem readlink or open syscalls issued by Hugo processes that resolve outside the project directory
  • Track dependency changes to Hugo themes, especially non-Go-module sources such as Git submodules and copied directories
  • Log and review Hugo build output for warnings referencing paths outside the configured content roots

How to Mitigate CVE-2026-50135

Immediate Actions Required

  • Upgrade Hugo to version 0.162.0 or later on all build machines and CI runners
  • Audit all local theme mounts and vendored assets for symbolic links pointing outside the project tree
  • Rotate any secrets that were readable by the Hugo build user if a vulnerable version processed untrusted content

Patch Information

The fix is committed in gohugoio/hugo commit f8b5fa0 and released in Hugo v0.162.0. The patch introduces LstatIfPossible on the decorator filesystem and routes statRoot through it, restoring correct symlink handling. Full details are available in GitHub Security Advisory GHSA-fw87-fv5r-9fpw.

Workarounds

  • Prefer Go-module themes over locally vendored themes, since the module proxy strips symlinks during extraction
  • Run Hugo builds under an unprivileged user account with access limited to the project directory
  • Remove or reject symlinks in pre-commit hooks and CI validation before invoking hugo
bash
# Upgrade Hugo and verify the patched version
go install github.com/gohugoio/hugo@v0.162.0
hugo version

# Enumerate symlinks in the site and flag out-of-tree targets
find . -type l -print0 | while IFS= read -r -d '' link; do
    target=$(readlink -f "$link")
    case "$target" in
        "$PWD"/*) ;;
        *) echo "OUT-OF-TREE symlink: $link -> $target" ;;
    esac
done

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.