Every CVE Search/ Get API key

CVE-2026-82405

Klever-Go Account takeover: `kleverUpdateAccountPermission` authorizes on attacker-controlled `RecipientAddr` instead of the authenticated caller

8.7 HighFix available

Klever-Go is the Go implementation of the Klever blockchain protocol. Prior to 1.7.20, the KleverUpdateAccountPermission built-in authorizes replacement of a target account's permissions by checking attacker-controlled vmInput.RecipientAddr instead of authenticated vmInput.CallerAddr. An attacker-controlled contract can choose a victim account with configured permissions as RecipientAddr, and contractHasValidPermission can accept the victim's default self-signer as authorization. UpdatePermission can then replace the victim's entire permission set with attacker-supplied Owner permissions, enabling asset theft or permanent lockout without a victim key or signature. Accounts without stored permissions and the native transaction path are not affected. This issue is fixed in version 1.7.20.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
Low
User interaction
None

What an attacker can affect

Confidentiality
High
Integrity
High
Availability
High

Exploit likelihood

0.43%Up 0.2 points since Sep 24, 2026
90%50%10%1%

What each source reports

CVE List Score used 8.7

Klever-Go Account takeover: `kleverUpdateAccountPermission` authorizes on attacker-controlled `RecipientAddr` instead of the authenticated caller

Klever-Go is the Go implementation of the Klever blockchain protocol. Prior to 1.7.20, the KleverUpdateAccountPermission built-in authorizes replacement of a target account's permissions by checking attacker-controlled vmInput.RecipientAddr instead of authenticated vmInput.CallerAddr. An attacker-controlled contract can choose a victim account with configured permissions as RecipientAddr, and contractHasValidPermission can accept the victim's default self-signer as authorization. UpdatePermission can then replace the victim's entire permission set with attacker-supplied Owner permissions, enabling asset theft or permanent lockout without a victim key or signature. Accounts without stored permissions and the native transaction path are not affected. This issue is fixed in version 1.7.20.

Record
CVE-2026-82405
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Weakness
CWE-863
Updated
2026-11-02
EUVDEUVD-2026-85648 8.7

Klever-Go is the Go implementation of the Klever blockchain protocol. Prior to 1.7.20, the KleverUpdateAccountPermission built-in authorizes replacement of a target account's permissions by checking attacker-controlled vmInput.RecipientAddr instead of authenticated vmInput.CallerAddr. An attacker-controlled contract can choose a victim account with configured permissions as RecipientAddr, and contractHasValidPermission can accept the victim's default self-signer as authorization. UpdatePermission can then replace the victim's entire permission set with attacker-supplied Owner permissions, enabling asset theft or permanent lockout without a victim key or signature. Accounts without stored permissions and the native transaction path are not affected. This issue is fixed in version 1.7.20.

Record
EUVD-2026-85648
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-97cv-x867-6xhm 8.7

Klever-Go Account takeover: `kleverUpdateAccountPermission` authorizes on attacker-controlled `RecipientAddr` instead of the authenticated caller

Description

The VM built-in function KleverUpdateAccountPermission (registered always-active, creator.go:381-390 / core/vmconstants.go:234) rewrites an account's entire permission set. Its authorization check uses vmInput.RecipientAddr attacker-controlled instead of the authenticated vmInput.CallerAddr. The sibling handler kleverChangeOwnerAddress.go:86 uses vmInput.CallerAddr correctly, so the safe pattern exists in-repo; this handler deviates. The native transaction path (txProcess.go:833) is safe it uses tx.GetSender().

