Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# proto-file: proto/templated_plugin.proto
# proto-message: TemplatedPlugin

###############
# PLUGIN INFO #
###############

info: {
type: VULN_DETECTION
name: "N8N_ExposedRestApi"
author: "yuradoc (yuradoc.research@gmail.com)"
version: "1.0"
}

finding: {
main_id: {
publisher: "GOOGLE"
value: "N8N_EXPOSED_REST_API"
}
title: "Exposed n8n REST API"
description:
"The n8n automation platform instance exposes its REST API without proper "
"authentication, allowing unauthenticated access to workflow management "
"functionality. This may enable attackers to enumerate, modify, and execute "
"workflows, including those containing built-in command execution nodes, "
"potentially leading to remote code execution. Exposure conditions vary "
"across n8n versions. Early releases without user management did not "
"consistently protect REST endpoints, and available authentication mechanisms "
"(e.g., JWT or basic auth) may not fully cover all /rest/* routes. In later "
"0.x versions, authentication behavior may be inconsistent—for example, "
"instances without an initialized owner account or with ineffective "
"configuration flags may allow unauthenticated access to /rest/* endpoints."
recommendation:
"Ensure authentication is properly enforced for the n8n instance and that it "
"is not exposed to untrusted networks. Verify that REST API endpoints are "
"protected, especially in older or partially initialized deployments. Refer "
"to https://docs.n8n.io/hosting/securing/overview/ for guidance."
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think part of the recommendation should be to upgrade the n8n version since this only detects older versions

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated!

severity: CRITICAL
}

###########
# ACTIONS #
###########

actions: {
name: "n8n_vuln_fingerprint"
http_request: {
method: GET
uri: "/rest/settings"
response: {
http_status: 200
expect_all: {
conditions: [
{ body: {} contains: '"urlBaseWebhook"' },
{ body: {} contains: '"versionCli":"0.' }
]
}
}
}
}

actions: {
name: "n8n_retrieve_auth_cookie"
http_request: {
method: GET
uri: "/rest/login"
response: {
extract_any: {
patterns: [
{
from_header: { name: "Set-Cookie" }
regexp: "(n8n-auth=[^;]+)"
variable_name: "n8n_auth_token"
},
{
from_body: {}
regexp: "()"
}
]
}
}
}
}

actions: {
name: "n8n_retrieve_existing_flows"
http_request: {
method: GET
uri: "/rest/workflows"
headers: [
{ name: "Cookie" value: "{{ n8n_auth_token }}" }
]
response: {
http_status: 200
expect_all: {
conditions: [
{ body: {} contains: '"data":[' }
]
}
}
}
}

#############
# WORKFLOWS #
#############

workflows: {
actions: [
"n8n_vuln_fingerprint",
"n8n_retrieve_auth_cookie",
"n8n_retrieve_existing_flows"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# proto-file: proto/templated_plugin_tests.proto
# proto-message: TemplatedPluginTests

config: {
tested_plugin: "N8N_ExposedRestApi"
}

tests: {
name: "whenVulnerable_noOwner_returnsTrue"
expect_vulnerability: true

mock_http_server: {
mock_responses: [
{
uri: "/rest/settings"
status: 200
body_content: '{"data":{"urlBaseWebhook":"http://target:5678/","versionCli":"0.237.0",...}}'
},
{
uri: "/rest/login"
status: 200
headers: [
{ name: "Set-Cookie" value: "n8n-auth=jwt_token" }
]
},
{
uri: "/rest/workflows"
status: 200
condition: {
headers: [
{ name: "Cookie" value: "n8n-auth=jwt_token" }
]
}
body_content: '{"data":[{"id":"Q2cpznBmI6NCfeBF","name":"Vulnerable Workflow",...},...]}'
}
]
}
}

tests: {
name: "whenVulnerable_noUserManagement_returnsTrue"
expect_vulnerability: true

mock_http_server: {
mock_responses: [
{
uri: "/rest/settings"
status: 200
body_content: '{"data":{"urlBaseWebhook":"http://target:5678/","versionCli":"0.54.0",...}}'
},
{
uri: "/rest/login"
status: 404
},
{
uri: "/rest/workflows"
status: 200
body_content: '{"data":[{"id":"1","name":"Legacy Workflow",...},...]}'
}
]
}
}

tests: {
name: "whenNotVulnerable_restProtected_returnsFalse"
expect_vulnerability: false

mock_http_server: {
mock_responses: [
{
uri: "/rest/settings"
status: 200
body_content: '{"data":{"urlBaseWebhook":"http://target:5678/","versionCli":"0.237.0",...}}'
},
{
uri: "/rest/login"
status: 401
},
{
uri: "/rest/workflows"
status: 401
}
]
}
}

tests: {
name: "whenNotVulnerable_n8nModern_returnsFalse"
expect_vulnerability: false

mock_http_server: {
mock_responses: [
{
uri: "/rest/settings"
status: 200
body_content: '{"data":{"instanceId":"...","versionCli":"1.123.21"}}'
}
]
}
}

tests: {
name: "whenNotN8n_returnsFalse"
expect_vulnerability: false

mock_http_server: {
mock_responses: [
{
uri: "/rest/settings"
status: 404
}
]
}
}