CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Dashboard
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-MQPR-49JJ-32RC
6.54.00%

The Automation Trap: Forging GitHub Webhooks in n8n

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 26, 2026·5 min read·7 visits

PoC Available

Executive Summary (TL;DR)

n8n's GitHub Trigger node didn't verify HMAC signatures. If an attacker knew your webhook URL (which was often leaked via shared workflow JSONs), they could trigger your automation pipelines with fake data. Fixed in 1.123.15, but requires manual intervention to secure existing workflows.

A critical look at how n8n, the popular workflow automation tool, left the door wide open for webhook forgery. For years, the GitHub Trigger node failed to verify cryptographic signatures, allowing anyone who guessed the webhook URL to masquerade as GitHub. Combined with a flaw where webhook IDs were preserved during workflow copying, this vulnerability created a perfect storm for unauthorized workflow execution and potential lateral movement.

The Hook: Trust, But Don't Verify

n8n is the darling of the self-hosted automation world. It’s the glue that holds together the disparate APIs of modern devops infrastructure. You push code to GitHub, n8n catches the webhook, runs some tests, updates a Slack channel, and maybe deploys a container. It’s magic. But magic often relies on illusion, and in this case, the illusion was security.

For a long time, the GitHub Trigger node in n8n operated on the "honor system." When you set up a webhook in GitHub, you are supposed to define a secret. GitHub uses this secret to hash the payload and sends it in the X-Hub-Signature-256 header. The receiver (n8n) is supposed to re-hash the payload and check if it matches.

Here’s the punchline: n8n didn't check. It ignored the signature entirely. It was like a bouncer who checks if you're on the list but doesn't check your ID. As long as you knew where the party was (the webhook URL), you could walk right in claiming to be the CEO.

The Flaw: The Xerox Effect

The vulnerability is actually a two-headed monster. The first head is the missing signature verification we just mentioned. But the second head is what made this truly dangerous: Predictable Identifiers.

n8n workflows are defined in JSON. Developers love to share them. "Hey, check out this cool CI/CD pipeline I built!" Copy JSON, Paste into Gist. When another user imported that JSON, n8n helpfully preserved the webhookId—the unique path component of the URL. This meant that if you imported a workflow, you were often listening on the exact same endpoint path as the original author (or anyone else who copied it).

Because there was no cryptographic verification, the only thing stopping an attacker was the secrecy of the URL. But if the URL is hardcoded in a public Gist, or if the webhookId is short and guessable, that protection evaporates. It’s security by obscurity, where the obscurity is printed on a billboard.

The Code: The Smoking Gun

Let's look at the fix to understand the breakage. The remediation required changes in both the backend (to actually check signatures) and the frontend (to stop copying webhook IDs).

In the backend, the fix (Commit afe32232) finally introduced a verifySignature helper. Before this code existed, the node simply accepted the request. Now, it performs a standard HMAC-SHA256 check.

> [!NOTE] > Notice the use of timingSafeEqual. This is crucial. A standard string comparison (==) is susceptible to timing attacks, where an attacker can guess the signature byte-by-byte based on how long the server takes to reject it.

// packages/nodes-base/nodes/Github/GithubTriggerHelpers.ts
 
export function verifySignature(this: IWebhookFunctions): boolean {
    const webhookData = this.getWorkflowStaticData('node');
    const webhookSecret = webhookData.webhookSecret as string | undefined;
 
    // THE LEGACY TRAP: If no secret exists, fail open (allow access)
    // This is for backward compatibility, but it's dangerous.
    if (!webhookSecret) return true;
 
    const req = this.getRequestObject();
    const signature = req.header('x-hub-signature-256');
    if (!signature || !signature.startsWith('sha256=')) return false;
 
    const providedSignature = signature.substring(7);
    const hmac = createHmac('sha256', webhookSecret);
    
    // Hash the raw body
    hmac.update(Buffer.isBuffer(req.rawBody) ? req.rawBody : JSON.stringify(req.rawBody));
    
    return timingSafeEqual(Buffer.from(hmac.digest('hex')), Buffer.from(providedSignature));
}

The frontend fix (Commit a19347a6) ensures that when you paste a node, a new UUID is generated for the webhook, breaking the "Xerox Effect."

The Exploit: Fake It 'Til You Make It

Exploiting this is trivially easy if you have the URL. Let's say you found a leaked n8n workflow URL for a company's deployment pipeline: https://n8n.corp.target/webhook/github-deploy.

You want to trick their n8n instance into thinking the main branch was just updated, perhaps triggering a build that pulls from your malicious repository (if the workflow logic is flawed) or simply poisoning their logs/databases.

The Attack Chain:

The Payload:

You don't even need to calculate a signature because the vulnerable server ignores it. You just need curl:

curl -X POST https://target-n8n.com/webhook/1234-5678 \
     -H "Content-Type: application/json" \
     -H "X-GitHub-Event: push" \
     -d '{
           "ref": "refs/heads/main",
           "repository": {
             "name": "production-api",
             "full_name": "company/production-api"
           },
           "commits": [
             {
               "id": "malicious-commit-hash",
               "message": "Legit update",
               "author": { "name": "admin" }
             }
           ]
         }'

If the workflow uses the commit message to generate changelogs or sends the commit author an email with internal secrets, you own that process now.

The Mitigation: The Toggle of Destiny

Upgrading to n8n version 1.123.15 or 2.5.0 is the first step, but it is NOT sufficient. This is where many administrators will fail.

Remember that line in the code analysis? if (!webhookSecret) return true;. This is the backward compatibility bridge. When you update n8n, your existing workflows do not have a secret generated yet. They are still running in "Legacy Mode," effectively failing open to ensure your automations don't break overnight.

To actually fix the vulnerability, you must:

  1. Update the n8n instance.
  2. Open every workflow containing a GitHub Trigger node.
  3. Deactivate the workflow.
  4. Reactivate the workflow.

Only upon reactivation does the new code generate a cryptographically secure secret and register it with GitHub. Until you do this, you are still vulnerable, even on the latest version. It's a UX compromise that favors uptime over security default—a classic trade-off.

Official Patches

n8nOfficial n8n Release Notes

Fix Analysis (2)

Technical Appendix

CVSS Score
6.5/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L
EPSS Probability
4.00%
Top 35% most exploited
15,000
Estimated exposed hosts via Shodan

Affected Systems

n8n workflow automation platform (self-hosted)n8n (npm package) < 1.123.15n8n (npm package) 2.x < 2.5.0

Affected Versions Detail

Product
Affected Versions
Fixed Version
n8n
n8n.io
< 1.123.151.123.15
n8n
n8n.io
2.x < 2.5.02.5.0
AttributeDetail
CWE IDCWE-345
Attack VectorNetwork
CVSS6.5 (Moderate)
ImpactIntegrity & Confidentiality
Exploit StatusPoC Available
RiskWebhook Forgery

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1566Phishing (via Webhook)
Initial Access
CWE-345
Insufficient Verification of Data Authenticity

Insufficient Verification of Data Authenticity

Known Exploits & Detection

ManualExploitable via standard HTTP clients (curl/Postman) by guessing the webhook ID.

Vulnerability Timeline

Frontend fix committed (ID regeneration)
2026-01-13
Backend fix committed (Signature verification)
2026-01-15
Advisory GHSA-MQPR-49JJ-32RC Published
2026-01-20

References & Sources

  • [1]n8n Security Advisory
  • [2]n8n GitHub Trigger Documentation

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.