CVE-2026-54754

github.com/klever-io/klever-go: Integer Underflow (Wrap or Wraparound) (CVE-2026-54754)

criticalEPSS 0.3 %

Beschreibung

Zusammenfassung

Wenn ein Marktplatz-Auftrag abgewickelt wird (MarketBuy / BuyItNow, und Auktion Claim), wird der Kaufpreis des Käufers in drei Teile aufgeteilt — Empfehlungsprovision, Royalties und den Restbetrag für den Verkäufer (Marktplatz-Auftragseigentümer):

marketOwnerAmount = CurrentBid − referralAmount − royaltiesAmount

Die Empfehlungsprovision und die Royalties werden unbedingt ausgezahlt, aber der Restbetrag für den Verkäufer wird nur dann ausgezahlt, wenn er positiv ist (computeMarketOwnerAmount gibt Ok zurück und zahlt nichts aus, wenn der Betrag <= 0 ist). Wenn die Summe von referral% + royalty% mehr als 100 % des Gebots beträgt, wird der marketOwnerAmount negativ und wird stillschweigend übersprungen — so dass der Marktplatz mehr KLV / Verkaufswährung auszahlt, als der Käufer bezahlt hat, indem er den Unterschied aus dem Nichts erschafft.

Die kombinierte Obergrenze royalty% + referral% <= 100% wird einmal überprüft, zur Zeit der Auflistung (Sell). Aber die beiden Prozentsätze werden asymmetrisch bei der Abwicklung bezogen:

  • Der Empfehlungsprozentsatz wird in den Auftrag abgebildet bei Sell (MarketOrderData.ReferralPercentage);
  • Der Royalty-Prozentsatz wird nie abgebildet — er wird live aus dem Asset zur Zeit des Kaufs gelesen (asset.Royalties.MarketPercentage).

Daher ist die Invariante zur Auflistungszeit nur eine Garantie für Zeit der Überprüfung/Zeit der Verwendung. Nach einer gültigen Auflistung kann der Asset-Eigentümer den Royalty MarketPercentage über AssetTrigger → UpdateRoyalties erhöhen; beim nächsten Kauf überschreiten die live Royalty und die abgebildete Empfehlung 100 %, und die Abwicklung erschafft den Überschuss. Die erschaffenen Mittel landen in von Angreifern kontrollierten Empfehlungs-/Royalty-Adressen.

Dies wurde aktiv auf der Hauptnetzwerk ausgenutzt (siehe Beweise), indem mehrere Millionen KLV vor dem Einsatz des Notfall-Schutzes erschaffen wurden.

Betroffenes Komponente

  • Repository: klever-io/klever-go (Knoten).
  • Abwicklung / Erschaffungsort: core/kapp/market/market.goexecuteBuyMarket (L575+), computeReferralAmount (L361+), computeRoyaltiesAmount (L490+), computeRoyaltiesFixedDeposit (L443+), computeMarketOwnerAmount (L540+).
  • TOCTOU-Quellen: Kombinierte Überprüfung bei Sell (market.go:908), Abbildung des Auftrags für Empfehlung, aber nicht Royalty (market.go:997), live Mutation der Royalty über core/kapp/kda/trigger.gohandleUpdateRoyaltiesNFTandSFT (L613+, setzt asset.Royalties.MarketPercentage bei L670).
  • Erreichbar von sowohl Buy (BuyItNow, market.go:204+) als auch Auktion Claim (market.go:705, market.go:731).
  • Vor dem Fix: nicht durch irgendeine Fork-Fahne gesperrt — auf der Hauptnetzwerk ausnutzbar. Der Fix ist hinter der neuen FixMarketBuyOverflow Aktivierungs-Epoche Fahne gesperrt.

Ursache

1. Abwicklung zahlt Empfehlung + Royalty unbedingt, Restbetrag für Verkäufer nur wenn positiv

core/kapp/market/market.goexecuteBuyMarket (L575+):

referralAmount, _  := tools.ComputePercentageI64(marketOrder.CurrentBid,
                          int64(marketOrder.ReferralPercentage), ...)        // L583: SNAPSHOT Empfehlung %
royaltiesAmount, _ := tools.ComputePercentageI64(marketOrder.CurrentBid,
                          int64(asset.Royalties.MarketPercentage), ...)      // L587: LIVE Royalty %
marketOwnerAmount := marketOrder.CurrentBid - referralAmount - royaltiesAmount  // L591: kann negativ werden

// ---- FIX (FixMarketBuyOverflow), hinzugefügt durch das Patch ----
if m.forkController.FixMarketBuyOverflow() && marketOwnerAmount < 0 {         // L593-596
    ctx.Receipts().AddError(ctx.ContractID(), common.ErrFieldInvalidRoyalties, common.ErrInvalidValue.Error())
    return transaction.Transaction_AmountInvalid, common.ErrInvalidValue
}

