Every CVE Search/ Get API key

CVE-2026-86065

Klever-Go: Unauthenticated WebSocket /subscribe: no read-size limit, no connection cap, permissive origin -> remote node memory/goroutine exhaustion (DoS)

7.5 HighFix available

Klever-Go is the Go implementation of the Klever blockchain protocol. Prior to 1.7.20, the default-open GET /subscribe endpoint in network/api/websocket/routes.go accepts unauthenticated WebSocket clients with permissive origin handling, does not call SetReadLimit to bound message size, and has no live-connection cap. SocketHub.HandleClientInsertion also accepts an unbounded address list that grows addressSubscription, and client.loopIn continues reading without a size limit, allowing one client to grow subscription maps or many clients to retain goroutines, buffered channels, and descriptors. The global HTTP request throttler does not count upgraded live WebSocket connections. Because the REST and WebSocket API runs in the node process, memory or scheduler exhaustion can crash the node and interrupt P2P and consensus participation. This issue is fixed in version 1.7.20.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
None
Integrity
None
Availability
High

Exploit likelihood

0.43%Up 0.1 points since Sep 24, 2026
90%50%10%1%

What each source reports

CVE List Score used 7.5

Klever-Go: Unauthenticated WebSocket /subscribe: no read-size limit, no connection cap, permissive origin -> remote node memory/goroutine exhaustion (DoS)

Klever-Go is the Go implementation of the Klever blockchain protocol. Prior to 1.7.20, the default-open GET /subscribe endpoint in network/api/websocket/routes.go accepts unauthenticated WebSocket clients with permissive origin handling, does not call SetReadLimit to bound message size, and has no live-connection cap. SocketHub.HandleClientInsertion also accepts an unbounded address list that grows addressSubscription, and client.loopIn continues reading without a size limit, allowing one client to grow subscription maps or many clients to retain goroutines, buffered channels, and descriptors. The global HTTP request throttler does not count upgraded live WebSocket connections. Because the REST and WebSocket API runs in the node process, memory or scheduler exhaustion can crash the node and interrupt P2P and consensus participation. This issue is fixed in version 1.7.20.

Record
CVE-2026-86065
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-770
Updated
2026-11-02
EUVDEUVD-2026-85575 7.5

Klever-Go is the Go implementation of the Klever blockchain protocol. Prior to 1.7.20, the default-open GET /subscribe endpoint in network/api/websocket/routes.go accepts unauthenticated WebSocket clients with permissive origin handling, does not call SetReadLimit to bound message size, and has no live-connection cap. SocketHub.HandleClientInsertion also accepts an unbounded address list that grows addressSubscription, and client.loopIn continues reading without a size limit, allowing one client to grow subscription maps or many clients to retain goroutines, buffered channels, and descriptors. The global HTTP request throttler does not count upgraded live WebSocket connections. Because the REST and WebSocket API runs in the node process, memory or scheduler exhaustion can crash the node and interrupt P2P and consensus participation. This issue is fixed in version 1.7.20.

Record
EUVD-2026-85575
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Updated
2026-11-02
GitHub AdvisoriesGHSA-4fwh-wrm6-97xm 7.5

Klever-Go: Unauthenticated WebSocket /subscribe: no read-size limit, no connection cap, permissive origin -> remote node memory/goroutine exhaustion (DoS)

Summary

The unauthenticated WebSocket endpoint GET /subscribe is registered open: true by default (config/node/api.yaml) and lets a remote, unauthenticated client exhaust the node's memory and goroutines. Because the REST API runs IN-PROCESS with the node — network/api/api.go Start(...) ends with ws.Run(kleverFacade.RestAPIInterface()) — exhausting/killing the API process takes down the entire node, including its P2P and consensus participation. No API key, account, stake, or funds are required.

Three compounding, independently-exploitable gaps stack on this one endpoint:

  1. Permissive origin — upgrader.CheckOrigin always returns true (network/api/websocket/routes.go), so any web origin can complete the handshake.
  2. No read-size limit — the connection never calls conn.SetReadLimit(...). gorilla's default is UNLIMITED, so a single conn.ReadJSON (processSubscription) or conn.ReadMessage (client.loopIn) can be forced to allocate an arbitrarily large buffer from ONE frame.
  3. No connection / fan-out cap — the gin global throttler (simultaneousRequests: 100) releases its slot as soon as handleSubscribe returns, which it does immediately after go processSubscription(conn, hub). Live WebSocket connections are therefore NOT counted by it. There is no per-IP / per-connection / hub-level cap. Each accepted connection spawns 2 goroutines plus a 500-entry buffered channel, and req.Addresses has no length cap, so the hub's addressSubscription map grows 1:1 with attacker-supplied strings.

