Move coin transfer can bypass blocked accounts
Description
When sending coins from one address to another using MsgSend
, the coins are checked to ensure that sending is enabled and that the receiver is not blocked:
ctx := sdk.UnwrapSDKContext(goCtx)
if err := k.IsSendEnabledCoins(ctx, msg.Amount...); err != nil {
return nil, err
}
if k.BlockedAddr(to) {
return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", msg.ToAddress)
}
Once validated, the coins are sent using SendCoins
, which will end up calling into the Move VM and executing 0x1::coin::transfer
.
The issue is that there is nothing stopping someone from directly calling 0x1::coin::transfer
in the Move VM, bypassing both the IsSendEnabledCoins
and BlockedAddr
checks.
Impact
A malicious user can send a coin that has sending disabled or transfer a coin to a blocked address.
Recommendations
If the blocking and sending functionality is required, it could be implemented inside the Move VM instead of relying on the default Cosmos SDK implementation, or the GoAPI
could be extended to expose this information.
Remediation
Initia Labs provided the following response:
Those features are not used (no effect even we use it) in our chain. We've changed invariant too in the staking and distribution module to allow funding to blocked accounts.