Beosin Research Series: Are Decentralized Exchanges (DEX) Safe Enough?

Beosin Research Series: Are Decentralized Exchanges (DEX) Safe Enough?

Decentralized exchanges (DEX) are a type of cryptocurrency exchange which allows for direct peer-to-peer cryptocurrency transactions. Our previous blog has analyzed the token-level issues, today we would like to talk about security issues that may arise in DEX’s process of swapping tokens. There are mainly two types of security issues in DEX:

(1) Security issues within DEX itself.

(2) Security issues that arise when interacting with other DEFI projects as a third-party protocol.

This blog will analyze the first situation.

Part.1

Reentrancy vulnerability

The reentrancy vulnerability is a classic type that needs to be prevented from. Compared to ordinary token’s reentrancy, the main manifestation of Uniswap’s reentrancy vulnerability is that an attacker can initiate a second swap before Uniswap updates the price in a single transaction, which makes the amount of tokens that can be redeemed in the second swap more than the normal swap as Uniswap has not updated its price yet. In addition, in the reentrancy attack of Uniswap, the attacker may only gain a small profit in a single transaction, so flashloan or circular arbitrage is commonly used to expand the profit.

Take the imBTC attack incident as an example, which was due to Uniswap V1 not fully considering contract callbacks when calling ERC777 series tokens.

This is shown as follows: when the attacker uses imBTC tokens to swap ETH (as in Figure 1), the contract first calculates the correct amount of ETH through the self.getInputPrice function and sends the ETH to the target address, then when the self.token.transferFrom function is called, it calls the _callTokensToSend function of imBTC contract (as in Figure 2), and the _callTokensToSend function will call the contract that the user specifies to store the imBTC tokens. Therefore, if the attacker deploys the storage contract and rewrites the TokensToSend function in it, then when the tokens are swapped, the pair (a transaction pair composed of two tokens) contract calls the storage contract deployed by the attacker and can call back the pair for a second swap. As the pair contract’s ledger is not yet updated at the time of the second swap, the calculated ETH amount will be more than the normal way, thus profiting.

Figure 1 Uniswap’s tokenToEthInput function
Figure 1 Uniswap’s tokenToEthInput function
Figure 2 imBTC’s transferFrom function
Figure 2 imBTC’s transferFrom function
Figure 3 imBTC’s _callTokensToSend function
Figure 3 imBTC’s _callTokensToSend function

The detailed attack process is as follows:

Figure 4 ETH-imBTC incident procedure
Figure 4 ETH-imBTC incident procedure

So, why is it that the second call of the tokenToEthSwapInput function sends more ETH than a normal swap? The reason can be explained by the following formulas:

First, under normal swap, the getInputPrice function calculates the amount of swappable ETH as:

The amount of ETH that can be swapped the second time normally is:

However, the amount of ETH available for swapping the second time after reentrancy is:

It can be seen that only the reserve of ETH have decreased in the second swap after reentrancy, while the reserve of imBTC does not increase. This results in an equal amount of imBTC being redeemable for more ETH without an increase in the denominator.
Beosin’s recommendation:
When contracts involve asset transfers, the logic is handled using the “check-validate-interact” model, and business-critical operations can be modified using OpenZeppelin’s official ReentrancyGuard.

Part.2

The swap function does not check the K value

The core of Uniswap is the constant product formula: K=x*y, where the K value is the product of the amount of tokens held by the pair contract and requires that the K value must increase after each subsequent transaction is completed (considering the fee). Therefore, without K-value verification, it will easily get exploited.

Figure 5 Price volatility of Uniswap
Figure 5 Price volatility of Uniswap

In the case of Impossible Finance, two functions are implemented for swapping tokens: cheapSwap and swap. Among them, cheapSwap lacks the check of k-value (see Figure 6), but the project party is aware of the consequences of missing the k-check and has added the modifier onlyIFRouter to the cheapSwap function to restrict the cheapSwap function to be called only by the specified Router contract.

Figure 6 The cheapSwap function without checking the k-value
Figure 6 The cheapSwap function without checking the k-value

Under normal circumstances, when a user redeems tokens using a Router contract, the getAmountsOut function is first used to calculate the correct tokens amounts; then safeTransferFrom is called to transfer the user’s redeemed tokens to the target pair contract; and finally, the _swap function is called internally to execute the cheapSwap function to transfer the redeemed tokens to the target address.

Figure 7 Router01 contract’s swapExactTokensForTokens function
Figure 7 Router01 contract’s swapExactTokensForTokens function

However, since the cheapSwap function lacks a K-value check, if an attacker deploys a malicious token contract and calls back the normal pair contract for swapping the same type of tokens when the safeTransferFrom function is called by the Router contract, it will result in a profit by redeeming the target token at the wrong price, as the amounts used for swap after callback are still the updated data, which no longer meet the validation after changing the state of the ledger.

