From a41fc3e266ef7b023a539f4112837fb9d88b12cb Mon Sep 17 00:00:00 2001 From: Yuriy Date: Wed, 18 Mar 2026 23:46:54 +0200 Subject: [PATCH 1/2] n8n testbeds --- n8n/exposed/README.md | 28 +++++++++++++++++++++++++ n8n/exposed/docker-compose-non-vuln.yml | 19 +++++++++++++++++ n8n/exposed/docker-compose-vuln.yml | 19 +++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 n8n/exposed/README.md create mode 100644 n8n/exposed/docker-compose-non-vuln.yml create mode 100644 n8n/exposed/docker-compose-vuln.yml diff --git a/n8n/exposed/README.md b/n8n/exposed/README.md new file mode 100644 index 00000000..e8e23e30 --- /dev/null +++ b/n8n/exposed/README.md @@ -0,0 +1,28 @@ +# n8n Exposed REST API + +n8n is a workflow automation platform that allows users to create and execute automated workflows. +This testbed demonstrates exposed and properly secured configurations of the n8n REST API. + +## Safe setup + +```shell +docker compose -f docker-compose-non-vuln.yml up -d +``` +This configuration enforces authentication and prevents unauthorized access to the REST API. + +## Vulnerable setup + +```shell +docker compose -f docker-compose-vuln.yml up -d +``` +This configuration exposes the REST API without authentication, allowing unauthorized access. + +## Notes + +- In older versions, exposure may occur when authentication is disabled. +- In newer versions, this issue is often caused by misconfigurations such as improperly secured reverse proxies. + +## References + +- https://github.com/n8n-io/n8n +- https://docs.n8n.io/hosting/securing/overview/ diff --git a/n8n/exposed/docker-compose-non-vuln.yml b/n8n/exposed/docker-compose-non-vuln.yml new file mode 100644 index 00000000..c52b4dca --- /dev/null +++ b/n8n/exposed/docker-compose-non-vuln.yml @@ -0,0 +1,19 @@ +services: + n8n: + image: n8nio/n8n:2.9.1 + container_name: n8n-non-vulnerable + restart: unless-stopped + + ports: + - "5678:5678" + + environment: + - N8N_HOST=0.0.0.0 + - N8N_PORT=5678 + - N8N_PROTOCOL=http + + volumes: + - n8n_data:/home/node/.n8n + +volumes: + n8n_data: diff --git a/n8n/exposed/docker-compose-vuln.yml b/n8n/exposed/docker-compose-vuln.yml new file mode 100644 index 00000000..3ef92b5f --- /dev/null +++ b/n8n/exposed/docker-compose-vuln.yml @@ -0,0 +1,19 @@ +services: + n8n: + image: n8nio/n8n:0.237.0 + container_name: n8n-vulnerable + restart: unless-stopped + + ports: + - "5678:5678" + + environment: + - N8N_HOST=0.0.0.0 + - N8N_PORT=5678 + - N8N_PROTOCOL=http + + volumes: + - n8n_data:/home/node/.n8n + +volumes: + n8n_data: From 63081d8d896945422878c4cbf858d87030939021 Mon Sep 17 00:00:00 2001 From: Yuriy Date: Fri, 27 Mar 2026 13:49:35 +0200 Subject: [PATCH 2/2] n8n testbeds updated --- n8n/exposed/README.md | 104 +++++++++++++++++-- n8n/exposed/docker-compose-non-vuln.yml | 14 +-- n8n/exposed/docker-compose-vuln-auth.yml | 11 ++ n8n/exposed/docker-compose-vuln-no-owner.yml | 7 ++ n8n/exposed/docker-compose-vuln.yml | 16 +-- 5 files changed, 116 insertions(+), 36 deletions(-) create mode 100644 n8n/exposed/docker-compose-vuln-auth.yml create mode 100644 n8n/exposed/docker-compose-vuln-no-owner.yml diff --git a/n8n/exposed/README.md b/n8n/exposed/README.md index e8e23e30..8ee5f9a0 100644 --- a/n8n/exposed/README.md +++ b/n8n/exposed/README.md @@ -1,26 +1,112 @@ +Here's the corrected and tightened version: + +--- + # n8n Exposed REST API -n8n is a workflow automation platform that allows users to create and execute automated workflows. -This testbed demonstrates exposed and properly secured configurations of the n8n REST API. +n8n is a workflow automation platform for creating and executing automated workflows. This testbed demonstrates both exposed and properly secured configurations of the n8n REST API. + +## Vulnerable Setups + +```shell +docker compose -f docker-compose-vuln.yml up -d +``` +Early n8n versions (e.g. 0.54.0) shipped without user management. This setup reproduces that behavior: the REST API is fully accessible without authentication and the `/rest/login` endpoint does not exist. + +```shell +docker compose -f docker-compose-vuln-no-owner.yml up -d +``` +Later 0.x versions introduced user management but allowed instances to run without a configured owner account. In this state, the REST API is accessible without authentication. The `/rest/login` endpoint exists and returns a session cookie on anonymous requests, which can be used in subsequent calls as demonstrated in the steps below. + +```shell +docker compose -f docker-compose-vuln-auth.yml up -d +``` +Reproduces the BasicAuth split-brain misconfiguration: BasicAuth is enabled and protects the UI (returns `401`), but the `/rest/*` router has no auth middleware applied and remains fully accessible without credentials. -## Safe setup +## Safe Setup ```shell docker compose -f docker-compose-non-vuln.yml up -d ``` -This configuration enforces authentication and prevents unauthorized access to the REST API. +A properly configured n8n instance with authentication enforced on all `/rest/*` endpoints. The detector must produce no finding against this target. + +## Steps to Reproduce -## Vulnerable setup +### Step 1 — Fingerprint + +Confirm the target is n8n 0.x by checking for fields that are only present in the 0.x settings schema: ```shell -docker compose -f docker-compose-vuln.yml up -d +curl -s http://:5678/rest/settings | jq . ``` -This configuration exposes the REST API without authentication, allowing unauthorized access. + +Expected response on a vulnerable instance: + +```json +{ + "data": { + ... + "urlBaseWebhook": "http://:5678/", + "versionCli": "0.237.0", + "saveManualExecutions": false, + "saveDataErrorExecution": "all", + ... + } +} +``` + +The presence of `versionCli` and `urlBaseWebhook` confirms this is n8n 0.x. This fingerprint self-scoping — the detector cannot fire against modern instances regardless of their configuration. + +### Step 2 — Session Bootstrap (where applicable) + +On instances with user management enabled but no owner configured (`docker-compose-vuln-no-owner.yml`), an anonymous `GET /rest/login` returns a valid session cookie: + +```shell +curl -s -i http://:5678/rest/login +``` + +Expected response: + +``` +HTTP/1.1 200 OK +Set-Cookie: n8n-auth=; Path=/; HttpOnly; SameSite=Lax +``` + +On early versions without user management (`docker-compose-vuln.yml` and `docker-compose-vuln-auth.yml`), this endpoint does not exist. The cookie is not needed — `/rest/workflows` is directly accessible without one. + +### Step 3 — Workflows Access + +Without cookie (early versions / split-brain): + +```shell +curl -s http://:5678/rest/workflows | jq . +``` + +With cookie (no-owner setup): + +```shell +curl -s -H 'Cookie: n8n-auth=' http://:5678/rest/workflows | jq . +``` + +Expected response in both cases: + +```json +{ + "data": [ + { + ... + } + ] +} +``` + +Confirms unauthenticated (or pre-authentication anonymous session) access to workflow data. ## Notes -- In older versions, exposure may occur when authentication is disabled. -- In newer versions, this issue is often caused by misconfigurations such as improperly secured reverse proxies. +- The vulnerable surface spans all n8n 0.x releases. Early versions (e.g. 0.54.0) had no user management and optional auth that did not cover `/rest/*` routes. Later 0.x versions introduced user management but remained exploitable when no owner account was initialized, or when flags such as `N8N_USER_MANAGEMENT_DISABLED` produced inconsistent auth behavior. +- n8n 1.x enforces authentication at the application layer via session middleware applied directly to the `/rest/` router. Empirical testing confirms that `/rest/workflows` returns `401` on all 1.x+ configurations, including fresh instances with no owner account configured. Reverse proxy misconfigurations that forward unauthenticated requests still reach n8n's own auth check and do not bypass it. +- The `versionCli` and `urlBaseWebhook` fields in `/rest/settings` are unique to n8n 0.x. Their absence in 1.x+ means the fingerprint step will not match modern instances, preventing false positives by design. ## References diff --git a/n8n/exposed/docker-compose-non-vuln.yml b/n8n/exposed/docker-compose-non-vuln.yml index c52b4dca..303017a0 100644 --- a/n8n/exposed/docker-compose-non-vuln.yml +++ b/n8n/exposed/docker-compose-non-vuln.yml @@ -2,18 +2,6 @@ services: n8n: image: n8nio/n8n:2.9.1 container_name: n8n-non-vulnerable - restart: unless-stopped - ports: - "5678:5678" - - environment: - - N8N_HOST=0.0.0.0 - - N8N_PORT=5678 - - N8N_PROTOCOL=http - - volumes: - - n8n_data:/home/node/.n8n - -volumes: - n8n_data: + restart: unless-stopped diff --git a/n8n/exposed/docker-compose-vuln-auth.yml b/n8n/exposed/docker-compose-vuln-auth.yml new file mode 100644 index 00000000..bde34c25 --- /dev/null +++ b/n8n/exposed/docker-compose-vuln-auth.yml @@ -0,0 +1,11 @@ +services: + n8n: + image: n8nio/n8n:0.54.0 + container_name: n8n-vulnerable-auth + ports: + - "5678:5678" + environment: + - N8N_BASIC_AUTH_ACTIVE=true + - N8N_BASIC_AUTH_USER=admin + - N8N_BASIC_AUTH_PASSWORD=str0ngpassword! + restart: unless-stopped diff --git a/n8n/exposed/docker-compose-vuln-no-owner.yml b/n8n/exposed/docker-compose-vuln-no-owner.yml new file mode 100644 index 00000000..1b929f4c --- /dev/null +++ b/n8n/exposed/docker-compose-vuln-no-owner.yml @@ -0,0 +1,7 @@ +services: + n8n: + image: n8nio/n8n:0.237.0 + container_name: n8n-vulnerable-no-owner + ports: + - "5678:5678" + restart: unless-stopped diff --git a/n8n/exposed/docker-compose-vuln.yml b/n8n/exposed/docker-compose-vuln.yml index 3ef92b5f..aa20ce25 100644 --- a/n8n/exposed/docker-compose-vuln.yml +++ b/n8n/exposed/docker-compose-vuln.yml @@ -1,19 +1,7 @@ services: n8n: - image: n8nio/n8n:0.237.0 + image: n8nio/n8n:0.54.0 container_name: n8n-vulnerable - restart: unless-stopped - ports: - "5678:5678" - - environment: - - N8N_HOST=0.0.0.0 - - N8N_PORT=5678 - - N8N_PROTOCOL=http - - volumes: - - n8n_data:/home/node/.n8n - -volumes: - n8n_data: + restart: unless-stopped