Cointime

Download App
iOS & Android

How Safe is SafeMoon? Analyzing the FETA and BEVO Exploits

Validated Project

Recently, a number of deflationary tokens such as FETA and BEVO have suffered flashloan exploits. We noticed that our analysis contradicted a number of previously published reports.

According to these published analyses, one might get the impression that SafeMoon-type deflationary tokens are inherently vulnerable. Allegedly, if DEX Pair is not added to excludeFromReward(), an attacker can extract money from it. Such news would be quite disturbing given the popularity of SafeMoon clones.

In this post, we will show the real vulnerability conditions and bugged code.

Attack Flow

Exploit Transaction | Exploiter | Exploit Contract | Victim Pair | Token Contract

The attacker:

  • Flashloaned 18.5 WBNB at PancakeSwap
  • Swapped all of them for 156m FETA at victim DEX Pair. 100m FETA left
  • Used deliver() token method to burn 156 million FETA tokens (part of the normal SafeMoon implementation). Since a large number of tokens were burned, the deflationary nature of FETA increased all the balances, including the victim DEX pair. Its balance increased from 100 million to 313 million
  • Withdrew 205 million FETA tokens from DEX Pair via skim() method (part of the normal DEX Pair implementation)
  • Used deliver() to burn 205 million FETA tokens
  • Withdrew via skim() 1.2 billion extra FETA tokens from Pair
  • Swapped all FETA (1.8 billion) for 28.9 WBNB, leaving 0.355 WBNB in the DEX Pair
  • Repaid the flashloan of 18.5 WBNB, earning 10.3 WBNB profit

SafeMoon and DEX Pair Basics

SafeMoon

  1. SafeMoon is an ERC20 token, the balance of which grows over time.
  2. Each transfer incurs a burn fee of 5%. Send 100 tokens and receive 95. Token balances of all other rewardable accounts are automatically increased (5 tokens distributed pro-rata).
  3. To achieve balance auto-updating, the balance is not stored directly but as a Reflection. TokenBalance[owner] = Reflection[owner] / Rate.
  4. When Reflections are burnt, the Rate is decreased. All token balances increase automatically.a. Rate = ReflectionTotal / TokenTotal.b. For example, if you burn half of the Reflections, the Rate is halved, and everyone’s token balance is doubled.c. TokenTotal (the sum of all token balances) remains the same.
  5. Some accounts are excludedFromReward. Their token balances don’t grow automatically. That is useful for DEXs. The contract owner had the power to move addresses to and from that list.

DEX Pair

  1. DEX Pair holds liquidity: amountA of tokenA and amountB of tokenB.
  2. The user can swap one token for another.
  3. The price is determined by a ratio of the amounts. PriceA = amountB/amountA.
  4. The price is automatically adjusted so that amountB*amountA remains constant (though it is slightly adjusted by fee).
  5. For example, if you send to Pair 10*amountA of tokenA, you will get 10/11*amountB of tokenB. Because after the operation there will be 11 times more tokenA and 11 times less tokenB - the product is preserved.
  6. DEX Pair remembers its balances and allows everyone to extract surplus via skim() method to keep the reserves product the same as remembered.

Analysis

At first glance, the attack scenario seems clear:

  1. The author of another token forgot to add the DEX Pair WBNB-FETA address to the excludedFromReward list.
  2. Because of this, the DEX Pair's balance began to grow on its own as fees were burned. This is not ideal, because the DEX Pair is trying to keep track of its balances.
  3. This extra balance was considered the exchanger's contribution: they received more WBNB in return for their work.
  4. However, once the attacker burned a large number of tokens, then extracted the excess from the DEX Pair and repeated this action. The attacker got a lot of tokens.
  5. This allowed them to exchange a huge number of tokens for almost all WBNB in Pair.

From this superficial analysis, it turns out that the deflationary nature of the token and the forgetfulness of its deployer are to blame. It is necessary to add DEX Pair to excludedFromReward. If suddenly somewhere else there are such pairs of other deflationary tokens (which is very likely), then an attacker can also extract funds from them, bringing down the token price.

However, in reality, it is not possible to use the above approach. It is impossible to extract more from a Pair than was burned in shares. For example, if deliver() is applied to half of the total supply of tokens, then the Rate will drop two times, and the balance of the Pair will only double, which means that the skim() call will give only half of this. This is not a larger share than what was initially burned.

Let's look at an example:

  1. The attacker has 80% of the tokens. 10% is in the Pair, and another 10% is held by other users (possibly controlled by the attacker).
  2. Burning 80% of the tokens will reduce the Rate of the token by a factor of five. The pair will have 50%, and the other 50% are held by other users.
  3. The attacker can extract four-fifths of the balance from the Pair, or 40%.
  4. The final distribution of tokens is 40% for the attacker, 10% for the Pair, and 50% for other users.
  5. There is no benefit in repeating the scenario because the attacker's position has worsened.

