Open code423n4 opened 1 year ago
0xSorryNotSorry marked the issue as primary issue
deanamiel marked the issue as sponsor confirmed
We have added a separate token manager for fee on transfer tokens, which is protected from reentrancy. Here is a link to the public PR: https://github.com/axelarnetwork/interchain-token-service/pull/96
berndartmueller marked the issue as selected for report
Lines of code
https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/token-manager/implementations/TokenManagerLiquidityPool.sol#L82-L85 https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/token-manager/implementations/TokenManagerLockUnlock.sol#L48-L51
Vulnerability details
Impact
A malicious actor can trick a
TokenManager
into thinking that a bigger amount of tokens was transferred. On the destination chain, the malicious actor will be able to receive more tokens than they sent on the source chain.Proof of Concept
TokenManagerLockUnlock and TokenManagerLiquidityPool are
TokenManager
implementations that transfer tokens from/to users when sending tokens cross-chain. The low-level_takeToken
function (TokenManagerLiquidityPool._takeToken, TokenManagerLockUnlock._takeToken) is used to take tokens from a user on the source chain before emitting a cross-chain message, e.g. via the TokenManager.sendToken function. The function computes the difference in the balance of the liquidity pool or the token manager before and after the transfer, to track the actual amount of tokens transferred. The amount is then passed in the cross-chain message to tell theInterchainTokenService
contract on the destination chain how much tokens to give to the recipient.The
_takeToken
function, however, is not protected from re-entrancies, which opens up the following attack scenario:_takeToken
function callstransferFrom
on the ERC777 token contract, which calls the tokensToSend hook on the malicious contract (the sender).TokenManager.sendToken
and sends 100 more tokens._takeToken
call, the balance change will equal 100 since, in ERC777, the balance state is updated only after thetokensToSend
hook, so only the re-entered token transfer will be counted.TokenManager.sendToken
will result in 100 tokens transferred cross-chain._takeToken
call, the balance change will equal 200 because the balance of the receiver will increase twice during thetransferFrom
call: once for the first call and once for the re-entered call.TokenManager
contract will emit two cross-chain messages: one will transfer 100 tokens (the re-entered call) and the other will transfer 200 tokens (the first call). This will let the malicious actor to receive 300 tokens on the destination chain, while spending only 200 tokens on the source chain.Since the protocol is expected to support different implementations of ERC20 tokens, including custom ones, the attack scenario is valid for any token implementation that uses hooks during transfers.
Tools Used
Manual review
Recommended Mitigation Steps
Consider adding a re-entrancy protection to the
TokenManagerLiquidityPool._takeToken
andTokenManagerLockUnlock._takeToken
functions, for example by using the ReentrancyGuard from OpenZeppelin.Assessed type
Reentrancy