Affected Component / Code Path

Unauthenticated, reachable by default, no recovery on the resource-allocation path:

gin engine (network/api/api.go: Start -> ws.Run, IN-PROCESS with node)
 -> GET /subscribe                              network/api/websocket/routes.go:34  (SubscribeTopics)
   -> handleSubscribe                            network/api/websocket/routes.go:39
     -> upgrader.Upgrade  (CheckOrigin == true)  network/api/websocket/routes.go:22  <-- GAP #1
     -> go processSubscription(conn, hub)         network/api/websocket/routes.go:46  (throttler slot freed here)
        -> conn.ReadJSON(&req)  (no SetReadLimit) network/api/websocket/routes.go:57  <-- GAP #2
        -> hub.HandleClientInsertion(...)         websocket/websocket.go:121          <-- GAP #3 (addresses uncapped)
           -> websocket.NewClient -> loopIn/loopOut (2 goroutines + 500-buf chan per conn)  websocket/client.go:24
              -> conn.ReadMessage()  (no SetReadLimit, no deadline)  websocket/client.go:77  <-- GAP #2

Root-cause excerpts (commit 23b74e1):

network/api/websocket/routes.go

var upgrader = gorilla.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true                      // GAP #1: any origin accepted
	},
}

func handleSubscribe(c *gin.Context, hub *websocket.SocketHub) {
	conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		log.Error(subscribeOp, "err", err.Error())
		return
	}
	go processSubscription(conn, hub) // returns now -> gin global throttler slot released (GAP #3)
}

func processSubscription(conn *gorilla.Conn, hub *websocket.SocketHub) {
	// no conn.SetReadLimit(...) anywhere (GAP #2)
	_ = conn.SetReadDeadline(time.Now().Add(subscribeReadTimeout))
	var req subscribeRequest
	if err := conn.ReadJSON(&req); err != nil { ... }      // unbounded read
	_ = conn.SetReadDeadline(time.Time{})                  // deadline cleared
	...
	client := websocket.NewClient(conn, hub)
	hub.HandleClientInsertion(parsedTypes, req.Addresses, client) // req.Addresses uncapped (GAP #3)
}

websocket/websocket.go — HandleClientInsertion inserts every address with no length cap:

for _, address := range addresses {
	if _, ok := h.addressSubscription[address]; !ok {
		h.addressSubscription[address] = make(map[*client]userOptions) // grows 1:1 with attacker input
	}
	...
}

websocket/client.go — loopIn reads with no…

Record
GHSA-4fwh-wrm6-97xm
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-770
Updated
2026-11-02
NVD 7.5

Klever-Go is the Go implementation of the Klever blockchain protocol. Prior to 1.7.20, the default-open GET /subscribe endpoint in network/api/websocket/routes.go accepts unauthenticated WebSocket clients with permissive origin handling, does not call SetReadLimit to bound message size, and has no live-connection cap. SocketHub.HandleClientInsertion also accepts an unbounded address list that grows addressSubscription, and client.loopIn continues reading without a size limit, allowing one client to grow subscription maps or many clients to retain goroutines, buffered channels, and descriptors. The global HTTP request throttler does not count upgraded live WebSocket connections. Because the REST and WebSocket API runs in the node process, memory or scheduler exhaustion can crash the node and interrupt P2P and consensus participation. This issue is fixed in version 1.7.20.

Record
CVE-2026-86065
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-770
Updated
2026-11-02
OSV 7.5

Klever-Go: Unauthenticated WebSocket /subscribe: no read-size limit, no connection cap, permissive origin -> remote node memory/goroutine exhaustion (DoS)

Klever-Go is the Go implementation of the Klever blockchain protocol. Prior to 1.7.20, the default-open GET /subscribe endpoint in network/api/websocket/routes.go accepts unauthenticated WebSocket clients with permissive origin handling, does not call SetReadLimit to bound message size, and has no live-connection cap. SocketHub.HandleClientInsertion also accepts an unbounded address list that grows addressSubscription, and client.loopIn continues reading without a size limit, allowing one client to grow subscription maps or many clients to retain goroutines, buffered channels, and descriptors. The global HTTP request throttler does not count upgraded live WebSocket connections. Because the REST and WebSocket API runs in the node process, memory or scheduler exhaustion can crash the node and interrupt P2P and consensus participation. This issue is fixed in version 1.7.20.

Record
CVE-2026-86065
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-770
Updated
2026-11-02
OSVGHSA-4fwh-wrm6-97xm 7.5

Klever-Go: Unauthenticated WebSocket /subscribe: no read-size limit, no connection cap, permissive origin -> remote node memory/goroutine exhaustion (DoS)

Summary

