GCVE Workshop - 22 September 2026 (14:00-18:00), Luxembourg Before The Vulnopticon Conference - Registration

GHSA-W4C6-7R69-W7J9

Vulnerability from github – Published: 2026-06-05 16:41 – Updated: 2026-06-09 18:40
VLAI
Summary
klever-go: REST API slow-header connection exhaustion via Gin Engine.Run
Details

Summary

The Klever seednode REST API starts a Gin engine with Engine.Run(restAPIInterface). In Gin v1.9.1, Engine.Run calls Go's default http.ListenAndServe, which constructs an HTTP server without application-level ReadHeaderTimeout, ReadTimeout, or MaxHeaderBytes limits.

An unauthenticated client that can reach a REST listener bound with Klever's documented --rest-api-interface :8080 all-interface option can hold incomplete HTTP headers open indefinitely. In a local proof against the real cmd/seednode/api.Start path on v1.7.17, 120 slow-header connections caused 20/20 legitimate /log probes to fail with accept: too many open files. A fixed control using the same Gin router behind an explicit http.Server with ReadHeaderTimeout, ReadTimeout, and MaxHeaderBytes retained 0 slow connections and served 20/20 probes.

This report is distinct from the P2P advisories and from my direct-message goroutine report. This finding concerns Klever-owned HTTP REST startup code (cmd/seednode/api and network/api) using Gin Engine.Run without server-level header deadlines. It does not depend on MultiDataInterceptor, Batch.Decompress, libp2p, malformed P2P messages, or direct-message goroutine spawning.

Details

Seednode REST API, latest release v1.7.17:

  • cmd/seednode/api/api.go:17 defines Start(restAPIInterface, marshalizer).
  • cmd/seednode/api/api.go:18 creates ws := gin.Default().
  • cmd/seednode/api/api.go:23 returns ws.Run(restAPIInterface).
  • cmd/seednode/CLI.md:23 documents --rest-api-interface; it says :8080 binds all interfaces and off disables the API.

Node REST API, latest release v1.7.17:

  • network/api/api.go:79 creates ws = gin.Default().
  • network/api/api.go:98 returns ws.Run(kleverFacade.RestAPIInterface()).
  • cmd/node/main.go:147-150 documents the same --rest-api-interface flag and says :8080 binds all interfaces.
  • docker/README.md:56-61 and docker/README.md:67-70 publish host port 8080 for full-node and validator Docker examples.
  • README.md:264-268 documents that the node exposes a REST API for blockchain queries and operations.

The seednode REST API source is byte-identical across v1.7.14 through v1.7.17; the captured runtime PoC was executed on v1.7.17.

Current develop commit 10bcfd50 remains affected:

  • network/api/api.go:98 still returns ws.Run(kleverFacade.RestAPIInterface()).
  • cmd/seednode/api/api.go:59 still returns ws.Run(restAPIInterface).

Gin v1.9.1 implements Engine.Run as:

func (engine *Engine) Run(addr ...string) (err error) {
    address := resolveAddress(addr)
    err = http.ListenAndServe(address, engine.Handler())
    return
}

In my source sweep, I did not find a production http.Server{ReadHeaderTimeout: ...} wrapper for either REST start path. The only ReadHeaderTimeout hit I found in the repository was a test helper under network/api/websocket/routes_test.go.

PoC

GitHub Private Vulnerability Reporting does not appear to allow file attachments in this form, so I am including the reproduction command and captured output inline. I can paste the full 254-line Go test patch in a reply immediately if useful.

The test starts two local child servers:

  1. Vulnerable: the real cmd/seednode/api.Start path.
  2. Fixed control: the same Gin router served through http.Server{ReadHeaderTimeout: 250ms, ReadTimeout: 250ms, MaxHeaderBytes: 4096}.

Reproduction from a clean checkout:

git clone https://github.com/klever-io/klever-go
cd klever-go
git checkout v1.7.17

# Apply the PoC patch to cmd/seednode/api.
# I can provide the full patch in this advisory thread.

go test ./cmd/seednode/api -run TestPoC_SeednodeAPISlowlorisDifferential -count=1 -v -timeout 60s

Captured output on v1.7.17:

POC_RESULT mode=vulnerable slow_connections_opened=120 slow_connections_still_open=111 legitimate_probe_ok=0 legitimate_probe_fail=20
POC_RESULT mode=fixed slow_connections_opened=120 slow_connections_still_open=0 legitimate_probe_ok=20 legitimate_probe_fail=0

The vulnerable server also logs repeated accept failures:

http: Accept error: accept tcp 127.0.0.1:56415: accept: too many open files; retrying in 1s

Impact

