Full Report
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!
Analysis Summary
# Incident Report: SportsDAO Flashloan Manipulation
## Executive Summary
In November 2022, SportsDAO’s sDAO token contract was exploited via a flashloan attack that leveraged flaws in its custom staking logic. By manipulating the contract's liquidity provider (LP) token balance and exploiting an incorrectly implemented reward calculation, an attacker was able to artificially inflate staking rewards. The incident resulted in the theft of approximately $13,661 BUSD, though significantly more funds remained at risk.
## Incident Details
- **Discovery Date:** November 22, 2022
- **Incident Date:** November 2022
- **Affected Organization:** SportsDAO
- **Sector:** Decentralized Finance (DeFi) / Athletics Collectibles
- **Geography:** Global / Binance Smart Chain (BSC)
## Timeline of Events
### Initial Access
- **Date/Time:** November 2022
- **Vector:** External function call to an unprotected administrative function.
- **Details:** The attacker identified that the `withdrawTeam()` function, which transfers all LP tokens to the SportsDAO team address, was globally executable by any user.
### Lateral Movement
- **Reconnaissance:** The attacker analyzed the `getPerTokenReward()` function and identified a mathematical vulnerability where the reward rate was inversely proportional to the contract’s LP balance (`LPInstance.balanceOf(address(this))`).
- **Exploitation:** The attacker used a flashloan to acquire BUSD/sDAO, then called `withdrawTeam()` to drain the contract's LP balance to near zero.
### Data Exfiltration/Impact
- **Detail:** By pushing a tiny amount of LP tokens back into the contract via a bug in `transferFrom()`, the attacker triggered a division-by-near-zero scenario in the reward calculation.
- **Outcome:** This "busted math" resulted in an astronomical `newPerTokenReward`. The attacker then claimed these inflated rewards and swapped them for ~13.6k BUSD.
### Detection & Response
- **Detection:** Post-incident analysis by security researchers using on-chain data from BSCScan.
- **Response Actions:** Security researchers developed a Proof of Concept (PoC) to document the flaw; the DAO was notified via public analysis.
## Attack Methodology
- **Initial Access:** Exploitation of public visibility on the `withdrawTeam()` function.
- **Persistence:** Not applicable (one-time flashloan transaction).
- **Privilege Escalation:** Functional escalation by utilizing an "Owner-only" style function (`withdrawTeam`) that lacked access controls.
- **Discovery:** On-chain code auditing of the sDAO ERC20 implementation.
- **Impact:** Manipulation of the denominator in a reward distribution formula to drain assets.
## Impact Assessment
- **Financial:** Loss of approximately 13,661 BUSD. An estimated 418,000 sDAO tokens remained in the contract, indicating the potential for a much larger loss.
- **Operational:** The staking and reward mechanism was rendered insolvent and unreliable.
- **Reputational:** High; the incident highlighted "weird" and non-standard overrides of the ERC20 standard.
## Indicators of Compromise
- **Contract Address:** `0x6666625Ab26131B490E7015333F97306F05Bf816` (sDAO Token)
- **Behavioral:** Sudden calls to `withdrawTeam()` by non-team addresses followed immediately by `getReward()` calls in a single transaction block.
## Response Actions
- **Containment:** None reported by the project at the time of analysis.
- **Eradication:** Implementation of a Proof of Concept by researchers to prevent further "copy-cat" exploits by identifying the root cause.
- **Recovery:** Movement of remaining funds (suggested) to a secure, audited vault.
## Lessons Learned
- **Standardization:** Overriding standard IERC20 functions with complex logic (staking/rewards) introduces significant attack surfaces.
- **Access Control:** Critical functions affecting contract state or fund distribution (like `withdrawTeam`) must be protected by `onlyOwner` or similar modifiers.
- **Mathematical Safety:** Denominators in financial calculations must be protected against manipulation, specifically preventing them from being decreased to zero or near-zero by unauthorized users.
## Recommendations
- **Decoupling Logic:** Move staking and reward logic to a separate, audited Vault contract rather than embedding it within the ERC20 token contract itself.
- **Audit Requirement:** Any contract managing user funds or rewards must undergo a professional security audit to identify "logic bombs" in mathematical formulas.
- **Input Validation:** Ensure that internal balances used for reward calculations cannot be manipulated via external calls.