The unauthenticated WebSocket endpoint GET /subscribe is registered open: true by default (config/node/api.yaml) and lets a remote, unauthenticated client exhaust the node's memory and goroutines. Because the REST API runs IN-PROCESS with the node — network/api/api.go Start(...) ends with ws.Run(kleverFacade.RestAPIInterface()) — exhausting/killing the API process takes down the entire node, including its P2P and consensus participation. No API key, account, stake, or funds are required.

Three compounding, independently-exploitable gaps stack on this one endpoint:

  1. Permissive origin — upgrader.CheckOrigin always returns true (network/api/websocket/routes.go), so any web origin can complete the handshake.
  2. No read-size limit — the connection never calls conn.SetReadLimit(...). gorilla's default is UNLIMITED, so a single conn.ReadJSON (processSubscription) or conn.ReadMessage (client.loopIn) can be forced to allocate an arbitrarily large buffer from ONE frame.
  3. No connection / fan-out cap — the gin global throttler (simultaneousRequests: 100) releases its slot as soon as handleSubscribe returns, which it does immediately after go processSubscription(conn, hub). Live WebSocket connections are therefore NOT counted by it. There is no per-IP / per-connection / hub-level cap. Each accepted connection spawns 2 goroutines plus a 500-entry buffered channel, and req.Addresses has no length cap, so the hub's addressSubscription map grows 1:1 with attacker-supplied strings.

Affected Component / Code Path

Unauthenticated, reachable by default, no recovery on the resource-allocation path:

gin engine (network/api/api.go: Start -> ws.Run, IN-PROCESS with node)
 -> GET /subscribe                              network/api/websocket/routes.go:34  (SubscribeTopics)
   -> handleSubscribe                            network/api/websocket/routes.go:39
     -> upgrader.Upgrade  (CheckOrigin == true)  network/api/websocket/routes.go:22  <-- GAP #1
     -> go processSubscription(conn, hub)         network/api/websocket/routes.go:46  (throttler slot freed here)
        -> conn.ReadJSON(&req)  (no SetReadLimit) network/api/websocket/routes.go:57  <-- GAP #2
        -> hub.HandleClientInsertion(...)         websocket/websocket.go:121          <-- GAP #3 (addresses uncapped)
           -> websocket.NewClient -> loopIn/loopOut (2 goroutines + 500-buf chan per conn)  websocket/client.go:24
              -> conn.ReadMessage()  (no SetReadLimit, no deadline)  websocket/client.go:77  <-- GAP #2

Root-cause excerpts (commit 23b74e1):

network/api/websocket/routes.go

var upgrader = gorilla.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true                      // GAP #1: any origin accepted
	},
}

func handleSubscribe(c *gin.Context, hub *websocket.SocketHub) {
	conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		log.Error(subscribeOp, "err", err.Error())
		return
	}
	go processSubscription(conn, hub) // returns now -> gin global throttler slot released (GAP #3)
}

func processSubscription(conn *gorilla.Conn, hub *websocket.SocketHub) {
	// no conn.SetReadLimit(...) anywhere (GAP #2)
	_ = conn.SetReadDeadline(time.Now().Add(subscribeReadTimeout))
	var req subscribeRequest
	if err := conn.ReadJSON(&req); err != nil { ... }      // unbounded read
	_ = conn.SetReadDeadline(time.Time{})                  // deadline cleared
	...
	client := websocket.NewClient(conn, hub)
	hub.HandleClientInsertion(parsedTypes, req.Addresses, client) // req.Addresses uncapped (GAP #3)
}

websocket/websocket.go — HandleClientInsertion inserts every address with no length cap:

for _, address := range addresses {
	if _, ok := h.addressSubscription[address]; !ok {
		h.addressSubscription[address] = make(map[*client]userOptions) // grows 1:1 with attacker input
	}
	...
}

websocket/client.go — loopIn reads with no…

Record
GHSA-4fwh-wrm6-97xm
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-770
Updated
2026-11-02
OSVGO-2026-6581 No score

Klever-Go: Unauthenticated WebSocket /subscribe: no read-size limit, no connection cap, permissive origin -> remote node memory/goroutine exhaustion (DoS) in github.com/klever-io/klever-go

Klever-Go: Unauthenticated WebSocket /subscribe: no read-size limit, no connection cap, permissive origin -> remote node memory/goroutine exhaustion (DoS) in github.com/klever-io/klever-go

Record
GO-2026-6581
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
github.com/klever-io/klever-go
Go
before 1.7.20
1.7.20

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-86065 is a high vulnerability (CVSS 7.5) in Klever-io Klever-go, published September 23, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.43%, the 37th percentile. Fixed in github.com/klever-io/klever-go 1.7.20.

References

This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open