Resources

People often ask me "How did you learn how to hack?" The answer: by reading. This page is a collection of the blog posts and other articles that I have accumulated over the years of my journey. Enjoy!

SportsDAO Flashloan Attack Analysis- 1022

FaithPosted 3 Years Ago
  • The SportDAO is a DAO centered around athletics. There are many collectables in sports that can easily moved to blockchain like playing cards and sneakers. The SportsDAO has its own sDAO tokens as well.
  • The sDAO appears to be a standard ERC20 token but has a few extra functions that override the standard IERC20 functions. A few to note for this vulnerability:
    • stakeLP(): Allows a user to stake BUSD-sDAO LP tokens directly in the contract.
    • getReward(): Collect sDAO tokens from the staked LP tokens.
    • withdrawTeam(): Transfers all LP tokens to a preset TEAM address.
    • tranferFrom(): Added functionality for calculating the total staking reward of the tokens accumulated when sDAO tokens are transfered from the BUSD-sDAO token contract.
  • The code for the transferFrom() math for the tokens is incorrect. When executing the code for staking, the functionality only checks to see if the address we're trying to transfer to is the BUSD-sDAO LP token. If it is, then 7% of that is added directly to the totalStakeReward.
  • Why is this a problem? The variable totalStakeReward is used directly in the rewards per token calculation AND is directly accessible to any user. Within the transfer() function, only msg.sender must be the owner, indicating this is a flaw in the contract.
  • The final piece of the puzzle is that the function withdrawTeam() can be called externally by anybody to send funds to the TEAM address, which is owned by the SportsDAO. By pairing this functionality of transferring with the direct control over some of the fields for rewards, we come across a vulnerability!
  • The issue relies in the control of sending funds and the control over the math happening. In the statement below, the funds of LPInstance can be removed by the second issue mentioned above. Since this is the denominator of the math, controlling this is a HUGE deal.
    newPerTokenReward = (totalStakeReward - lastTotalStakeReward) * 1e18 
                             / LPInstance.balanceOf(address(this));
    
  • The idea is to make the funds of LPInstance be extremely small; this can be done first by removing all of the funds via the withdrawTeam() then adding in a tiny amount via the bug in transferFrom(). Once the denominator is tiny, the math for the rewards is completely busted. The amount of rewards per token will be extremely high, since it believes it has very little of the token!
  • An attacker used this pair of bugs, alongside a small flash loan, to get a large amount of funds from the contract. The author of this post includes a full proof of concept as well. Overall, awesome explanation of the vulnerability and nice POC!