For an externally reachable Klever REST listener, a single unauthenticated client can retain many server-side connections by never completing HTTP headers. Because the Go server has no read-header deadline, those connections persist until the client closes them or an external proxy/firewall intervenes.

The direct result is REST API unavailability for legitimate clients. The local proof demonstrates this as 0/20 legitimate /log probes succeeding while the vulnerable server is saturated, versus 20/20 succeeding with the fixed server wrapper.

I am not claiming default public internet exposure. The default bind is localhost:8080. The affected condition is a REST API listener exposed through Klever's documented all-interface bind or Docker port-publish deployment shape.

This maps to the SECURITY.md High category: "Denial of Service affecting network availability." If Klever treats externally reachable REST API unavailability as non-critical because the default bind is localhost, the conservative classification is Medium under "Performance degradation attacks" / "Non-critical DoS vectors."

All testing was local loopback only. I did not contact Klever mainnet, public testnet, hosted RPCs, explorers, or third-party production infrastructure.

Suggested fix:

Start both REST APIs through explicit http.Server values instead of Engine.Run, for example:

srv := &http.Server{
    Addr:              restAPIInterface,
    Handler:           ws.Handler(),
    ReadHeaderTimeout: 5 * time.Second,
    ReadTimeout:       10 * time.Second,
    WriteTimeout:      30 * time.Second,
    IdleTimeout:       120 * time.Second,
    MaxHeaderBytes:    32 << 10,
}
return srv.ListenAndServe()

Apply the same pattern to:

  • cmd/seednode/api.Start
  • network/api.Start