Investigation

Now let’s take a closer look at the attack with figures:

  1. At the start of the attack:a. rTotalis 66700 (the actual figure was 66784287767507956726405749082784862500447315773962267311622542245534674271656, we will cut all reflection balances for simplicity).b. The attacker has 156 million tokens (30500 reflections).c. The Pair has 100 million tokens (19500 reflections).d. And 21700 reflections were excludedFromReward.e. The Rate = (rTotal - rExcludedFromReward) / tSupply = 66700 - 21700) / tSupply = 195.5.
  2. After the first deliver()of 156 million tokens (30500 reflections):a. The attacker has 0 tokens (0 reflections).b. The Pair has 313 million tokens (same 19500 reflections).c. rTotal is now 66700 - 30500 = 36200.d. The Rate becomes Rate = (36200 - 21700) / tSupply = 62.6, that is 195.5 / 62.6 = 3.12 times lower than initially. This is suspicious.
  3. The attacker skimmed 213 million from Pair (some were burned during the transfer).a. The attacker has 205 million tokens (13000 reflections).b. The Pair has 100 million tokens again (6500 reflections).c. rTotal and Rate have lowered due to fees.
  4. The attacker calls deliver()for the second time and burns 205 million tokens (13000 reflections).a. rTotal = 36200 - 13000 = 23200b. Rate = (23200 - 21700) / tSupply = 4.83. That is 62.6 / 4.83 = 13times less.Suspicious again.c. The pair balance becomes 1.3 billion tokens with a small Rate (still 6500 reflections)
  5. Now the attacker can skim again and swap tokens back for WBNB.

It’s obvious now that in our case the Rate declines much faster than expected. Why does this happen?

Vulnerability

Despite the fact that rTotal was initially 66700 according to the FETA token’s logic, in reality there were 73300 reflections circulating. With extra reflections at the attacker’s disposal, they can practically burn “all” the supply but still keep a lot. Or burn 99% of rTotal and get x100 to 6600 (10%) unaccounted “shadow” reflections. That is exactly what happened.

rTotal state field is updated each time reflections are burned, for example in the function deliver() we get:

  function deliver(uint256 tAmount) public {
        address sender = _msgSender();
        require(!_isExcluded[sender], "Excluded addresses cannot call this function");
        (uint256 rAmount,,,,,,) = _getValues(tAmount);
        _rOwned[sender] = _rOwned[sender].sub(rAmount);
        _rTotal = _rTotal.sub(rAmount);
        _tFeeTotal = _tFeeTotal.add(tAmount);
    }

In the standard SafeMoon clone is the function _reflectFee(). It adjusts rTotal according to the amount that was burnt by transfer().

However, in our modified FETA token code the authors have split the fee into rFeerBurnrCharity and forgot to adjust rTotal with rCharity. Below is the code with our comments:

    function _transferBothExcluded(address sender, address recipient, uint256 tAmount) private {
        uint256 currentRate =  _getRate();
        (uint256 rAmount, uint256 rTransferAmount, uint256 rFee, uint256 tTransferAmount, 
            uint256 tFee, uint256 tBurn, uint256 tCharity) = _getValues(tAmount);
        uint256 rBurn =  tBurn.mul(currentRate);
        uint256 rCharity = tCharity.mul(currentRate);
        // sub 100% of rAmount from sender, add 91% to receiver
        _bothTransferContent(sender, recipient, tAmount, rAmount, tTransferAmount, rTransferAmount);    
        // add 3% to _rOwned[charity]
        _sendToCharity(tCharity, sender); 
        // sub 9% of rAmount from rTotal
        _reflectFee(rFee, rBurn, rCharity, tFee, tBurn, tCharity);
        emit Transfer(sender, recipient, tTransferAmount);
    }

In _sendToCharity(), 3% of transferred reflections (rAmount) are added to the charity account (_rOwned[charity]), but _reflectFee() still “burns” 9% (all three fees):

_rTotal = _rTotal.sub(rFee).sub(rBurn).sub(rCharity);

Via this bug, ~10% of extra reflections appeared in the system which were not accounted for by rTotal. This is what finally led to the exploit execution.

Conclusion

In order to find similar contracts in time, draw conclusions, and make recommendations for future projects, it is important to fully understand the causes and circumstances of this attack.

Adding a Pair to the excludedFromReward list closes the attack vector and should always be done.

However, as we have shown, the "standard" clones of the SafeMoon project are not affected by the attack. The specific bug in rTotal management was uncovered in FETA/BEVO code modification.

At CertiK we strongly recommend that all projects undergo a thorough code audit, even if the code is mostly forked from an audited project and changes appear (on the surface) to be minimal.

Read more: https://www.certik.com/resources/blog/54xxSWllEyCCKBgMKh32AW-how-safe-is-safemoon-analyzing-the-feta-and-bevo-exploits

