Some of token's underlying assets for corresponding vault can fail to be transferred when such token is a rebasing token or token with airdrops, such as AMPL #28
Based on this protocol's token whitelist, this protocol needs to support rebasing tokens and tokens with airdrops, such as AMPL. Balances of such tokens' holders can be changed when respective rebasing or airdrop event is triggered. For example, as shown by AMPL's balanceOf function below, the AMPL holders' token balances depend on _gonsPerFragment, which can be changed when AMPL's rebase function below is called.
For such token, it is possible that the _deposit function below is called before the token's rebasing or airdrop event is triggered. This would cause _vaultAllowance to be inaccurate for such token when the token's rebasing or airdrop event is later triggered. Due to the inaccurate _vaultAllowance for the corresponding token and vault, such token's underlying assets for such vault can be lost when all of such _vaultAllowance is transferred from the router.
For instance, when the following _deposit function for AMPL is called for the first time and AMPL's rebase function has not been called, based on the transferFrom function below and balanceOf function of AMPL, _vaultAllowance for AMPL and the corresponding vault would be increased to the router's _gonBalances for such vault dividing by _gonsPerFragment at that moment.
function transferFrom(
address from,
address to,
uint256 value
) external override validRecipient(to) returns (bool) {
_allowedFragments[from][msg.sender] = _allowedFragments[from][msg.sender].sub(value);
uint256 gonValue = value.mul(_gonsPerFragment);
_gonBalances[from] = _gonBalances[from].sub(gonValue);
_gonBalances[to] = _gonBalances[to].add(gonValue);
...
}
Later, AMPL's rebase function is called, which increases its _totalSupply and decreases its _gonsPerFragment. Afterwards, when the corresponding vault calls the following transferOut, _transferOutV5, or _transferOutAndCallV5 function, the maximum AMPL balances that can be transferred from the router would be the _vaultAllowance for AMPL and such vault. Since such _vaultAllowance equals the router's _gonBalances for the corresponding vault dividing by the previous _gonsPerFragment, multiplying such _vaultAllowance by the new lower _gonsPerFragment would be less than the router's _gonBalances for such vault; thus, when AMPL's transfer function below is called, some of the router's _gonBalances for the corresponding vault cannot be transferred but all of the _vaultAllowance for AMPL and such vault is used and removed. As a result, the portion of the router's _gonBalances for the corresponding vault, which fails to be transferred, is lost.
The new _gonsPerFragment equals 1.157920892373162e77 / 269765777507592640 = 4.292319444932454e59.
Vault A calls the _transferOutV5 function to transfer all of the _vaultAllowance for AMPL and Vault A that is 1e13.
_vaultAllowance for AMPL and Vault A is reduced to 0.
The transferred portion of the router's _gonBalances for Vault A equals 1e13 * 4.292319444932454e+59 = 4.292319444932454e72.
The portion of the router's _gonBalances for Vault A, which fails to be transferred, equals 4.308289928543639e72 - 4.292319444932454e72 = 1.5970483611185176e70.
Tools Used
Manual Review
Recommended Mitigation Steps
Like many other protocols, this protocol can consider to not support rebasing tokens and tokens with airdrops, such as AMPL. If this protocol wants to support these tokens, it needs to communicate with its users about the shortcomings of these tokens in this protocol, such as these tokens' airdrops can be lost. Alternatively, _vaultAllowance for these tokens can be updated to keep track of these tokens' underlying assets held by the router for the corresponding vaults in order to transfer the underlying assets instead of the balances of these tokens.
Lines of code
https://github.com/code-423n4/2024-06-thorchain/blob/5b91b5c6683a222b0ce046533515e301c9699d74/chain/ethereum/contracts/THORChain_Router.sol#L143-L160 https://github.com/code-423n4/2024-06-thorchain/blob/5b91b5c6683a222b0ce046533515e301c9699d74/chain/ethereum/contracts/THORChain_Router.sol#L438-L453 https://github.com/code-423n4/2024-06-thorchain/blob/5b91b5c6683a222b0ce046533515e301c9699d74/chain/ethereum/contracts/THORChain_Router.sol#L185-L207 https://github.com/code-423n4/2024-06-thorchain/blob/5b91b5c6683a222b0ce046533515e301c9699d74/chain/ethereum/contracts/THORChain_Router.sol#L209-L238 https://github.com/code-423n4/2024-06-thorchain/blob/5b91b5c6683a222b0ce046533515e301c9699d74/chain/ethereum/contracts/THORChain_Router.sol#L304-L389
Vulnerability details
Impact
Based on this protocol's token whitelist, this protocol needs to support rebasing tokens and tokens with airdrops, such as AMPL. Balances of such tokens' holders can be changed when respective rebasing or airdrop event is triggered. For example, as shown by AMPL's
balanceOf
function below, the AMPL holders' token balances depend on_gonsPerFragment
, which can be changed when AMPL'srebase
function below is called.https://etherscan.io/address/0xd0e3f82ab04b983c05263cf3bf52481fbaa435b1#code#F1#L169
https://etherscan.io/address/0xd0e3f82ab04b983c05263cf3bf52481fbaa435b1#code#F1#L107
For such token, it is possible that the
_deposit
function below is called before the token's rebasing or airdrop event is triggered. This would cause_vaultAllowance
to be inaccurate for such token when the token's rebasing or airdrop event is later triggered. Due to the inaccurate_vaultAllowance
for the corresponding token and vault, such token's underlying assets for such vault can be lost when all of such_vaultAllowance
is transferred from the router.For instance, when the following
_deposit
function for AMPL is called for the first time and AMPL'srebase
function has not been called, based on thetransferFrom
function below andbalanceOf
function of AMPL,_vaultAllowance
for AMPL and the corresponding vault would be increased to the router's_gonBalances
for such vault dividing by_gonsPerFragment
at that moment.https://github.com/code-423n4/2024-06-thorchain/blob/5b91b5c6683a222b0ce046533515e301c9699d74/chain/ethereum/contracts/THORChain_Router.sol#L143-L160
https://github.com/code-423n4/2024-06-thorchain/blob/5b91b5c6683a222b0ce046533515e301c9699d74/chain/ethereum/contracts/THORChain_Router.sol#L438-L453
https://etherscan.io/address/0xd0e3f82ab04b983c05263cf3bf52481fbaa435b1#code#F1#L270
Later, AMPL's
rebase
function is called, which increases its_totalSupply
and decreases its_gonsPerFragment
. Afterwards, when the corresponding vault calls the followingtransferOut
,_transferOutV5
, or_transferOutAndCallV5
function, the maximum AMPL balances that can be transferred from the router would be the_vaultAllowance
for AMPL and such vault. Since such_vaultAllowance
equals the router's_gonBalances
for the corresponding vault dividing by the previous_gonsPerFragment
, multiplying such_vaultAllowance
by the new lower_gonsPerFragment
would be less than the router's_gonBalances
for such vault; thus, when AMPL'stransfer
function below is called, some of the router's_gonBalances
for the corresponding vault cannot be transferred but all of the_vaultAllowance
for AMPL and such vault is used and removed. As a result, the portion of the router's_gonBalances
for the corresponding vault, which fails to be transferred, is lost.https://github.com/code-423n4/2024-06-thorchain/blob/5b91b5c6683a222b0ce046533515e301c9699d74/chain/ethereum/contracts/THORChain_Router.sol#L185-L207
https://github.com/code-423n4/2024-06-thorchain/blob/5b91b5c6683a222b0ce046533515e301c9699d74/chain/ethereum/contracts/THORChain_Router.sol#L209-L238
https://github.com/code-423n4/2024-06-thorchain/blob/5b91b5c6683a222b0ce046533515e301c9699d74/chain/ethereum/contracts/THORChain_Router.sol#L304-L389
https://etherscan.io/address/0xd0e3f82ab04b983c05263cf3bf52481fbaa435b1#code#F1#L223
Proof of Concept
The following steps can occur for the described scenario.
TOTAL_GONS
is1.157920892373162e77
;_totalSupply
is268765777507592648
;_gonsPerFragment
is1.157920892373162e77 / 268765777507592648 = 4.308289928543639e59
._deposit
function to deposit1e13
wei AMPL for Vault A._vaultAllowance
for AMPL and Vault A equals1e13
._gonBalances
for Vault A equals1e13 * 4.308289928543639e59 = 4.308289928543639e72
.rebase
function is called to increase_totalSupply
by1e15
._totalSupply
equals268765777507592648 + 1e15 = 269765777507592640
._gonsPerFragment
equals1.157920892373162e77 / 269765777507592640 = 4.292319444932454e59
._transferOutV5
function to transfer all of the_vaultAllowance
for AMPL and Vault A that is1e13
._vaultAllowance
for AMPL and Vault A is reduced to0
._gonBalances
for Vault A equals1e13 * 4.292319444932454e+59 = 4.292319444932454e72
._gonBalances
for Vault A, which fails to be transferred, equals4.308289928543639e72 - 4.292319444932454e72 = 1.5970483611185176e70
.Tools Used
Manual Review
Recommended Mitigation Steps
Like many other protocols, this protocol can consider to not support rebasing tokens and tokens with airdrops, such as AMPL. If this protocol wants to support these tokens, it needs to communicate with its users about the shortcomings of these tokens in this protocol, such as these tokens' airdrops can be lost. Alternatively,
_vaultAllowance
for these tokens can be updated to keep track of these tokens' underlying assets held by the router for the corresponding vaults in order to transfer the underlying assets instead of the balances of these tokens.Assessed type
ERC20