m.computeReferralAmount(ctx, marketOrder, referralAmount, currencyID)   // zahlt Empfehlung vollständig aus
m.computeRoyaltiesFixedDeposit(ctx, marketOrder, asset)                 // zahlt feste Royalty (KLV) aus
m.computeRoyaltiesAmount(ctx, marketOrder, asset, currencyID, royaltiesAmount) // zahlt % Royalty vollständig aus
m.computeMarketOwnerAmount(ctx, marketOrder, currencyID, marketOwnerAmount)    // <-- überspringt wenn <= 0

computeMarketOwnerAmount (L540-542) — der stille Übergang:

func (m *marketKapp) computeMarketOwnerAmount(... marketOwnerAmount int64) (... , error) {
	if marketOwnerAmount <= 0 {
		return transaction.Transaction_Ok, nil   // negativer Anteil des Verkäufers verworfen, KEIN Fehler
	}
	// ... AddToBalance(marketOwnerAmount) ...
}

Während computeReferralAmount (L376) und computeRoyaltiesAmount (L515) jeweils AddToBalance(...) den vollständig berechneten Betrag mit keinem entsprechenden Debit vom Käufer jenseits des einzigen bidderAcc.SubFromBalance(amount) in Buy (market.go:301) hinzufügen.

Konservierung bricht: Käufer wird einmal mit bid belastet; Empfänger werden mit referralAmount + royaltiesAmount gutgeschrieben. Wenn diese Summe > bid ist, wird der Überschuss (referralAmount + royaltiesAmount − bid) erschaffen.

2. Die kombinierte ≤100% Invariante wird nur zur Auflistungszeit durchgesetzt

Sell (market.go:908) lehnt korrekt eine Auflistung ab, deren kombinierter Anteil mehr als 100 % beträgt:

if asset.Royalties.MarketPercentage + marketplace.ReferralPercentage > core.HundredPercent {
	return transaction.Transaction_ParameterInvalid, common.ErrInvalidValue
}

…und bildet Empfehlung in den Auftrag ab, aber nicht Royalty (market.go:997-998):

marketOrder := &kapps.MarketOrderData{
	// ...
	ReferralPercentage:    marketplace.ReferralPercentage, // abgebildet
	RoyaltiesFixedDeposit: asset.Royalties.MarketFixed,    // abgebildet
	// HINWEIS: asset.Royalties.MarketPercentage wird NICHT abgebildet -> live bei Kauf gelesen
}

MarketOrderData hat kein Feld für den Royalty-Prozentsatz (kapps/market.pb.go), daher liest die Abwicklung ihn immer live aus dem (veränderlichen) Asset.

3. Royalty % ist nach der Auflistung veränderbar

core/kapp/kda/trigger.gohandleUpdateRoyaltiesNFTandSFT (L613+) erlaubt es dem Asset-Eigentümer, asset.Royalties.MarketPercentage (L670) mit nur einer pro-Feld <= 100% Überprüfung (CheckValid100Params, L651) zu überschreiben — es hat keine Kenntnis von irgendeinem snapshotted Empfehlungsanteil eines bestehenden Marktplatz-Auftrags. So kann der Eigentümer die Royalty für das Asset auf 100 % erhöhen (`AssetTrigger / UpdateRoyal

Beweise

Praktische Ausnutzung

  1. Erstellen Sie ein neues Asset mit einem anfänglichen Royalty-Prozentsatz von 0.
  2. Richten Sie einen Marktplatz ein, der eine Empfehlungsprovision von 10000 (was 100 % entspricht) hat.
  3. **Listen

Metriken

Severity
critical
kein öffentlicher PoC bekannt
9.6
Quelle: cna-v3
22.3 %
Erhöht — CVE ist relevanter als mindestens 10 % der heute bewerteten CVEs.
0.3 %
Niedrig — Modell schätzt < 1 % Ausnutzungs-Wahrscheinlichkeit.
Veröffentlicht
2026-08-28 20:18 UTC
CWE-191, CWE-367, CWE-682

Weakness-Klassen (CWE)

  • CWE-191Base

    Integer Underflow (Wrap or Wraparound)

    The product subtracts one value from another, such that the result is less than the minimum allowable integer value, which produces a value that is not equal to the correct result.

    cwe.mitre.org →
  • CWE-367Base

    Time-of-check Time-of-use (TOCTOU) Race Condition

    The product checks the state of a resource before using that resource, but the resource's state can change between the check and the use in a way that invalidates the results of the check.

    cwe.mitre.org →
  • CWE-682Pillar

    Incorrect Calculation

    The product performs a calculation that generates incorrect or unintended results that are later used in security-critical decisions or resource management.

    cwe.mitre.org →

Betroffene Produkte

Aus der Hersteller-/CERT-Meldung extrahierte Produkte und Versionsbereiche. Ein Version-Range wie „<4.14.6“ impliziert die Update-Empfehlung „auf 4.14.6 oder höher aktualisieren“.

  • go

    github.com/klever-io/klever-go

Quellen & Referenzen

IDCVE-2026-54754