If Klever expects deployments to expose the REST API through a reverse proxy, I still recommend setting server-level limits in the application. That keeps the binary safe when operators use the documented direct bind or Docker port-publish path.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 1.7.17"
      },
      "package": {
        "ecosystem": "Go",
        "name": "github.com/klever-io/klever-go"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "1.7.14"
            },
            {
              "fixed": "1.7.18"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-52880"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-400",
      "CWE-770"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-05T16:41:58Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "### Summary\n\nThe Klever seednode REST API starts a Gin engine with `Engine.Run(restAPIInterface)`. In Gin v1.9.1, `Engine.Run` calls Go\u0027s default `http.ListenAndServe`, which constructs an HTTP server without application-level `ReadHeaderTimeout`, `ReadTimeout`, or `MaxHeaderBytes` limits.\n\nAn unauthenticated client that can reach a REST listener bound with Klever\u0027s documented `--rest-api-interface :8080` all-interface option can hold incomplete HTTP headers open indefinitely. In a local proof against the real `cmd/seednode/api.Start` path on `v1.7.17`, 120 slow-header connections caused 20/20 legitimate `/log` probes to fail with `accept: too many open files`. A fixed control using the same Gin router behind an explicit `http.Server` with `ReadHeaderTimeout`, `ReadTimeout`, and `MaxHeaderBytes` retained 0 slow connections and served 20/20 probes.\n\nThis report is distinct from the P2P advisories and from my direct-message goroutine report. This finding concerns Klever-owned HTTP REST startup code (`cmd/seednode/api` and `network/api`) using Gin `Engine.Run` without server-level header deadlines. It does not depend on `MultiDataInterceptor`, `Batch.Decompress`, libp2p, malformed P2P messages, or direct-message goroutine spawning.\n\n### Details\n\nSeednode REST API, latest release `v1.7.17`:\n\n- `cmd/seednode/api/api.go:17` defines `Start(restAPIInterface, marshalizer)`.\n- `cmd/seednode/api/api.go:18` creates `ws := gin.Default()`.\n- `cmd/seednode/api/api.go:23` returns `ws.Run(restAPIInterface)`.\n- `cmd/seednode/CLI.md:23` documents `--rest-api-interface`; it says `:8080` binds all interfaces and `off` disables the API.\n\nNode REST API, latest release `v1.7.17`:\n\n- `network/api/api.go:79` creates `ws = gin.Default()`.\n- `network/api/api.go:98` returns `ws.Run(kleverFacade.RestAPIInterface())`.\n- `cmd/node/main.go:147-150` documents the same `--rest-api-interface` flag and says `:8080` binds all interfaces.\n- `docker/README.md:56-61` and `docker/README.md:67-70` publish host port `8080` for full-node and validator Docker examples.\n- `README.md:264-268` documents that the node exposes a REST API for blockchain queries and operations.\n\nThe seednode REST API source is byte-identical across `v1.7.14` through `v1.7.17`; the captured runtime PoC was executed on `v1.7.17`.\n\nCurrent `develop` commit `10bcfd50` remains affected:\n\n- `network/api/api.go:98` still returns `ws.Run(kleverFacade.RestAPIInterface())`.\n- `cmd/seednode/api/api.go:59` still returns `ws.Run(restAPIInterface)`.\n\nGin v1.9.1 implements `Engine.Run` as:\n\n```go\nfunc (engine *Engine) Run(addr ...string) (err error) {\n    address := resolveAddress(addr)\n    err = http.ListenAndServe(address, engine.Handler())\n    return\n}\n```\n\nIn my source sweep, I did not find a production `http.Server{ReadHeaderTimeout: ...}` wrapper for either REST start path. The only `ReadHeaderTimeout` hit I found in the repository was a test helper under `network/api/websocket/routes_test.go`.\n\n### PoC\n\nGitHub Private Vulnerability Reporting does not appear to allow file attachments in this form, so I am including the reproduction command and captured output inline. I can paste the full 254-line Go test patch in a reply immediately if useful.\n\nThe test starts two local child servers:\n\n1. Vulnerable: the real `cmd/seednode/api.Start` path.\n2. Fixed control: the same Gin router served through `http.Server{ReadHeaderTimeout: 250ms, ReadTimeout: 250ms, MaxHeaderBytes: 4096}`.\n\nReproduction from a clean checkout:\n\n```bash\ngit clone https://github.com/klever-io/klever-go\ncd klever-go\ngit checkout v1.7.17\n\n# Apply the PoC patch to cmd/seednode/api.\n# I can provide the full patch in this advisory thread.\n\ngo test ./cmd/seednode/api -run TestPoC_SeednodeAPISlowlorisDifferential -count=1 -v -timeout 60s\n```\n\nCaptured output on `v1.7.17`:\n\n```text\nPOC_RESULT mode=vulnerable slow_connections_opened=120 slow_connections_still_open=111 legitimate_probe_ok=0 legitimate_probe_fail=20\nPOC_RESULT mode=fixed slow_connections_opened=120 slow_connections_still_open=0 legitimate_probe_ok=20 legitimate_probe_fail=0\n```\n\nThe vulnerable server also logs repeated accept failures:\n\n```text\nhttp: Accept error: accept tcp 127.0.0.1:56415: accept: too many open files; retrying in 1s\n```\n\n### Impact\n\nFor an externally reachable Klever REST listener, a single unauthenticated client can retain many server-side connections by never completing HTTP headers. Because the Go server has no read-header deadline, those connections persist until the client closes them or an external proxy/firewall intervenes.\n\nThe direct result is REST API unavailability for legitimate clients. The local proof demonstrates this as 0/20 legitimate `/log` probes succeeding while the vulnerable server is saturated, versus 20/20 succeeding with the fixed server wrapper.\n\nI am not claiming default public internet exposure. The default bind is `localhost:8080`. The affected condition is a REST API listener exposed through Klever\u0027s documented all-interface bind or Docker port-publish deployment shape.\n\nThis maps to the `SECURITY.md` High category: \"Denial of Service affecting network availability.\" If Klever treats externally reachable REST API unavailability as non-critical because the default bind is localhost, the conservative classification is Medium under \"Performance degradation attacks\" / \"Non-critical DoS vectors.\"\n\nAll testing was local loopback only. I did not contact Klever mainnet, public testnet, hosted RPCs, explorers, or third-party production infrastructure.\n\nSuggested fix:\n\nStart both REST APIs through explicit `http.Server` values instead of `Engine.Run`, for example:\n\n```go\nsrv := \u0026http.Server{\n    Addr:              restAPIInterface,\n    Handler:           ws.Handler(),\n    ReadHeaderTimeout: 5 * time.Second,\n    ReadTimeout:       10 * time.Second,\n    WriteTimeout:      30 * time.Second,\n    IdleTimeout:       120 * time.Second,\n    MaxHeaderBytes:    32 \u003c\u003c 10,\n}\nreturn srv.ListenAndServe()\n```\n\nApply the same pattern to:\n\n- `cmd/seednode/api.Start`\n- `network/api.Start`\n\nIf Klever expects deployments to expose the REST API through a reverse proxy, I still recommend setting server-level limits in the application. That keeps the binary safe when operators use the documented direct bind or Docker port-publish path.",
  "id": "GHSA-w4c6-7r69-w7j9",
  "modified": "2026-06-09T18:40:44Z",
  "published": "2026-06-05T16:41:58Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/klever-io/klever-go/security/advisories/GHSA-w4c6-7r69-w7j9"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/klever-io/klever-go"
    },
    {
      "type": "WEB",
      "url": "https://github.com/klever-io/klever-go/releases/tag/v1.7.18"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "klever-go: REST API slow-header connection exhaustion via Gin Engine.Run"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…