Skip to content

Commit 835f3af

Browse files
Merge pull request #806 from YuriyPobezhymov:master
PiperOrigin-RevId: 906382663 Change-Id: Ia8615123795e6e3d29862d3350aab26268117df1
2 parents f4fceee + 6c26006 commit 835f3af

2 files changed

Lines changed: 232 additions & 0 deletions

File tree

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# proto-file: proto/templated_plugin.proto
2+
# proto-message: TemplatedPlugin
3+
4+
###############
5+
# PLUGIN INFO #
6+
###############
7+
8+
info: {
9+
type: VULN_DETECTION
10+
name: "N8N_ExposedRestApi"
11+
author: "yuradoc (yuradoc.research@gmail.com)"
12+
version: "1.0"
13+
}
14+
15+
finding: {
16+
main_id: {
17+
publisher: "GOOGLE"
18+
value: "N8N_EXPOSED_REST_API"
19+
}
20+
title: "Exposed n8n REST API"
21+
description:
22+
"The n8n automation platform instance exposes its REST API without proper "
23+
"authentication, allowing unauthenticated access to workflow management "
24+
"functionality. This may enable attackers to enumerate, modify, and execute "
25+
"workflows, including those containing built-in command execution nodes, "
26+
"potentially leading to remote code execution. Exposure conditions vary "
27+
"across n8n versions. Early releases without user management did not "
28+
"consistently protect REST endpoints, and available authentication mechanisms "
29+
"(e.g., JWT or basic auth) may not fully cover all /rest/* routes. In later "
30+
"0.x versions, authentication behavior may be inconsistent—for example, "
31+
"instances without an initialized owner account or with ineffective "
32+
"configuration flags may allow unauthenticated access to /rest/* endpoints."
33+
recommendation:
34+
"Upgrade the n8n instance to a recent supported version where authentication "
35+
"is consistently enforced by default. Older 0.x versions may not reliably "
36+
"protect REST API endpoints. Additionally, ensure authentication is properly "
37+
"configured and that the instance is not exposed to untrusted networks. Verify "
38+
"that all REST API endpoints are protected, especially in partially initialized "
39+
"deployments. Refer to https://docs.n8n.io/hosting/securing/overview/ for guidance."
40+
severity: CRITICAL
41+
}
42+
43+
###########
44+
# ACTIONS #
45+
###########
46+
47+
actions: {
48+
name: "n8n_vuln_fingerprint"
49+
http_request: {
50+
method: GET
51+
uri: "/rest/settings"
52+
response: {
53+
http_status: 200
54+
expect_all: {
55+
conditions: [
56+
{ body: {} contains: '"urlBaseWebhook"' },
57+
{ body: {} contains: '"versionCli":"0.' }
58+
]
59+
}
60+
}
61+
}
62+
}
63+
64+
# Gets the cookie if available, still passes this step if not available.
65+
actions: {
66+
name: "n8n_retrieve_auth_cookie"
67+
http_request: {
68+
method: GET
69+
uri: "/rest/login"
70+
response: {
71+
extract_any: {
72+
patterns: [
73+
{
74+
from_header: { name: "Set-Cookie" }
75+
regexp: "(n8n-auth=[^;]+)"
76+
variable_name: "n8n_auth_token"
77+
},
78+
{
79+
from_body: {}
80+
regexp: "()"
81+
}
82+
]
83+
}
84+
}
85+
}
86+
}
87+
88+
# Gets workflows, sending the previous cookie if it was available.
89+
actions: {
90+
name: "n8n_retrieve_existing_flows"
91+
http_request: {
92+
method: GET
93+
uri: "/rest/workflows"
94+
headers: [
95+
{ name: "Cookie" value: "{{ n8n_auth_token }}" }
96+
]
97+
response: {
98+
http_status: 200
99+
expect_all: {
100+
conditions: [
101+
{ body: {} contains: '"data":[' }
102+
]
103+
}
104+
}
105+
}
106+
}
107+
108+
#############
109+
# WORKFLOWS #
110+
#############
111+
112+
workflows: {
113+
actions: [
114+
"n8n_vuln_fingerprint",
115+
"n8n_retrieve_auth_cookie",
116+
"n8n_retrieve_existing_flows"
117+
]
118+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# proto-file: proto/templated_plugin_tests.proto
2+
# proto-message: TemplatedPluginTests
3+
4+
config: {
5+
tested_plugin: "N8N_ExposedRestApi"
6+
}
7+
8+
tests: {
9+
name: "whenVulnerable_noOwner_returnsTrue"
10+
expect_vulnerability: true
11+
12+
mock_http_server: {
13+
mock_responses: [
14+
{
15+
uri: "/rest/settings"
16+
status: 200
17+
body_content: '{"data":{"urlBaseWebhook":"http://target:5678/","versionCli":"0.237.0",...}}'
18+
},
19+
{
20+
uri: "/rest/login"
21+
status: 200
22+
headers: [
23+
{ name: "Set-Cookie" value: "n8n-auth=jwt_token" }
24+
]
25+
},
26+
{
27+
uri: "/rest/workflows"
28+
status: 200
29+
condition: {
30+
headers: [
31+
{ name: "Cookie" value: "n8n-auth=jwt_token" }
32+
]
33+
}
34+
body_content: '{"data":[{"id":"Q2cpznBmI6NCfeBF","name":"Vulnerable Workflow",...},...]}'
35+
}
36+
]
37+
}
38+
}
39+
40+
tests: {
41+
name: "whenVulnerable_noUserManagement_returnsTrue"
42+
expect_vulnerability: true
43+
44+
mock_http_server: {
45+
mock_responses: [
46+
{
47+
uri: "/rest/settings"
48+
status: 200
49+
body_content: '{"data":{"urlBaseWebhook":"http://target:5678/","versionCli":"0.54.0",...}}'
50+
},
51+
{
52+
uri: "/rest/login"
53+
status: 404
54+
},
55+
{
56+
uri: "/rest/workflows"
57+
status: 200
58+
body_content: '{"data":[{"id":"1","name":"Legacy Workflow",...},...]}'
59+
}
60+
]
61+
}
62+
}
63+
64+
tests: {
65+
name: "whenNotVulnerable_restProtected_returnsFalse"
66+
expect_vulnerability: false
67+
68+
mock_http_server: {
69+
mock_responses: [
70+
{
71+
uri: "/rest/settings"
72+
status: 200
73+
body_content: '{"data":{"urlBaseWebhook":"http://target:5678/","versionCli":"0.237.0",...}}'
74+
},
75+
{
76+
uri: "/rest/login"
77+
status: 401
78+
},
79+
{
80+
uri: "/rest/workflows"
81+
status: 401
82+
}
83+
]
84+
}
85+
}
86+
87+
tests: {
88+
name: "whenNotVulnerable_n8nModern_returnsFalse"
89+
expect_vulnerability: false
90+
91+
mock_http_server: {
92+
mock_responses: [
93+
{
94+
uri: "/rest/settings"
95+
status: 200
96+
body_content: '{"data":{"instanceId":"...","versionCli":"1.123.21"}}'
97+
}
98+
]
99+
}
100+
}
101+
102+
tests: {
103+
name: "whenNotN8n_returnsFalse"
104+
expect_vulnerability: false
105+
106+
mock_http_server: {
107+
mock_responses: [
108+
{
109+
uri: "/rest/settings"
110+
status: 404
111+
}
112+
]
113+
}
114+
}

0 commit comments

Comments
 (0)