-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
steal RSR stakers funds by reentrancy attack in issue() function's token transfers #318
Comments
Given that issue() respects the check-effects pattern and all state is updated prior to the loop that transfers tokens I am a bit skeptical that this attack would work without a coded POC. That being said, since the balances cannot be updated atomically if an ERC777 token is present, there may be some risk here. Downgrading to M and will use this to aggregate all of the many re-entrancy concerns raised. |
0xean changed the severity to 2 (Med Risk) |
0xean marked the issue as satisfactory |
0xean marked the issue as primary issue |
#297 has a bit cleaner description and POC of the issue |
dupe of #347 |
0xean marked the issue as duplicate of #347 |
Lines of code
https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/RToken.sol#L186-L280
https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/BackingManager.sol#L105-L150
https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/BasketHandler.sol#L271-L275
Vulnerability details
Impact
Function
issue()
loops through basket tokens and transfers them to BackingManager and mint RToken for user if issuance fit in one block, and transfer required tokens to protocol from user. because protocol is permission less and supports all ERC20 tokens it's possible for tokens to be ERC777 compliant or any other protocol tokens which have hook function during transfer, so transfer of tokens can cause external call to holder address. during the basket erc20 token list transfer's external call which triggers hook call, attacker can callbackingManager.manageTokens()
and becausebasketsNeeded
updated but tokens balance in BackingManager is not yet updated code would detect that RTokens are not fully collateralized wrongly and would seize RSR stakers fund to become fully collateralized. This would cause RSR stakers to lose funds in favor of the RToken holders. attacker can drain RSR stakers funds totally and then withdraw his inflated RTokens.This is reading reentrancy attack across contracts.
Proof of Concept
This is part of
issue()
code where creates RTokens in the same block:As you can see when issuance can fit in the same block code would increase basketsNeeded and then tries to transfer baskets erc20 tokens from issuer to BackingManager address. If one of those tokens were ERC777 then that token would call token holder hook function in token transfers. there may be other protocol tokens that calls registered hook functions of sender or receiver during the token transfer. as reserve protocol is permission less and tries to work with all tokens so the external call in the token transfer can hook sender registered hook functions. attacker can use this and perform reentrancy attack. This is
fullyCollateralized()
code in BasketHandler contract:As you can see it reads
basketsNeeded
from RToken contract and calculated baskets held by BackingManager current token balances by comparing them decides that RTtoken is fully collateralized or not. This is_manageTokens()
code which is called bymanageTokens()
which is callable by anyone:As you can see when RTtoken is not fully collateralized code would seize RSR tokens from RSR stakers.
so to perform the attack, attacker would do these steps: (in one transaction by writing a smart contract)
SOME_ERC777
,USDT
] with quantity [1, 1] are in the basket right now.SOME_ERC777
token to get called during transfers.issue()
to create AMOUNT1 RToken (which can be fit in current block) and code would increasebasketsNeeded
value and then tries to transfer basket tokens from attacker address to BackingManager contract.SOME_ERC777
token, attacker registered hook function would get called and attacker contract would callbackingManager.manageTokens()
and_manageTokens()
function would callfullyCollateralized()
and becausebasketsNeeded
updated but BackingManager token balances are not yet updated so code would detect that RToken is not fully collateralized and it would seize RSR stakers RSR tokens to trade.issue()
would transfer rest of the erc20 tokens from attacker address to contract address. even so RTokens were fully collateralized but code seized RSR stakers funds. after this attacker callmanageTokens()
again and code would distribute those extra funds betweenRToken
holders by minting new RTokens and melting them.lastIssRate
was higher. but even if it was the default value, attacker can stole RSR token up to worth oflastIssRate * totalSupply()
RToken in each block from RSR holders.Tools Used
VIM
Recommended Mitigation Steps
prevent reading reentrancy attack by central reentrancy guard or by proxy interface contract that has reentrancy guard.
or create contract state (similar to basket nonce) which changes after each interaction and check for contracts states change during the call. (start and end of the call)
The text was updated successfully, but these errors were encountered: