CVE-2026-82405
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.
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
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 High
CVSS: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 High
CVSS: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:
- Wrong variable:
CallerAddris never referenced in the handler; auth iscontractHasValidPermission(target.GetPermissions(), RecipientAddr), which returns true ifRecipientAddris a signer withWeight >= Thresholdin the target account's permissions and the permission grantsUpdateAccountPermissionContractType. - RecipientAddr is attacker-controlled: when a contract calls a built-in via
ExecuteOnDestContextWithTypedArgs(baseOps.go:1967),prepareIndirectContractCallInput(baseOps.go:2485) setsRecipientAddr = destination(contract-chosen) andCallerAddr = the calling contract. The blockchain hook (blockChainHook.go:454/467) dispatches oninput.Functionand passes the input through unchanged; no guard forcesRecipientAddr == CallerAddrand there is no SC-destination validation on this path. - Self-signer default satisfies the check:
createDefaultOwnerPermission(accounts.go:1848) makes an account its own signer (weight 1, threshold 1, Owner type), andCheckPermissionGrantedForContractsreturns true for Owner, socontractHasValidPermission(V.perms, V) == true. (More generally,RecipientAddrcan be set to any of V's signer addresses meeting threshold all public on-chain.) Accounts with no stored permissions have emptyGetPermissions()and are immune. - 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 High
CVSS: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 High
CVSS: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 High
CVSS: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:
- Wrong variable:
CallerAddris never referenced in the handler; auth iscontractHasValidPermission(target.GetPermissions(), RecipientAddr), which returns true ifRecipientAddris a signer withWeight >= Thresholdin the target account's permissions and the permission grantsUpdateAccountPermissionContractType. - RecipientAddr is attacker-controlled: when a contract calls a built-in via
ExecuteOnDestContextWithTypedArgs(baseOps.go:1967),prepareIndirectContractCallInput(baseOps.go:2485) setsRecipientAddr = destination(contract-chosen) andCallerAddr = the calling contract. The blockchain hook (blockChainHook.go:454/467) dispatches oninput.Functionand passes the input through unchanged; no guard forcesRecipientAddr == CallerAddrand there is no SC-destination validation on this path. - Self-signer default satisfies the check:
createDefaultOwnerPermission(accounts.go:1848) makes an account its own signer (weight 1, threshold 1, Owner type), andCheckPermissionGrantedForContractsreturns true for Owner, socontractHasValidPermission(V.perms, V) == true. (More generally,RecipientAddrcan be set to any of V's signer addresses meeting threshold all public on-chain.) Accounts with no stored permissions have emptyGetPermissions()and are immune. - 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 High
CVSS: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
| Package | Affected | Fixed |
|---|---|---|
github.com/klever-io/klever-goGo |
through 1.7.19 | 1.7.20 |
github.com/klever-io/klever-goGo |
before 1.7.20 | 1.7.20 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE 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
github.com/klever-io/klever-go/security/advisories/GHSA-97cv-x867-6xhm
github.com/klever-io/klever-go/commit/c58740eb74d7ee8f07db1e18a7d6214b5559ba32
gist.github.com/mabdullah22/a41f90aa5ba86bbebf121f739bd5f5e9
github.com/klever-io/klever-go/releases/tag/v1.7.20
github.com/advisories/GHSA-97cv-x867-6xhm
github.com/CVEProject/cvelistV5/tree/main/cves/2026/82xxx/CVE-2026-82405.json
nvd.nist.gov/vuln/detail/CVE-2026-82405
github.com/klever-io/klever-go
Left out here: the rest of 2 source descriptions. The JSON record has them.