Every CVE Search/ Get API key

CVE-2026-72697

Grav CMS before 2.0.16 Path Traversal via media_directory

7.1 HighFix available

Grav CMS before 2.0.16 contains a path traversal vulnerability in the media_directory() Twig function that fails to validate filesystem paths, allowing authenticated users to enumerate and access files outside intended scope. Attackers with page authoring privileges can supply arbitrary filesystem paths to media_directory() and use the allow-listed filepath accessor on Medium objects to read file contents of any file matching configured media extensions that the web server process can access.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
Low
User interaction
None

What an attacker can affect

Confidentiality
High
Integrity
None
Availability
None

Exploit likelihood

0.38%Up 0.1 points since Aug 25, 2026
90%50%10%1%

What each source reports

CVE List Score used 7.1

Grav CMS before 2.0.16 Path Traversal via media_directory

Grav CMS before 2.0.16 contains a path traversal vulnerability in the media_directory() Twig function that fails to validate filesystem paths, allowing authenticated users to enumerate and access files outside intended scope. Attackers with page authoring privileges can supply arbitrary filesystem paths to media_directory() and use the allow-listed filepath accessor on Medium objects to read file contents of any file matching configured media extensions that the web server process can access.

Record
CVE-2026-72697
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-22
Updated
2026-11-02
EUVDEUVD-2026-65211 7.1

Grav CMS before 2.0.16 contains a path traversal vulnerability in the media_directory() Twig function that fails to validate filesystem paths, allowing authenticated users to enumerate and access files outside intended scope. Attackers with page authoring privileges can supply arbitrary filesystem paths to media_directory() and use the allow-listed filepath accessor on Medium objects to read file contents of any file matching configured media extensions that the web server process can access.

Record
EUVD-2026-65211
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-47ch-6w46-6xm7 7.1

Grav: media_directory() Twig function allows filesystem path traversal and file content disclosure from sandboxed page content

Summary

