GHSA-9wh6-9hq7-9688: Input Validation

Published Sep 23, 2026
·
Updated

Location: core/kapp/validators/validators.go:201 (Register), (genesis/checking/nodesSetupChecker.go:73). core/consensus/slot/bls/subslotStartSlot.go:165 core/consensus/.../headerSignatureVerify.go:123 (Create(...)).

Description

Klever uses a BDN (Boneh-Drijvers-Neven) BLS multi-signature over BLS12-381 to finalize blocks (crypto/signing/mcl/multisig/bls.go, herumi/bls-go-binary). The library is initialized with only bls.Init(bls.BLS12381) and no order-verification flags, so Deserialize does not enforce prime-order-subgroup membership subgroup safety relies on explicit IsValidOrder() calls in the wrappers.

Runtime validator registration (validators.Register) stores the submitted 96-byte BLSPublicKey via SetBLSPublicKey with no curve check, no subgroup check, and no proof-of-possession (CreateValidatorContract carries the key but no signature proving key ownership; grep confirms no proofOfPossession/VerifyProof/BLSSignature verification anywhere in core/kapp/validators/). CheckPublicKeyValid — which does IsValid && IsValidOrder && !IsZero — runs only at genesis, not on runtime registration/update.

POC

Attack: (1) stake the minimum to register a validator, submitting a 96-byte BLSPublicKey that is not a valid G2 point (arbitrary bytes). Registration succeeds. (2) Once the validator is eligible and selected into a consensus group, every in-group node including the honest leader (group[0]) calls MultiSigner().Reset(groupPubKeys, selfIndex) at slot start, which deserializes all group keys via PublicKeyFromByteArray → herumi Deserialize deterministically fails on the malformed key → SetSlotCanceled(true). The verify side (Create(consensusPubKeys)) fails identically. No block is produced for that round.

Impact

Every consensus round whose group contains the malformed-key validator is a missed slot. One eligible bad-key validator poisons roughly a groupSize / eligibleSet fraction of rounds → sustained liveness degradation. Where the consensus group equals the eligible set (small or early-stage networks), this is a full chain halt. Cost is the minimum validator stake, permissionless, and repeatable; no fork flag gates the missing validation.

Rated High (Byzantine liveness; Critical on small validator sets). The severity scales down to a fractional missed-slot / throughput-degradation attack on a large validator set where the bad validator is only occasionally in the active group.

Recommendation

Enforce CheckPublicKeyValid (curve + prime-order subgroup + non-zero) on the submitted BLS key at runtime validator registration and config-update, and ideally require a proof-of-possession (a BLS signature over the validator's own key/identity) at registration to prove key ownership and well-formedness. Gate the stricter validation behind an epoch fork flag for reprocessing consistency.

Affected Software

1 affected componentFixes available
go/github.com/klever-io/klever-go<=1.7.19
1.7.20

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade go/github.com/klever-io/klever-go to a version that resolves this vulnerability.

    Fixed in 1.7.20
  2. Configuration

    Gate the stricter BLS public-key validation behind an epoch fork flag so the validation change is applied consistently during reprocessing.

    epoch fork flag = enabled
  3. Compensating control

    Enforce CheckPublicKeyValid on submitted BLS public keys during runtime validator registration and configuration updates; require curve validity, prime-order subgroup membership via IsValidOrder, and a non-zero key.

  4. Compensating control

    Require proof-of-possession at validator registration using a BLS signature over the validator's own public key or identity, and verify the proof before accepting the validator.

Event History

Sep 23, 2026
Advisory Published
via GitHub·09:24 PM
Data Sourced
via GitHub·09:24 PM
DescriptionWeaknessAffected Software

Frequently Asked Questions

1

Are validators defined at genesis affected in the same way as validators registered later?

Genesis validation calls CheckPublicKeyValid, which checks that the key is valid, in the correct subgroup, and nonzero. Those checks are not performed for runtime validator registration or update.

2

What does an attacker need to submit through the runtime registration path?

The runtime registration path accepts a submitted 96-byte BLS public key and stores it through SetBLSPublicKey. It does not perform a curve check, subgroup check, or proof-of-possession verification.

3

Does the BLS library configuration itself enforce subgroup membership during deserialization?

No. The library is initialized only with bls.Init(bls.BLS12_381) and without order-verification flags, so Deserialize does not enforce prime-order-subgroup membership.

4

Is proof of ownership for a validator BLS key checked when creating a validator?

No. CreateValidatorContract carries the public key without a signature proving ownership, and the provided data identifies no proof-of-possession or BLS signature verification in the validator code.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203