Figure 8 Swap function of contract for k-value check
Figure 8 Swap function of contract for k-value check

The specific steps of the attack in this incident are as follows:

  1. In the preparation phase the attacker deploys the AAA token contract and borrows 1000 WBNB through flash loan in exchange for 65,140 IF tokens from the project party.
  2. Use half of these IF tokens (32,570) to build an IF-AAA trading pool with the attacker’s own deployed AAA tokens.
  3. Execute the token swap in the AAA-IF-BUSD path and execute the attacker’s malicious code when the Router contract calls the transferFrom function of the AAA token contract, reentering to the pair contract of IF-BUSD and swapping the other half of the IF tokens for 221,897 BUSD normally.
  4. Return to the AAA-IF-BUSD path of swapping, pass in the previously calculated amounts value into the _Swap function to execute this swap, and use half of the IF to swap another 2,521,897 BUSBD.
  5. Return the flash loan to complete the attack.
Figure 9 Incident procedure
Figure 9 Incident procedure

Beosin’s recommendation:

Checking the K value in critical redeem functions is a must. Do not rely on external validation for k-value just to save gas and code volume.

Part.3

Deflationary tokens do not set pair for dividend exclusion

Deflationary tokens will incur additional dividends and fees when they are traded. If such tokens are included in a trading contract and are not specially handled, this may result in a discrepancy between the token reserve recorded in the pair contract and the actual available balance of tokens.

Take the XSquid incident as an example. XSquid is a deflationary token which fails to add the pair contract address to the rewards exclusion list, and this results in the pair contract holding excess XSquid dividend reward tokens in addition to the normal token swap and liquidity storage. Therefore, the attacker can call the Swap function to convert the excess XSquid tokens in the pair contract to WHT to withdraw (as in Figure 11), or withdraw the excess XSquid tokens directly through the skim function (as in Figure 12).

Figure 10 XSquid trading pair contract without adding rewards exclusion
Figure 10 XSquid trading pair contract without adding rewards exclusion
Figure 11 Swap function can swap excess WHT tokens
Figure 11 Swap function can swap excess WHT tokens
Figure 12 skim function can withdraw the part greater than reserve
Figure 12 skim function can withdraw the part greater than reserve

Beosin’s recommendation:

The handling fees and dividends needs to be carefully considered when adding deflationary rewards tokens in DEX. When creating a deflationary token pair, a rewards exclusion can be added to avoid dividend issues for these tokens. In addition, the following two categories are not security issues of DEX itself, but can be used by project parties to commit fraud leveraging DEX features.

PART.1

Trading pool scam

This type of problem mainly refers to the project owner leaving a backdoor in the tokens they issue, creating a trading pool with mainstream tokens, enticing investors to buy the project side’s tokens using the tokens that have value in their hands, and constantly pulling the price to cheat the investors.

Take the following TRTC project as an example, a trading pool of ETH-TRTC is created by the project owner. However, the token contract of TRTC has a restriction on the transferFrom function, which requires the transfer-out party to be owner or Uniswap. For investors, they can only buy TRTC tokens through Uniswap, but cannot sell TRTC tokens. Finally, the project owner withdraws the ETH from investors and runs away, which brings huge losses to investors.

Figure 13 Transfer function of the TRTC contract
Figure 13 Transfer function of the TRTC contract
Figure 14 TRTC contract with ensure modifier
Figure 14 TRTC contract with ensure modifier
Figure 15 TransferFrom function of TRTC contract
Figure 15 TransferFrom function of TRTC contract

PART.2

Rug Pull

A rug pull is a malicious maneuver in the cryptocurrency industry where crypto developers abandon a project and run away with investors’ funds, and has now become the biggest type of scam in the DeFi ecosystem, where project owners deliberately create the illusion of soaring token prices or high returns for investors who provide liquidity. Once a large amount of capital is gathered, the liquidity from the pool is removed or tokens are rolled up. Such examples are seen in projects like AnubisDAO, Meerkat Finance, TurtleDEX, Squid token Squidcoin, etc. The project owners have deleted their social media accounts and disappeared after running away with the funds, resulting in huge losses for investors.

Beosin’s recommendation

Beosin recommends that project parties use lockups and multisig to control token liquidity to avoid a rug pull. Investors should not be intrigued by huge profits to avoid being rug pulled.

Contact US

Website: https://beosin.com/

Email:contact@beosin.com

Twitter: https://twitter.com/Beosin_com

Telegram: https://t.me/beosin

Medium:https://medium.com/@Beosin

Github: https://github.com/Beosin20180329

Discord: https://discord.com/invite/B4QJxhStV4

Subscribe to Beosin
Receive the latest updates directly to your inbox.
Verification
This entry has been permanently stored onchain and signed by its creator.