CVE-2026-54754: Klever-Go: Marketplace settlement mints KLV when referral % + royalty % exceed the bid (negative seller share silently skipped)

Published Aug 28, 2026
·
Updated

Summary

When a marketplace order is settled (MarketBuy / BuyItNow, and auction Claim), the buyer's payment is split three ways — referral, royalties, and the seller (market-order owner) remainder:

marketOwnerAmount = CurrentBid − referralAmount − royaltiesAmount

Referral and royalties are paid out unconditionally, but the seller remainder is only paid when positive (computeMarketOwnerAmount returns Ok and pays nothing when the amount is <= 0). When referral% + royalty% exceeds 100% of the bid, marketOwnerAmount goes negative and is silently skipped — so the marketplace pays out more KLV / sale currency than the buyer paid in, minting the difference out of thin air.

The combined ceiling royalty% + referral% <= 100% is checked once, at listing time (Sell). But the two percentages are sourced asymmetrically at settlement:

- referral % is snapshotted into the order at Sell (MarketOrderData.ReferralPercentage); - royalty % is never snapshotted — it is read live from the asset at buy time (asset.Royalties.MarketPercentage).

So the listing-time invariant is a time-of-check/time-of-use guarantee only. After a valid listing, the asset owner raises the royalty MarketPercentage via AssetTrigger → UpdateRoyalties; at the next buy the live royalty plus the snapshotted referral exceed 100%, and the settlement mints the overflow. The minted funds land in attacker-controlled referral / royalty addresses.

This was actively exploited on mainnet (see Evidence), minting tens of millions of KLV before the emergency guard was deployed.

Affected component

- Repository: klever-io/klever-go (node). - Settlement / mint site: core/kapp/market/market.go — executeBuyMarket (L575+), computeReferralAmount (L361+), computeRoyaltiesAmount (L490+), computeRoyaltiesFixedDeposit (L443+), computeMarketOwnerAmount (L540+). - TOCTOU sources: Sell combined check (market.go:908), order snapshot of referral but not royalty (market.go:997), live royalty mutation via core/kapp/kda/trigger.go — handleUpdateRoyaltiesNFTandSFT (L613+, sets asset.Royalties.MarketPercentage at L670). - Reachable from both Buy (BuyItNow, market.go:204+) and auction Claim (market.go:705, market.go:731). - Pre-fix: not gated by any fork flag — exploitable on mainnet. The fix is gated behind the new FixMarketBuyOverflow activation-epoch flag.

---

Root cause

1. Settlement pays referral + royalty unconditionally, seller remainder only if positive core/kapp/market/market.go — executeBuyMarket (L575+): go referralAmount, := tools.ComputePercentageI64(marketOrder.CurrentBid, int64(marketOrder.ReferralPercentage), ...) // L583: SNAPSHOT referral % royaltiesAmount, := tools.ComputePercentageI64(marketOrder.CurrentBid, int64(asset.Royalties.MarketPercentage), ...) // L587: LIVE royalty % marketOwnerAmount := marketOrder.CurrentBid - referralAmount - royaltiesAmount // L591: can go negative

// ---- FIX (FixMarketBuyOverflow), added by the patch ---- if m.forkController.FixMarketBuyOverflow() && marketOwnerAmount < 0 { // L593-596 ctx.Receipts().AddError(ctx.ContractID(), common.ErrFieldInvalidRoyalties, common.ErrInvalidValue.Error()) return transaction.TransactionAmountInvalid, common.ErrInvalidValue }

m.computeReferralAmount(ctx, marketOrder, referralAmount, currencyID) // pays referral in full m.computeRoyaltiesFixedDeposit(ctx, marketOrder, asset) // pays fixed royalty (KLV) m.computeRoyaltiesAmount(ctx, marketOrder, asset, currencyID, royaltiesAmount) // pays % royalty in full m.computeMarketOwnerAmount(ctx, marketOrder, currencyID, marketOwnerAmount) // <-- skips when <= 0 computeMarketOwnerAmount (L540-542) — the silent skip: go func (m marketKapp) computeMarketOwnerAmount(... marketOwnerAmount int64) (... , error) { if marketOwnerAmount <= 0 { return transaction.TransactionOk, nil // negative seller share dropped, NO error } // ... AddToBalance(marketOwnerAmount) ... } Meanwhile computeReferralAmount (L376) and computeRoyaltiesAmount (L515) each AddToBalance(...) the full computed amount with no matching debit from the buyer beyond the single bidderAcc.SubFromBalance(amount) taken in Buy (market.go:301).