The media_directory() Twig function is allow-listed for use in sandboxed, editor-authored page content (system/config/security.yaml). Its implementation, GravExtension::mediaDirFunc(), only treats the input as unsafe when it looks like a Grav stream (user://, theme://, etc). If the input is instead a plain filesystem path, absolute or relative, the stream check is skipped entirely and the raw string is handed straight to new Media($media_dir), which lists every file in that directory whose extension matches a configured media type (which by default includes txt, json, xml, pdf, doc, docx, and more, not just images) and builds Medium objects for them.

Separately, the sandbox's own allow-list for the Medium class includes the filepath accessor. A code comment directly above that allow-list entry states the developers' intent was for filepath to be part of the "dangerous surface" that "stays blocked", but it is listed as an allowed method on the very same line, contradicting that stated intent.

Combined, a user who can enter page content that gets processed as Twig (process.twig: true in frontmatter, or any modular page, which is unsandboxed and unconditional per the code comment in processPage()) can point media_directory() at any directory the web server process can read, anywhere on the filesystem, and both enumerate and read the content of any file in it whose extension is a recognized media type.

Affected product and version

Product: Grav CMS, getgrav/grav Confirmed present in: 2.0.15, commit c2b46866857a93a0aa7048e7ed707ed3ed45dbc3

Affected code

system/src/Grav/Common/Twig/Extension/GravExtension.php, mediaDirFunc():

public function mediaDirFunc($media_dir)
{
    /** @var UniformResourceLocator $locator */
    $locator = $this->grav['locator'];

    if ($locator->isStream($media_dir)) {
        $media_dir = $locator->findResource($media_dir);
    }

    if ($media_dir && file_exists($media_dir)) {
        return new Media($media_dir);
    }

    return null;
}

There is no check that $media_dir, when it is not a recognized stream, is contained within any site-relative root. It is used exactly as supplied.

system/src/Grav/Common/Page/Media.php, init(), called from the constructor:

protected function init()
{
    $path = $this->getPath();

    // Handle special cases where page doesn't exist in filesystem.
    if (!$path || !is_dir($path)) {
        return;
    }
    ...
    $iterator = new FilesystemIterator($path, FilesystemIterator::UNIX_PATHS | FilesystemIterator::SKIP_DOTS);

    foreach ($iterator as $file => $info) {
        ...
        [$basename, $ext, $type, $extra] = $this->getFileParts($filename);
        if (!in_array(strtolower((string) $ext), $media_types, true)) {
            continue;
        }
        ...
    }

$path here is whatever was passed to the Media constructor, the raw, unvalidated string from mediaDirFunc().

system/config/security.yaml, the Medium sandbox allow-list and the comment directly above it:

    # ...
    # dangerous surface (save, set, copy, deleteFile, toArray, filepath, …) is
    # absent from ALLOWED_ACTIONS and stays blocked.
    - class: 'Grav\Common\Page\Medium\Medium'
      methods: 'url, html, filepath, filename, metadata, srcset, parsedownelement, __tostring, @media_actions'

filepath is named in the comment as something that is supposed to stay blocked, and is then listed as an allowed method one line later.

media_directory in the sandbox's function allow-list:

    - media_directory

Root cause

Two gaps, and the second one converts the first from "list filenames from a directory" into "read the content of files":

  1. mediaDirFunc()'s containment check only fires for recognized Grav streams. A plain filesystem path, which is the normal, documented shape of a string, is not a stream by definition, so it always takes the unchecked path.
  2. Once a…
Record
GHSA-47ch-6w46-6xm7
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-22
Updated
2026-11-02
GitHub AdvisoriesGHSA-rj4c-4q9x-543x 7.1

Duplicate Advisory: Grav: media_directory() Twig function allows filesystem path traversal and file content disclosure from sandboxed page content

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-47ch-6w46-6xm7. This link is maintained to preserve external references.

Original Description

Grav CMS before 2.0.16 contains a path traversal vulnerability in the media_directory() Twig function that fails to validate filesystem paths, allowing authenticated users to enumerate and access files outside intended scope. Attackers with page authoring privileges can supply arbitrary filesystem paths to media_directory() and use the allow-listed filepath accessor on Medium objects to read file contents of any file matching configured media extensions that the web server process can access.

Record
GHSA-rj4c-4q9x-543x
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-22
Updated
2026-11-02
NVD 7.1

Grav CMS before 2.0.16 contains a path traversal vulnerability in the media_directory() Twig function that fails to validate filesystem paths, allowing authenticated users to enumerate and access files outside intended scope. Attackers with page authoring privileges can supply arbitrary filesystem paths to media_directory() and use the allow-listed filepath accessor on Medium objects to read file contents of any file matching configured media extensions that the web server process can access.

Record
CVE-2026-72697
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-22
Updated
2026-11-02
OSV 7.1

Grav CMS before 2.0.16 Path Traversal via media_directory

Grav CMS before 2.0.16 contains a path traversal vulnerability in the media_directory() Twig function that fails to validate filesystem paths, allowing authenticated users to enumerate and access files outside intended scope. Attackers with page authoring privileges can supply arbitrary filesystem paths to media_directory() and use the allow-listed filepath accessor on Medium objects to read file contents of any file matching configured media extensions that the web server process can access.

Record
CVE-2026-72697
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-22
Updated
2026-11-02
OSVGHSA-47ch-6w46-6xm7 7.1

Grav: media_directory() Twig function allows filesystem path traversal and file content disclosure from sandboxed page content

Summary

The media_directory() Twig function is allow-listed for use in sandboxed, editor-authored page content (system/config/security.yaml). Its implementation, GravExtension::mediaDirFunc(), only treats the input as unsafe when it looks like a Grav stream (user://, theme://, etc). If the input is instead a plain filesystem path, absolute or relative, the stream check is skipped entirely and the raw string is handed straight to new Media($media_dir), which lists every file in that directory whose extension matches a configured media type (which by default includes txt, json, xml, pdf, doc, docx, and more, not just images) and builds Medium objects for them.

Separately, the sandbox's own allow-list for the Medium class includes the filepath accessor. A code comment directly above that allow-list entry states the developers' intent was for filepath to be part of the "dangerous surface" that "stays blocked", but it is listed as an allowed method on the very same line, contradicting that stated intent.

Combined, a user who can enter page content that gets processed as Twig (process.twig: true in frontmatter, or any modular page, which is unsandboxed and unconditional per the code comment in processPage()) can point media_directory() at any directory the web server process can read, anywhere on the filesystem, and both enumerate and read the content of any file in it whose extension is a recognized media type.

Affected product and version

Product: Grav CMS, getgrav/grav Confirmed present in: 2.0.15, commit c2b46866857a93a0aa7048e7ed707ed3ed45dbc3

Affected code

system/src/Grav/Common/Twig/Extension/GravExtension.php, mediaDirFunc():

public function mediaDirFunc($media_dir)
{
    /** @var UniformResourceLocator $locator */
    $locator = $this->grav['locator'];

    if ($locator->isStream($media_dir)) {
        $media_dir = $locator->findResource($media_dir);
    }

    if ($media_dir && file_exists($media_dir)) {
        return new Media($media_dir);
    }

    return null;
}

There is no check that $media_dir, when it is not a recognized stream, is contained within any site-relative root. It is used exactly as supplied.

system/src/Grav/Common/Page/Media.php, init(), called from the constructor:

protected function init()
{
    $path = $this->getPath();

    // Handle special cases where page doesn't exist in filesystem.
    if (!$path || !is_dir($path)) {
        return;
    }
    ...
    $iterator = new FilesystemIterator($path, FilesystemIterator::UNIX_PATHS | FilesystemIterator::SKIP_DOTS);

    foreach ($iterator as $file => $info) {
        ...
        [$basename, $ext, $type, $extra] = $this->getFileParts($filename);
        if (!in_array(strtolower((string) $ext), $media_types, true)) {
            continue;
        }
        ...
    }

$path here is whatever was passed to the Media constructor, the raw, unvalidated string from mediaDirFunc().

system/config/security.yaml, the Medium sandbox allow-list and the comment directly above it:

    # ...
    # dangerous surface (save, set, copy, deleteFile, toArray, filepath, …) is
    # absent from ALLOWED_ACTIONS and stays blocked.
    - class: 'Grav\Common\Page\Medium\Medium'
      methods: 'url, html, filepath, filename, metadata, srcset, parsedownelement, __tostring, @media_actions'

filepath is named in the comment as something that is supposed to stay blocked, and is then listed as an allowed method one line later.

media_directory in the sandbox's function allow-list:

    - media_directory

Root cause

Two gaps, and the second one converts the first from "list filenames from a directory" into "read the content of files":

  1. mediaDirFunc()'s containment check only fires for recognized Grav streams. A plain filesystem path, which is the normal, documented shape of a string, is not a stream by definition, so it always takes the unchecked path.
  2. Once a…
Record
GHSA-47ch-6w46-6xm7
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-22
Updated
2026-11-02
OSVGHSA-rj4c-4q9x-543x 7.1

Duplicate Advisory: Grav: media_directory() Twig function allows filesystem path traversal and file content disclosure from sandboxed page content

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-47ch-6w46-6xm7. This link is maintained to preserve external references.

Original Description

Grav CMS before 2.0.16 contains a path traversal vulnerability in the media_directory() Twig function that fails to validate filesystem paths, allowing authenticated users to enumerate and access files outside intended scope. Attackers with page authoring privileges can supply arbitrary filesystem paths to media_directory() and use the allow-listed filepath accessor on Medium objects to read file contents of any file matching configured media extensions that the web server process can access.

Record
GHSA-rj4c-4q9x-543x
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-22
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
getgrav/grav
Packagist
through 2.0.15
2.0.16
getgrav/grav
Packagist
before 2.0.16, plus listed versions
0.8.00.9.00.9.10.9.100.9.11
Show 45 moreShow less
0.9.120.9.130.9.140.9.150.9.160.9.170.9.180.9.190.9.20.9.200.9.210.9.220.9.230.9.240.9.250.9.260.9.270.9.280.9.290.9.30.9.300.9.310.9.320.9.330.9.340.9.350.9.360.9.370.9.380.9.390.9.40.9.400.9.410.9.420.9.430.9.440.9.450.9.50.9.60.9.70.9.80.9.91.0.01.0.0-rc.11.0.0-rc.2
2.0.16

History

History
RecordedChangedSources
affected
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-72697 is a high vulnerability (CVSS 7.1) in Getgrav Grav, published August 25, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.38%, the 31st percentile. Fixed in getgrav/grav 2.0.16.

References

This page doesn't list everything

Left out here: 300 versions and the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open