critical · 9.6CVE-2026-54754Aug 28, 2026

CVE-2026-54754: klever-go Marketplace Settlement Integer Underflow Mints KLV

Shubham Kandhare
Security Engagement Manager, SecureLayer7

A flaw in the Klever blockchain marketplace settlement logic lets an attacker set royalty and referral percentages that together exceed 100%, causing the node to pay out more KLV than it collected…

Packagegithub.com/klever-io/klever-go
Ecosystemgo
Affected< 1.7.19
Fixed in1.7.19
CVE-2026-54754: klever-go Marketplace Settlement Integer Underflow Mints KLV

The problem

When a MarketBuy or auction Claim settles, the node splits the buyer's bid into referral, royalty, and seller shares. Referral and royalty are paid unconditionally; the seller's remainder is only paid when positive. If the two cuts together exceed 100% of the bid, the seller share goes negative and is silently skipped, so recipients are credited more than the buyer paid.

The combined ceiling (referral% + royalty% <= 100%) is checked once, at listing time (Sell). But referral% is snapshotted into the order while royalty% is always read live from the asset at buy time. An attacker lists with royalty 0% then raises it to 100% via AssetTrigger/UpdateRoyalties after the listing passes validation.

The next buy mints one full bid per settlement. This was actively exploited on mainnet before the emergency guard shipped.

Proof of concept

A working proof-of-concept for CVE-2026-54754 in github.com/klever-io/klever-go, with the exact payload below.

bash
# Four-transaction attack using any funded account
# No special privileges required.

# 1. Create an NFT collection you own.
#    Set royalties.marketPercentage = 0 and point royaltiesAddress to yourself.
#    (collectionID used by the attacker: NFLATION-ESGO)

# 2. Create a marketplace with referralPercentage = 10000 (= 100%).
#    Point referralAddress to yourself.
#    CreateMarketplace contract — passes Sell-time check: 0 + 10000 == HundredPercent.

# 3. List one NFT for sale on that marketplace (Sell).
#    Sell check: asset.Royalties.MarketPercentage (0) + marketplace.ReferralPercentage (10000)
#               = 10000 == HundredPercent  -> OK
#    Order snapshot: ReferralPercentage = 10000
#                    asset.Royalties.MarketPercentage NOT snapshotted (read live at buy time)

# 4. Raise the asset royalty to 100% (AssetTrigger / UpdateRoyalties).
#    Per-field check: 10000 <= 10000 -> OK
#    No re-check against the open listing's snapshotted referral.

# 5. Buy the listing (MarketBuy) from any second account.
#    Settlement in executeBuyMarket (market.go:575+):
#      referralAmount  = ComputePercentageI64(bid, 10000)  = bid   // from snapshot
#      royaltiesAmount = ComputePercentageI64(bid, 10000)  = bid   // from LIVE asset
#      marketOwnerAmount = bid - bid - bid = -bid
#      computeMarketOwnerAmount: marketOwnerAmount <= 0 -> return Ok (silent skip, no debit)
#      referral  address credited: +bid
#      royalties address credited: +bid
#      buyer debited:               -bid
#    Net: bid KLV minted per settlement.
#    Repeat until supply targets are met. Only cost is tx fees.

# Regression test (runs on the patched repo with FixMarketBuyOverflow disabled):
# go test ./core/kapp/market/ -run TestMarketKApp_ExecuteBuyMarket_RoyaltyReferralInflation -v
# bid = 25,600,000 KLV; FixDisabled subtest asserts attacker balance == 2*bid (51,200,000 KLV)
# FixEnabled subtest asserts Transaction_AmountInvalid and attacker balance == 0

The root cause is a TOCTOU gap combined with an integer underflow that is silently tolerated. Referral percentage is snapshotted at listing time, but royalty percentage is always fetched live from the mutable asset at settlement. The Sell-time combined cap (CWE-367) is therefore bypassed by updating the royalty after listing.

The resulting negative seller remainder (CWE-191) is treated as a no-op by computeMarketOwnerAmount (market.go:540), which returns Ok and pays nothing to the seller, while the referral and royalty payouts already ran in full, crediting more than the buyer paid.

The patch (commit 8bcc600) adds a guard at market.go:593 behind the new FixMarketBuyOverflow fork flag: if marketOwnerAmount < 0, settlement returns Transaction_AmountInvalid before any payout runs. The correct long-term fix is to also snapshot the royalty percentage into MarketOrderData at Sell time, eliminating the TOCTOU entirely.

The fix

Upgrade to klever-go v1.7.19. The fix is in commit 8bcc600b0ac88070740c63c7ce1c8a968dd85251. It adds a pre-payout guard in executeBuyMarket that rejects settlement when marketOwnerAmount < 0, gated behind the FixMarketBuyOverflow activation-epoch flag so historical blocks reprocess correctly.

An emergency guard (common/emergencyGuard.go) was also deployed first as a proposer-side block filter while the protocol fix rolled out.

Reported by KleverPuls / kpulse.tech.

References: [1][2][3][4]

Related research