Comments

All Comments

Recommended for you

  • Survey: 75% of Nigerians Confident in Using Bitcoin for Financial Transactions

    A new survey shows that 75% of Nigerians are confident in using Bitcoin for financial transactions. This survey result comes at a critical time in Nigeria's traditional financial market. In recent months, the Nigerian currency, the Naira, has sharply declined, and the government is trying to maintain the Naira exchange rate while also targeting cryptocurrency. One of the measures recently taken by the Nigerian Securities and Exchange Commission (SEC) regarding the cryptocurrency industry is to propose a significant 400% increase in registration fees for cryptocurrency exchanges.

  • Amaranth Foundation founder spent $24.7 million to buy 7,814 ETH

    According to Spot On Chain, James Fickel, founder of Amaranth Foundation, spent $24.7 million in the past 40 minutes to purchase 7,814 ETH at a price of approximately $3,161 per coin. This giant currently provides Aave with 128,516 ETH ($404 million) and 40.97 million USDC, and has borrowed 2,266 WBTC ($146 million), seemingly trading long on the ETH/BTC pair since December 2023.

  • Vitalik: PoW is also quite centralized. PoW is just a temporary phase before moving to PoS

    Vitalik Buterin, co-founder of Ethereum, stated on social media that PoW is also quite centralized. It just hasn't been discussed too much because everyone knows it's just a temporary stage before transitioning to PoS. This doesn't even involve how to potentially avoid ASICs, simply because the upcoming PoS transition means there's no incentive to build them.

  • If a Hong Kong spot virtual asset ETF is sold at a premium, it can be converted into Hong Kong dollars on the Hong Kong Stock Exchange

    Currently only a few Hong Kong brokers with virtual asset retail licenses can subscribe to the Hong Kong Bitcoin ETF through the new share subscription method (PD/distributor), and after the ETF officially enters the Hong Kong Stock Exchange, all hundreds of Hong Kong brokers and banks can purchase it. The approved virtual asset ETF adopts the performance of the ChiNext CF Bitcoin Index (Asia-Pacific closing price), so the profit and loss risks of cash subscription for Bitcoin ETF are basically the same as those of directly buying Bitcoin. As the exchange ratio between Bitcoin and Bitcoin ETF is fixed, if physical subscription is used in the IOP stage, that is, Bitcoin is used to subscribe to Bitcoin ETF, the relevant ETF can be exchanged for Hong Kong dollars in the exchange if it is sold at a premium after listing, and then buy back Bitcoin at the same time to earn the price difference between on-exchange and off-exchange. (Finance News Agency)

  • SEC sues Bitcoin mining company Geosyn, accusing its founder of $5.6 million fraud

    On April 26th, the US SEC filed a lawsuit against bitcoin mining company Geosyn Mining and its co-founders, accusing them of falsely reporting the number of cryptocurrency mining equipment in operation and using customer funds for personal expenses, resulting in a $5.6 million investment fraud.

  • Hong Kong Stock Exchange to Start Trading Harvest Fund’s Bitcoin and Ethereum Spot ETFs on April 30

    The Hong Kong Stock Exchange will begin trading Harvest's Bitcoin and Ethereum spot ETFs on April 30.

  • The total market value of stablecoins exceeds 158 billion US dollars, and USDT has a market share of 69.8%

    According to DefiLlama data, the total market value of stablecoins has reached 158.197 billion US dollars, with a 7-day growth rate of 0.16%. Among them, the market value of UDST is 110.426 billion US dollars, with a market share of 69.8%.

  • Bitcoin spot ETF has a cumulative net inflow of US$12.082 billion, and Grayscale GBTC has a cumulative net outflow of over US$17.1 billion

    According to Farside Investors, the cumulative net inflow of Bitcoin spot ETF has reached 12.082 billion US dollars since its launch. Among them:

  • Rune DOG•GO•TO•THE•MOON ranked first in transaction volume in the past 24 hours

    According to Ord.io on social media platform, the top 5 trading volumes for runes in the past 24 hours are:

  • CARV announces completion of $10 million Series A financing, with OKX Ventures participating

    CARV announced the completion of a $10 million Series A financing round, led by Tribe Capital and IOSG Ventures. Consensys, OKX Ventures, Fenbushi Capital, No Limit Holdings, Draper Dragon, Arweave, ARPA, MARBLEX, and others participated in the round. The aim is to build the largest modular data layer for gaming and artificial intelligence, and to maximize data innovation while ensuring that individual users can derive value from internet sharing.Jeff Ren, partner at OKX Ventures, said, "CARV's revolutionary approach is reshaping the way we manage decentralized data. Its modular cross-chain protocol and ID aggregation solution cultivate data sovereignty and integrity while emphasizing security and efficiency. We are excited about this collaboration and look forward to seeing how OKX Web3 products can better collaborate with CARV's advanced cross-chain data layer."