Every CVE Search/ Get API key

CVE-2026-76839

Grav before 2.0.16 Information Disclosure via offsetGet

8.7 HighFix available

Grav before 2.0.16 allows sandboxed Twig templates to access sensitive User fields through allow-listed offsetGet() and offsetexists() methods that lack field filtering. Attackers with page-edit permissions can call offsetGet() on User objects to extract hashed passwords and 2FA secrets, enabling offline password cracking and authentication bypass.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
High
Integrity
None
Availability
None

Exploit likelihood

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

What each source reports

CVE List Score used 8.7

Grav before 2.0.16 Information Disclosure via offsetGet

Grav before 2.0.16 allows sandboxed Twig templates to access sensitive User fields through allow-listed offsetGet() and offsetexists() methods that lack field filtering. Attackers with page-edit permissions can call offsetGet() on User objects to extract hashed passwords and 2FA secrets, enabling offline password cracking and authentication bypass.

Record
CVE-2026-76839
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/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-522
Updated
2026-11-02
EUVDEUVD-2026-65219 8.7

Grav before 2.0.16 allows sandboxed Twig templates to access sensitive User fields through allow-listed offsetGet() and offsetexists() methods that lack field filtering. Attackers with page-edit permissions can call offsetGet() on User objects to extract hashed passwords and 2FA secrets, enabling offline password cracking and authentication bypass.

Record
EUVD-2026-65219
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-3jhr-mxmx-38cx 8.7

Grav: UserInterface offsetget/offsetexists allow-listed in Twig sandbox let editor-authored content leak hashed_password and 2FA secrets via offsetGet()

Summary

system/config/security.yaml's Twig sandbox policy allow-lists offsetget and offsetexists for Grav\Common\User\Interfaces\UserInterface. The concrete Grav\Common\User\DataUser\User class does not filter which fields offsetGet() returns, so any sandboxed template with access to a User object can read hashed_password, secret (2FA seed), and twofa_secret directly, bypassing the redaction Grav's own code applies everywhere else.

The core evidence, from Grav's own code

system/src/Grav/Common/User/DataUser/User.php:

/**
 * {@inheritdoc}
 * Override to filter out sensitive fields like password hashes
 */
public function jsonSerialize(): array
{
    $items = parent::jsonSerialize();

    // Security: Remove sensitive fields that should never be exposed to frontend
    unset($items['hashed_password']);
    unset($items['secret']);         // 2FA secret
    unset($items['twofa_secret']);   // Alternative 2FA field name

    return $items;
}

public function offsetGet($offset)
{
    $value = parent::offsetGet($offset);
    // only special-cases 'authorized', nothing else -- no redaction
    return $value;
}

system/config/security.yaml:

- class: 'Grav\Common\User\Interfaces\UserInterface'
  methods: 'authorize, authorized, authenticated, username, fullname, email, language, offsetget, offsetexists'

This is the same vulnerability shape as two already-fixed issues in this file (GHSA-j274-39qw-32c9 and GHSA-mc5q-6hpj-rp7j -- both a raw, unfiltered data-access path bypassing an intended redaction) recurring on a third class neither fix covered.

Live, end-to-end verification

Built a real Twig\Environment wired with the real Twig\Extension\SandboxExtension, policed by Grav's own GravSecurityPolicy class, constructed directly from values parsed out of the actual system/config/security.yaml (via Symfony\Component\Yaml\Yaml::parseFile, not a hand-copied excerpt), rendering real template strings against a real User object.

Environment setup:

git clone https://github.com/getgrav/grav.git
cd grav
apt-get install -y php8.3-curl php8.3-zip php8.3-xml php8.3-gd
curl -sL -o /tmp/composer.phar \
  "https://github.com/composer/composer/releases/latest/download/composer.phar"
COMPOSER_ALLOW_SUPERUSER=1 php /tmp/composer.phar install --no-dev --no-interaction

live_sandbox_render_test.php:

<?php
require 'vendor/autoload.php';

use Symfony\Component\Yaml\Yaml;
use Twig\Environment;
use Twig\Loader\ArrayLoader;
use Twig\Extension\SandboxExtension;
use Grav\Common\Twig\Sandbox\GravSecurityPolicy;
use Grav\Common\User\DataUser\User;

$securityYaml = Yaml::parseFile('system/config/security.yaml');
$sandboxCfg = $securityYaml['twig_sandbox'];