Conservation breaks: buyer is debited bid once; recipients are credited referralAmount + royaltiesAmount. When that sum > bid, the surplus (referralAmount + royaltiesAmount − bid) is minted.

2. The combined ≤100% invariant is enforced only at listing time Sell (market.go:908) correctly rejects a listing whose combined cut exceeds 100%: go if asset.Royalties.MarketPercentage + marketplace.ReferralPercentage > core.HundredPercent { return transaction.TransactionParameterInvalid, common.ErrInvalidValue } …and snapshots referral into the order, but not royalty (market.go:997-998): go marketOrder := &kapps.MarketOrderData{ // ... ReferralPercentage: marketplace.ReferralPercentage, // snapshotted RoyaltiesFixedDeposit: asset.Royalties.MarketFixed, // snapshotted // NOTE: asset.Royalties.MarketPercentage is NOT snapshotted -> read live at buy } MarketOrderData has no field for the royalty percentage (kapps/market.pb.go), so settlement always re-reads it live from the (mutable) asset.

3. Royalty % is mutable after listing core/kapp/kda/trigger.go — handleUpdateRoyaltiesNFTandSFT (L613+) lets the asset owner overwrite asset.Royalties.MarketPercentage (L670) with only a per-field <= 100% check (CheckValid100Params, L651) — it has no knowledge of any outstanding marketplace listing's snapshotted referral. So the owner can list at, e.g., referral 100% / royalty 0% (sum 100%, passes Sell), then raise royalty to 100%, making the buy-time sum 200%.

The shipped emergency-guard source documents this exact vector: "The royalty percentage is read live at buy time, so a listing made now can be weaponised later via UpdateRoyalties." (common/emergencyGuard.go)

Net effect: referralAmount + royaltiesAmount = bid + bid = 2·bid; marketOwnerAmount = −bid (skipped); bid KLV minted per settlement, paid to attacker-controlled addresses.

---

Proof of Concept

A. Committed regression test (deterministic, runnable today) core/kapp/market/markettest.go — TestMarketKAppExecuteBuyMarketRoyaltyReferralInflation. It builds an order with ReferralPercentage = 100% and an asset with MarketPercentage = 100% (the attacker is both the referral and the royalty address), then settles a bid of 25,600,000 KLV (25600000000000 base units):

bash go test ./core/kapp/market/ -run TestMarketKAppExecuteBuyMarketRoyaltyReferralInflation -v

- FixDisabledMintsKLVFromThinAir: settlement returns Ok; the attacker address ends with 2·bid credited while only bid was paid in — i.e. bid KLV minted. - FixEnabledRejectsInflation: with FixMarketBuyOverflow on, settlement returns TransactionAmountInvalid and the attacker balance stays 0 — no payout runs.

B. End-to-end on a local node (the real attack path) A single-node local network is sufficient. The exploit is four transactions from one ordinary funded account; nothing privileged is required.

1. Create an NFT collection you own, with royalties.marketPercentage = 0 and a royalties address you control. 2. Create a marketplace with referralPercentage = 10000 (100%) and a referral address you control (CreateMarketplace). 3. List one NFT for sale (Sell) on that marketplace. The Sell check passes because 0 (royalty) + 10000 (referral) = 10000 = HundredPercent. The order snapshots ReferralPercentage = 10000. 4. Raise the royalty on the asset to 100% (AssetTrigger / UpdateRoyalties, marketPercentage = 10000). Allowed: the per-field check passes and the live combined invariant is never re-evaluated against the open listing. 5. Buy the listing (MarketBuy) from a second account (or settle the auction via Claim). referralAmount = bid, royaltiesAmount = bid, marketOwnerAmount = −bid (skipped). Your referral + royalty addresses receive 2·bid; the buyer paid bid; bid KLV is minted.

Because the attacker controls buyer, seller, referral and royalty addresses, the only real cost is transaction fees; the cycle is repeatable until supply targets are met.

---

Evidence

Regression test (local, verbatim) === RUN TestMarketKAppExecuteBuyMarketRoyaltyReferralInflation === RUN TestMarketKAppExecuteBuyMarketRoyaltyReferralInflation/FixDisabledMintsKLVFromThinAir === RUN TestMarketKAppExecuteBuyMarketRoyaltyReferralInflation/FixEnabledRejectsInflation --- PASS: TestMarketKAppExecuteBuyMarketRoyaltyReferralInflation (0.00s) --- PASS: TestMarketKAppExecuteBuyMarketRoyaltyReferralInflation/FixDisabledMintsKLVFromThinAir (0.00s) --- PASS: TestMarketKAppExecuteBuyMarketRoyaltyReferralInflation/FixEnabledRejectsInflation (0.00s) PASS ok github.com/klever-io/klever-go/core/kapp/market 0.279s FixDisabled asserts the attacker balance equals 2·bid = 51,200,000 KLV for a single settlement (bid = 25,600,000 KLV), with bid of that minted. FixEnabled asserts rejection and a 0 balance.

Mainnet exploitation (observed) The bug was exploited in production, and was detected and characterised externally by the community monitoring project KleverPuls / kpulse.tech before the root cause was known internally. Over a ~24h window kpulse isolated a single wallet (opened 2026-06-04, ~204 transactions in ~24h, funded only by a ~242K KLV KuCoin withdrawal, no treasury/foundation funding) that:

- self-issued 3 NFT collections named "InflationPOC" and wash-traded one (NFLATION-ESGO/1) 29 times through self-created marketplaces — ~$450K of artificial, economically empty NFT volume; - swapped the proceeds KLV → USDC / USDT / WBTC / WETH on KleverSwap and bridged ~$72K of value to Ethereum via wrapped-asset burns over 24h (USDC −12,487 ≈ $12.5K; USDT −26,362 ≈ $26.4K; WBTC −0.31 ≈ $19.6K; WETH −7.55 ≈ $14K); - surfaced a spurious "1.86B KLV outflow" headline that kpulse correctly identified as a wash-trade receipt-doubling artifact with small real net KLV flow.

That "doubling of marketplace receipts" is precisely the on-chain signature of this bug: each abusive settlement pays out a referral cut (bid) plus a royalty cut (bid) while the buyer paid only bid once — the market contract emits ~2× the value it took in, which is the mint. The attacker's self-issued collection and self-created marketplaces are exactly the self-dealing setup the regression test reproduces (the test reuses the real on-chain identifiers: collectionID = "NFLATION-ESGO", asset 1, market name "Inflation Market").

kpulse could not determine the cause from on-chain data alone and flagged the activity for confirmation; the Klever core team then traced it to the referral+royalty settlement defect described above and shipped the emergency guard + protocol fix.

Each abusive settlement minted one bid of KLV; the observed bid was 25,600,000 KLV, repeated and funnelled through a short hop chain before being swapped and bridged. The emergency guard (common/emergencyGuard.go) blocks the following observed sender public keys (hex):

| Public key (hex) | Address | Role (observed) | |---|---|---| | 54ea28e527d4136508be955374afa54a8c25c19a48c674f412f7ce02db0f4e1b | klv12n4z3ef86sfk2z97j4fhfta9f2xztsv6frr8faqj7l8q9kc0fcdsfjfqez | root / minter | | bb687dbba23e1844fec674a32cb8809f0d3207506c53fc3d637e40dc56708d63 | klv1hd58mwaz8cvyflkxwj3jewyqnuxnyp6sd3flc0tr0eqdc4ns343skngdjq | collector hop (~125M KLV) | | 77388d3dfe6cd88e8da723254c11abf3d9cccb6fb77b000e5038fc3ff92b964d | klv1wuug6007dnvgard8yvj5cydt70vuejm0kaasqrjs8r7rl7ftjexsglalf6 | direct recipient (25.6M, idle) | | a196789b026f996867f08317cc6c5a4eb9ad3a59b1be3716420bc8692d4c3048 | klv15xt83xczd7vkselssvtucmz6f6u66wjekxlrw9jzp0yxjt2vxpyq2nawrw | hop-2 recipient (25M) |

The single-bid per-settlement size (25.6M KLV) matches the "direct recipient, 25.6M" entry, and the ~125M at the collector hop is consistent with roughly five abusive settlements.

---

Impact

- Unbounded inflation of KLV (and of any sale currency used for the listing), repeatable for only transaction fees, by any account that creates its own collection + marketplace. - The minted KLV is created by direct AddToBalance to attacker addresses (no tracked Mint), so the asset's booked supply does not change — the inflation is off the books and only detectable by summing balances / auditing receipts (it surfaces on-chain as doubled marketplace receipts). - Realized impact (observed): the attacker minted KLV via ~29 self-dealt settlements, swapped to stable/wrapped assets on KleverSwap, and off-ramped ~$72K to Ethereum via the bridge (USDC, USDT, WBTC, WETH) before the emergency guard halted the activity, alongside ~$450K of artificial NFT wash-trade volume. - Total loss of economic integrity for all KLV / token holders.

Who can exploit it / prerequisites

- Any account that can pay the one-time collection-create + marketplace-create fees and tx fees. No roles, admin, or allowlist. - Any client. The settlement is triggered by standard MarketBuy / Claim contracts POSTed to the public, unauthenticated /transaction/send RPC (network/api/transaction/routes.go, SendTX / BroadcastTX). The operator CLI, the SDKs, or a hand-signed curl all work. - Deterministic; the abusive state is reached with one extra UpdateRoyalties after a normal listing.

---

Remediation

Shipped as a layered response (embargoed):

Layer 0 — emergency guard (deployed first, fork-proof) — GHSA-p7gw rc1 common/emergencyGuard.go + data/transaction/emergencyGuard.go: matching transactions are kept out of blocks this node proposes (core/process/block/preprocess/transactions.go) and refused at the node API (node.go SendTransaction / SendBulkTransactions). It never changes block validity, so a partial-fleet rollout cannot fork the chain. It blocks the known attacker senders (all contract types) plus all MarketBuy, Sell, and CreateMarketplace / ConfigMarketplace operations while the protocol fix rolls out. Enforcement is by proposer cooperation, not protocol — coverage equals the share of block producers running the guard.

Layer 1 — protocol fix (consensus, epoch-gated) — GHSA-p7gw rc2 core/kapp/market/market.go:593 rejects the settlement when marketOwnerAmount < 0, before any payout runs, returning TransactionAmountInvalid. Gated behind the new FixMarketBuyOverflow activation-epoch flag (config/enableEpochs., core/fork/forks.go, core/interface.go) so historical blocks reprocess identically. Covered by TestMarketKAppExecuteBuyMarketRoyaltyReferralInflation.

Recommended hardening (defense in depth) 1. Snapshot the royalty % into MarketOrderData at Sell (as referral already is) and pay from the snapshot, eliminating the TOCTOU entirely; or re-evaluate the combined referral% + royalty% <= 100% invariant at settlement. 2. Treat a negative seller remainder as a hard error everywhere, and only treat exactly 0 as a no-op skip (computeMarketOwnerAmount), so a future regression aborts the tx instead of minting. 3. After splitting a payment pool, assert conservation (referral + royalties + ownerShare == bid) so any drift aborts the transaction.

---

Notes

- Triggered by both BuyItNow (Buy) and auction settlement (Claim). - The same family of "pay full cut, silently drop the negative remainder" minting also exists in the royalty-split paths and is tracked separately under GHSA-cgc5-v3f2-8m2v (split-royalty uint32 overflow). This advisory covers the top-level referral+royalty > bid case; the FixMarketBuyOverflow guard here only checks marketOwnerAmount, not intra-split over-payments.

Acknowledgments

- KleverPuls / kpulse.tech — community monitoring project that first detected and characterised the exploitation in the wild. kpulse isolated the attacker wallet and its "InflationPOC" collections, identified the marketplace wash-trading of NFLATION-ESGO/1 and the KleverSwap → bridge off-ramp (~$72K to Ethereum), and flagged the anomalous doubling of marketplace receipts — the exact on-chain signature of this bug — prompting the incident response that led to this fix. The root cause was then identified and remediated by the Klever core team.

Source

- Vulnerable / fixed code: core/kapp/market/market.go:540-545,575-596,908,997-998, core/kapp/kda/trigger.go:613-676, core/process/kda/assetHelper.go:101, tools/converters.go:102, core/constants.go:18. - Emergency guard: common/emergencyGuard.go, data/transaction/emergencyGuard.go, core/process/block/preprocess/transactions.go, node/node.go. - Fork flag: config/enableEpochs.go, config/node/enableEpochs.yaml, core/fork/forks.go, core/interface.go. - Regression test: core/kapp/market/markettest.go (TestMarketKAppExecuteBuyMarketRoyaltyReferralInflation).

Other sources

Klever-Go is the Go implementation of the Klever blockchain protocol. Prior to 1.7.19, marketplace settlement in core/kapp/market/market.go reads MarketOrderData.ReferralPercentage from the listing while reading asset.Royalties.MarketPercentage live at purchase time. An asset owner can create a valid listing and then use AssetTrigger UpdateRoyalties to make the combined referral and royalty percentages exceed the bid. executeBuyMarket pays referral and royalty amounts unconditionally while computeMarketOwnerAmount silently skips a nonpositive seller remainder, allowing MarketBuy, BuyItNow, or auction Claim settlement to credit more KLV or sale currency than the buyer paid. This can create unbacked currency and corrupt token supply integrity. 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-io/klever-go to a version that resolves this vulnerability.

    Fixed in 1.7.19
  3. Upgrade

    Upgrade to a fixed release to a version that resolves this vulnerability.

    Patch FixMarketBuyOverflow
  4. Configuration

    Enable the FixMarketBuyOverflow guard (activation-epoch flag) so that settlement rejects when marketOwnerAmount is negative (rather than silently skipping nonpositive seller remainder).

    Klever core (market settlement) FixMarketBuyOverflow activation-epoch flag = On
  5. Configuration

    Implement/ensure the settlement path returns an error when marketOwnerAmount < 0 (FixMarketBuyOverflow), before any payout, covering MarketBuy, BuyItNow, and auction Claim.

    Sell / MarketBuy / BuyItNow / auction Claim settlement Check for marketOwnerAmount negative = Reject
  6. Compensating control

    Deploy/ensure the emergency guard is active (common/emergencyGuard.go and data/transaction/emergencyGuard.go) to block known attacker sender public keys listed in the advisory.

  7. Operational

    Reprocess/verify historical blocks against the fix (as the advisory notes historical blocks reprocess identically) and run the regression test TestMarketKApp_ExecuteBuyMarket_RoyaltyReferralInflation to confirm the inflation path is rejected when the fix is enabled.

Event History

Aug 28, 2026
CVE Published
via MITRE·04:20 PM
Data Sourced
via MITRE·04:20 PM
DescriptionSeverityWeakness
Advisory Published
via GitHub·04:22 PM
Data Sourced
via GitHub·04:22 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

What conditions make a listed order exploitable?

The order’s snapshotted referral percentage plus the asset’s live royalty percentage must exceed 100% when the order is settled. This can occur if the royalty percentage changes after listing, because the royalty value is read at settlement while the referral value remains the value captured at listing.

2

What access does an attacker need?

The vulnerability is network-reachable, has low attack complexity, requires low privileges, and does not require user interaction. The supplied data does not specify which role or operation can change the live royalty percentage.

3

How can operators identify orders at risk or prior impact?

Review open orders by comparing each order’s stored referral percentage with the current asset royalty market percentage. Any combined value above 100% is at risk; completed settlements in that condition may show referral and royalty payouts exceeding the buyer’s payment while the seller remainder was not paid.

4

What is the impact when the combined percentages exceed 100%?

Referral and royalty payments are made unconditionally, but the negative seller remainder is skipped. This causes the marketplace to pay more of the sale currency than the buyer supplied, while the market-order owner receives no remainder.

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