highJul 10, 2026

Clauster: Missing Authentication Bypass on Non-Loopback Dashboard

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

Clauster instances bound to a public or LAN address silently skip all authentication when auth.enabled is left at its default of false, letting anyone on the network take full control of the dashboard

Packageclauster
Ecosystempip
Affected<= 0.2.1
Fixed in0.2.2

The problem

Clauster splits authentication across two independent layers: a runtime middleware that gates requests behind `auth.enabled`, and a config validator that checks whether *some* auth-related flag (password, reverse proxy, or explicit unauthenticated opt-out) is set.

These two layers did not agree.

An operator who set `auth.password_required: true` and supplied a password hash would pass config validation and start the server believing it was protected. But because `auth.enabled` defaults to `false`, the runtime guard passed every single request through without checking credentials.

This affected all non-loopback binds, including the Docker image which binds `0.0.0.0` by default.

Proof of concept

A working proof-of-concept for this issue in clauster, with the exact payload below.

bash
# clauster.yml (vulnerable config)
# host: 0.0.0.0
# auth:
#   enabled: false          # <-- default, not set by operator
#   password_required: true
#   password_hash: "<bcrypt>"

# Exploit: unauthenticated access to protected instance
curl http://<host>:7621/api/instances
# Returns HTTP 200 + full instance list with no credentials

The root cause is CWE-306 (Missing Authentication for Critical Function): the config validator accepted a config that *declared* authentication intent without *enforcing* it at runtime. The patch fixes this by making the validator fail-closed: starting with 0.2.2, a non-loopback bind is rejected at startup unless `auth.enabled: true` is present together with either `auth.password_required` (plus a hash) or `auth.reverse_proxy.enabled`.

Operators who truly want no auth must explicitly set `auth.allow_unauthenticated_network: true`.

Because the guard was a simple boolean short-circuit (`if not config.auth.enabled: return next(request)`), no special headers or encoding were needed. A plain unauthenticated HTTP GET to any API endpoint was sufficient.

The fix

Upgrade to clauster 0.2.2. If an immediate upgrade is not possible, add `auth.enabled: true` to `clauster.yml` (or set `CLAUSTER_AUTH_ENABLED=true`) alongside your existing password or reverse-proxy config. Alternatively, bind to loopback only (`host: 127.0.0.1`) and expose the instance through a trusted SSH tunnel or authenticating reverse proxy.

Reporter not attributed.

References: [1][2]

Related research