function rowsToMap(array $rows): array {
    $out = [];
    foreach ($rows as $row) {
        $out[$row['class']] = array_map('strtolower', array_map('trim', explode(',', $row['methods'])));
    }
    return $out;
}

$policy = new GravSecurityPolicy(
    $sandboxCfg['allowed_tags'],
    $sandboxCfg['allowed_filters'],
    rowsToMap($sandboxCfg['allowed_methods']),
    rowsToMap($sandboxCfg['allowed_properties']),
    $sandboxCfg['allowed_functions']
);
$sandbox = new SandboxExtension($policy, true);

$user = new User([
    'username'        => 'admin',
    'hashed_password' => '$2y$10$REALBCRYPTHASHVALUEshouldnotleakXXXXXXXXXXXXXXXXXXXXX',
    'secret'          => 'JBSWY3DPEHPK3PXP',
    'twofa_secret'    => 'ALT2FASECRETVALUE9999',
]);

function tryRender(string $label, string $template, SandboxExtension $sandbox, User $user): void {
    $twig = new Environment(new ArrayLoader(['@Page:test' => $template]));
    $twig->addExtension($sandbox);
    try {
        echo "$label => " . $twig->render('@Page:test', ['user' => $user]) . "\n";
    } catch (\Twig\Sandbox\SecurityError $e) {
        echo "$label => BLOCKED: " . $e->getMessage() . "\n";
    }
}