Mechanism:

  1. Wrong variable: CallerAddr is never referenced in the handler; auth is contractHasValidPermission(target.GetPermissions(), RecipientAddr), which returns true if RecipientAddr is a signer with Weight >= Threshold in the target account's permissions and the permission grants UpdateAccountPermissionContractType.
  2. RecipientAddr is attacker-controlled: when a contract calls a built-in via ExecuteOnDestContextWithTypedArgs (baseOps.go:1967), prepareIndirectContractCallInput (baseOps.go:2485) sets RecipientAddr = destination (contract-chosen) and CallerAddr = the calling contract. The blockchain hook (blockChainHook.go:454/467) dispatches on input.Function and passes the input through unchanged; no guard forces RecipientAddr == CallerAddr and there is no SC-destination validation on this path.
  3. Self-signer default satisfies the check: createDefaultOwnerPermission (accounts.go:1848) makes an account its own signer (weight 1, threshold 1, Owner type), and CheckPermissionGrantedForContracts returns true for Owner, so contractHasValidPermission(V.perms, V) == true. (More generally, RecipientAddr can be set to any of V's signer addresses meeting threshold all public on-chain.) Accounts with no stored permissions have empty GetPermissions() and are immune.
  4. Overwrite is unrestricted: UpdatePermission(V, attackerContract) (accounts.go:1863) replaces V's permission set with attacker-supplied signers; if the attacker supplies an Owner-type permission, no default is appended and V's prior control is fully evicted.

Code walkthrough

(a) The vulnerable handler — core/kapp/builtInFunctions/kleverUpdateAccountPermission.go:

func (e *kleverUpdateAccountPermission) ProcessBuiltinFunction(vmInput *vmcommon.ContractCallInput) (*vmcommon.VMOutput, error) {
    ...
    address := vmInput.NextArg()                       // Arguments[0] — attacker-chosen target account V
    contract, err := e.getUpdateAccountPermissionContract(vmInput) // Arguments[1] — attacker-chosen new permissions
    ...
    acc, err := e.accountsCacher.LoadUser(address)      // loads V
    ...
    // BUG: authorizes against vmInput.RecipientAddr (attacker-controlled), NOT vmInput.CallerAddr
    if !e.contractHasValidPermission(acc.GetPermissions(), vmInput.RecipientAddr) {   // L91
        return nil, errors.New("invalid permission operation")
    }
    // overwrites V's entire permission set with attacker-supplied signers
    resultCode, err := e.kappController.GetAccountsKApp().UpdatePermission(address, contract)
    ...
}

(b) The check just name-matches recipientAddr against V's own signers — same file:

func (e *kleverUpdateAccountPermission) contractHasValidPermission(permissions []*state.Permission, recipientAddr []byte) bool {
    for _, permission := range permissions {
        for _, signer := range permission.Signers {
            if !bytes.Equal(signer.Address, recipientAddr) {   // recipientAddr, not the authenticated caller
                continue
            }
            if signer.Weight >= permission.Threshold &&
                permission.CheckPermissionGrantedForContracts(transaction.TXContract_UpdateAccountPermissionContractType) {
                return true
            }
        }
    }
    return false
}

(c) The dispatch makes RecipientAddr attacker-controlled —…

Record
GHSA-97cv-x867-6xhm
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Weakness
CWE-863
Updated
2026-11-02
NVD 8.7

Klever-Go is the Go implementation of the Klever blockchain protocol. Prior to 1.7.20, the KleverUpdateAccountPermission built-in authorizes replacement of a target account's permissions by checking attacker-controlled vmInput.RecipientAddr instead of authenticated vmInput.CallerAddr. An attacker-controlled contract can choose a victim account with configured permissions as RecipientAddr, and contractHasValidPermission can accept the victim's default self-signer as authorization. UpdatePermission can then replace the victim's entire permission set with attacker-supplied Owner permissions, enabling asset theft or permanent lockout without a victim key or signature. Accounts without stored permissions and the native transaction path are not affected. This issue is fixed in version 1.7.20.

Record
CVE-2026-82405
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Weakness
CWE-863
Updated
2026-11-02
OSV 8.7

Klever-Go Account takeover: `kleverUpdateAccountPermission` authorizes on attacker-controlled `RecipientAddr` instead of the authenticated caller

Klever-Go is the Go implementation of the Klever blockchain protocol. Prior to 1.7.20, the KleverUpdateAccountPermission built-in authorizes replacement of a target account's permissions by checking attacker-controlled vmInput.RecipientAddr instead of authenticated vmInput.CallerAddr. An attacker-controlled contract can choose a victim account with configured permissions as RecipientAddr, and contractHasValidPermission can accept the victim's default self-signer as authorization. UpdatePermission can then replace the victim's entire permission set with attacker-supplied Owner permissions, enabling asset theft or permanent lockout without a victim key or signature. Accounts without stored permissions and the native transaction path are not affected. This issue is fixed in version 1.7.20.

Record
CVE-2026-82405
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Weakness
CWE-863
Updated
2026-11-02
OSVGHSA-97cv-x867-6xhm 8.7

Klever-Go Account takeover: `kleverUpdateAccountPermission` authorizes on attacker-controlled `RecipientAddr` instead of the authenticated caller

Description

The VM built-in function KleverUpdateAccountPermission (registered always-active, creator.go:381-390 / core/vmconstants.go:234) rewrites an account's entire permission set. Its authorization check uses vmInput.RecipientAddr attacker-controlled instead of the authenticated vmInput.CallerAddr. The sibling handler kleverChangeOwnerAddress.go:86 uses vmInput.CallerAddr correctly, so the safe pattern exists in-repo; this handler deviates. The native transaction path (txProcess.go:833) is safe it uses tx.GetSender().

Mechanism:

  1. Wrong variable: CallerAddr is never referenced in the handler; auth is contractHasValidPermission(target.GetPermissions(), RecipientAddr), which returns true if RecipientAddr is a signer with Weight >= Threshold in the target account's permissions and the permission grants UpdateAccountPermissionContractType.
  2. RecipientAddr is attacker-controlled: when a contract calls a built-in via ExecuteOnDestContextWithTypedArgs (baseOps.go:1967), prepareIndirectContractCallInput (baseOps.go:2485) sets RecipientAddr = destination (contract-chosen) and CallerAddr = the calling contract. The blockchain hook (blockChainHook.go:454/467) dispatches on input.Function and passes the input through unchanged; no guard forces RecipientAddr == CallerAddr and there is no SC-destination validation on this path.
  3. Self-signer default satisfies the check: createDefaultOwnerPermission (accounts.go:1848) makes an account its own signer (weight 1, threshold 1, Owner type), and CheckPermissionGrantedForContracts returns true for Owner, so contractHasValidPermission(V.perms, V) == true. (More generally, RecipientAddr can be set to any of V's signer addresses meeting threshold all public on-chain.) Accounts with no stored permissions have empty GetPermissions() and are immune.
  4. Overwrite is unrestricted: UpdatePermission(V, attackerContract) (accounts.go:1863) replaces V's permission set with attacker-supplied signers; if the attacker supplies an Owner-type permission, no default is appended and V's prior control is fully evicted.

Code walkthrough

(a) The vulnerable handler — core/kapp/builtInFunctions/kleverUpdateAccountPermission.go:

func (e *kleverUpdateAccountPermission) ProcessBuiltinFunction(vmInput *vmcommon.ContractCallInput) (*vmcommon.VMOutput, error) {
    ...
    address := vmInput.NextArg()                       // Arguments[0] — attacker-chosen target account V
    contract, err := e.getUpdateAccountPermissionContract(vmInput) // Arguments[1] — attacker-chosen new permissions
    ...
    acc, err := e.accountsCacher.LoadUser(address)      // loads V
    ...
    // BUG: authorizes against vmInput.RecipientAddr (attacker-controlled), NOT vmInput.CallerAddr
    if !e.contractHasValidPermission(acc.GetPermissions(), vmInput.RecipientAddr) {   // L91
        return nil, errors.New("invalid permission operation")
    }
    // overwrites V's entire permission set with attacker-supplied signers
    resultCode, err := e.kappController.GetAccountsKApp().UpdatePermission(address, contract)
    ...
}

(b) The check just name-matches recipientAddr against V's own signers — same file:

func (e *kleverUpdateAccountPermission) contractHasValidPermission(permissions []*state.Permission, recipientAddr []byte) bool {
    for _, permission := range permissions {
        for _, signer := range permission.Signers {
            if !bytes.Equal(signer.Address, recipientAddr) {   // recipientAddr, not the authenticated caller
                continue
            }
            if signer.Weight >= permission.Threshold &&
                permission.CheckPermissionGrantedForContracts(transaction.TXContract_UpdateAccountPermissionContractType) {
                return true
            }
        }
    }
    return false
}

(c) The dispatch makes RecipientAddr attacker-controlled —…

Record
GHSA-97cv-x867-6xhm
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Weakness
CWE-863
Updated
2026-11-02
OSVGO-2026-6582 No score

Klever-Go Account takeover: `kleverUpdateAccountPermission` authorizes on attacker-controlled `RecipientAddr` instead of the authenticated caller in github.com/klever-io/klever-go

Klever-Go Account takeover: kleverUpdateAccountPermission authorizes on attacker-controlled RecipientAddr instead of the authenticated caller in github.com/klever-io/klever-go

Record
GO-2026-6582
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
github.com/klever-io/klever-go
Go
through 1.7.19
1.7.20
github.com/klever-io/klever-go
Go
before 1.7.20
1.7.20

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-82405 is a high vulnerability (CVSS 8.7) in Klever-io Klever-go, published September 23, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.43%, the 37th percentile. Fixed in github.com/klever-io/klever-go 1.7.20.

References

This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open