tryRender('hashed_password via offsetGet()', "{{…
Record
GHSA-3jhr-mxmx-38cx
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
CVSS 3.1
7.7 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N
Weakness
CWE-522
Updated
2026-11-02
GitHub AdvisoriesGHSA-8vp7-8q4w-vv7m 8.7

Duplicate Advisory: Grav: UserInterface offsetget/offsetexists allow-listed in Twig sandbox let editor-authored content leak hashed_password and 2FA secrets via offsetGet()

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-3jhr-mxmx-38cx. This link is maintained to preserve external references.

Original Description

Grav before 2.0.16 allows sandboxed Twig templates to access sensitive User fields through allow-listed offsetGet() and offsetexists() methods that lack field filtering. Attackers with page-edit permissions can call offsetGet() on User objects to extract hashed passwords and 2FA secrets, enabling offline password cracking and authentication bypass.

Record
GHSA-8vp7-8q4w-vv7m
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/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-522
Updated
2026-11-02
NVD 8.7

Grav before 2.0.16 allows sandboxed Twig templates to access sensitive User fields through allow-listed offsetGet() and offsetexists() methods that lack field filtering. Attackers with page-edit permissions can call offsetGet() on User objects to extract hashed passwords and 2FA secrets, enabling offline password cracking and authentication bypass.

Record
CVE-2026-76839
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/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-522
Updated
2026-11-02
OSV 8.7

Grav before 2.0.16 Information Disclosure via offsetGet

Grav before 2.0.16 allows sandboxed Twig templates to access sensitive User fields through allow-listed offsetGet() and offsetexists() methods that lack field filtering. Attackers with page-edit permissions can call offsetGet() on User objects to extract hashed passwords and 2FA secrets, enabling offline password cracking and authentication bypass.

Record
CVE-2026-76839
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-522
Updated
2026-11-02
OSVGHSA-3jhr-mxmx-38cx 8.7

Grav: UserInterface offsetget/offsetexists allow-listed in Twig sandbox let editor-authored content leak hashed_password and 2FA secrets via offsetGet()

Summary

system/config/security.yaml's Twig sandbox policy allow-lists offsetget and offsetexists for Grav\Common\User\Interfaces\UserInterface. The concrete Grav\Common\User\DataUser\User class does not filter which fields offsetGet() returns, so any sandboxed template with access to a User object can read hashed_password, secret (2FA seed), and twofa_secret directly, bypassing the redaction Grav's own code applies everywhere else.

The core evidence, from Grav's own code

system/src/Grav/Common/User/DataUser/User.php:

/**
 * {@inheritdoc}
 * Override to filter out sensitive fields like password hashes
 */
public function jsonSerialize(): array
{
    $items = parent::jsonSerialize();

    // Security: Remove sensitive fields that should never be exposed to frontend
    unset($items['hashed_password']);
    unset($items['secret']);         // 2FA secret
    unset($items['twofa_secret']);   // Alternative 2FA field name

    return $items;
}

public function offsetGet($offset)
{
    $value = parent::offsetGet($offset);
    // only special-cases 'authorized', nothing else -- no redaction
    return $value;
}

system/config/security.yaml:

- class: 'Grav\Common\User\Interfaces\UserInterface'
  methods: 'authorize, authorized, authenticated, username, fullname, email, language, offsetget, offsetexists'

This is the same vulnerability shape as two already-fixed issues in this file (GHSA-j274-39qw-32c9 and GHSA-mc5q-6hpj-rp7j -- both a raw, unfiltered data-access path bypassing an intended redaction) recurring on a third class neither fix covered.

Live, end-to-end verification

Built a real Twig\Environment wired with the real Twig\Extension\SandboxExtension, policed by Grav's own GravSecurityPolicy class, constructed directly from values parsed out of the actual system/config/security.yaml (via Symfony\Component\Yaml\Yaml::parseFile, not a hand-copied excerpt), rendering real template strings against a real User object.

Environment setup:

git clone https://github.com/getgrav/grav.git
cd grav
apt-get install -y php8.3-curl php8.3-zip php8.3-xml php8.3-gd
curl -sL -o /tmp/composer.phar \
  "https://github.com/composer/composer/releases/latest/download/composer.phar"
COMPOSER_ALLOW_SUPERUSER=1 php /tmp/composer.phar install --no-dev --no-interaction

live_sandbox_render_test.php:

<?php
require 'vendor/autoload.php';

use Symfony\Component\Yaml\Yaml;
use Twig\Environment;
use Twig\Loader\ArrayLoader;
use Twig\Extension\SandboxExtension;
use Grav\Common\Twig\Sandbox\GravSecurityPolicy;
use Grav\Common\User\DataUser\User;

$securityYaml = Yaml::parseFile('system/config/security.yaml');
$sandboxCfg = $securityYaml['twig_sandbox'];

function rowsToMap(array $rows): array {
    $out = [];
    foreach ($rows as $row) {
        $out[$row['class']] = array_map('strtolower', array_map('trim', explode(',', $row['methods'])));
    }
    return $out;
}

$policy = new GravSecurityPolicy(
    $sandboxCfg['allowed_tags'],
    $sandboxCfg['allowed_filters'],
    rowsToMap($sandboxCfg['allowed_methods']),
    rowsToMap($sandboxCfg['allowed_properties']),
    $sandboxCfg['allowed_functions']
);
$sandbox = new SandboxExtension($policy, true);

$user = new User([
    'username'        => 'admin',
    'hashed_password' => '$2y$10$REALBCRYPTHASHVALUEshouldnotleakXXXXXXXXXXXXXXXXXXXXX',
    'secret'          => 'JBSWY3DPEHPK3PXP',
    'twofa_secret'    => 'ALT2FASECRETVALUE9999',
]);

function tryRender(string $label, string $template, SandboxExtension $sandbox, User $user): void {
    $twig = new Environment(new ArrayLoader(['@Page:test' => $template]));
    $twig->addExtension($sandbox);
    try {
        echo "$label => " . $twig->render('@Page:test', ['user' => $user]) . "\n";
    } catch (\Twig\Sandbox\SecurityError $e) {
        echo "$label => BLOCKED: " . $e->getMessage() . "\n";
    }
}

tryRender('hashed_password via offsetGet()', "{{…
Record
GHSA-3jhr-mxmx-38cx
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
CVSS 3.1
7.7 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N
Weakness
CWE-522
Updated
2026-11-02
OSVGHSA-8vp7-8q4w-vv7m 8.7

Duplicate Advisory: Grav: UserInterface offsetget/offsetexists allow-listed in Twig sandbox let editor-authored content leak hashed_password and 2FA secrets via offsetGet()

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-3jhr-mxmx-38cx. This link is maintained to preserve external references.

Original Description

Grav before 2.0.16 allows sandboxed Twig templates to access sensitive User fields through allow-listed offsetGet() and offsetexists() methods that lack field filtering. Attackers with page-edit permissions can call offsetGet() on User objects to extract hashed passwords and 2FA secrets, enabling offline password cracking and authentication bypass.

Record
GHSA-8vp7-8q4w-vv7m
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/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-522
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-76839 is a high vulnerability (CVSS 8.7) 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.34%, the 27th 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