Decentralized Exchange Vulnerabilities

In the past month, the losses as a result of hacker attacks on the crypto community have risen to $1.019B (24.10), surpassing the past months (omitting the Terra Luna incident)

By taking a closer look, we can see that the major exploits were conducted on DEX (Decentralized Exchange) platforms, summing up to nearly 15% of the total losses in October only.

With that in mind, let’s dive deeper into three cases in the world of DEX exploitation and try to answer these questions:

  • What are the major causes of DEX vulnerabilities?
  • How can we avoid these in the future?

Banana Swap

On August 28th the $DDC token exchange, Banana Swap, was attacked. The attacker successfully manipulated the price of the coin, by breaking the internal logic of price calculation, and as a result swapped 23 $DDC for 110,105 $BUSD.

The vulnerability was quite simple but the impact had huge potential for profit:

  1. The attack began with a transaction of 26 $DDC to the attacker’s account
  2. Then a search for a victim’s wallet was initiated in order to find a wallet that contains a large amount of the tokens
  3. [Exploitation] When a suitable account was found the attacker called handleDeductFee which performed a deduction of almost all the tokens from the victim’s balance
  4. In order to finalize the price manipulation, the function sync was called to update the value/price of $DDC

As a result, the balance of $DDC in the pool was reduced to 0.0003 $DDCs, after the value/price was updated, the price of $USD corresponding to $DDC is raised significantly, and a large amount of $USD can be swapped via a small amount of $DDC. The hacker used 23 $DDC to swap for 104,600 USD.

So how was it possible for an attacker to manipulate the balance of the victim’s account? What was the handleDeductFee function executing?

Let’s take a look at the code:

function handleDeductFee (ActionType actionType, uint256 feeAmount, address from, address user) external override {
	distributeFee(actionType, feeAmount, from, user);
}
// deduct the config fee and transfer to handle fee address which config.
// parameter from
function distributeFee (ActionType actionType, uint feeAmount, address from, address user) internal { 
	_balances [from] = _balances[from].sub(feeAmount);
	...
}

Here we have an external function with controllable arguments which calls an internal function passing the same arguments as parameters which performs a deduction using these arguments right away.

The attacker passed the victim address as the from parameter and the feeAmount was slightly less than the result of balanceOf of this address.

That’s it! Not one single validation on the incoming data. No complex theory behind that at all.

Cyber Security 101: Never trust user-controlled data.

More information can be found in the Twitter post of Beosin Alert here.

Next, we will review a more complex protocol. But as we witnessed already, missing one line of code may break the whole system.

Transit Swap

Transit Swap is a multi-chain DEX aggregator platform running on Ethereum and Binance chains. On October 1st, the platform was a victim of an attack draining almost $29M worth of assets from the wallets of the protocol’s users.

Swapping with Transit Swap involves passing through a couple of contracts that route and bridge different swappers and enforce permission management. Each of the contracts calls the other passing its parameters as arguments to the next, each of them uses the parameters to process some logic and execute actions.

A simplified version of the flow of data looks something like this:

As I mentioned earlier, each of the contracts calls the next passing the arguments as parameters, so why is this so critical?

“The main reason for this attack is that the Transit Swap protocol does not strictly validate the data passed in by the user during the token exchange, which results in an arbitrary external call.” – SlowMist’s analysis

None of the contracts properly validated the data used to make the transaction. As a result the attacker successfully manipulated the whole chain passing specifically crafted data for each step of the transaction.

This is yet another case of data validation with a larger-scale vulnerability. Let’s move forward to one of the biggest losses this year:

Maiar Exchange – Elrond Network

The third most profitable DEX attack of all time was conducted on the Maiar DEX. Hackers exploited a vulnerable function newly deployed on the mainnet. They successfully emptied the reserves of the protocol, stealing $113M of assets.

The exploit started with a function executeOnDestByCaller that allowed contract A to perform actions on contract C through proxy B. Contract A will call a function inside B which allows B to perform actions on C on behalf of Contract A.

To illustrate this we will illustrate Contracts A, B, and C. Contract A calls foo that is inside B. foo calls executeOnDestByCaller which allows B to execute the function bar of Contract C as an impostor of A.

In practice, Contract B can execute every function on behalf of A if A made the first call to B. This idea already sounds a little bit fishy.

To create an exploit using this function one needs to figure out how to make the victim call the contract and then one can do whatever they want with that contract using the external functions it provides like transferFrom.

Fortunately for the hackers, Elrond has a concept called Callbacks. By using these callbacks one can pass a malicious function as a callback to the victim, that will have no other option but to execute the callback and the rest is history.

If you would like to dive deeper, please refer to Elrond’s official report and Arda’s breakdown of the attack.

So there was no vulnerability in the code but rather a logical flaw. The public has found multiple security issues with this feature after the deployment, but the patch and fix could be delivered only a week later, thus, exposing all contracts to potential vulnerability for an entire full week.

Answering The Questions

Knowing what we know now we can address those questions.

The discussed incidents share a common cause: smart contract vulnerability. We are all familiar with the terms vulnerabilities and exploits in other scenarios so what makes it different in the smart contract scenario?

  1. Open-sourced – As we all know the ‘D’ in DEX stands for Decentralized, which means that the product is driven by the users, and that makes the code accessible to the world. This makes the vulnerabilities easier to find.
  2. Difficult to update – The cost of finding and updating a vulnerability is much higher than in an off-chain application. As the contracts are uploaded on the chain, patching a vulnerability means uploading a new copy of the contract and redirecting all the addresses from the old one to the new one if no proxy contract is in use.

Therefore we don’t get a second chance. We must ensure that the contract is bug-free and secure before we upload it to the chain.

Security audits before the upload and constant monitoring of security threats are crucial measures in the world of decentralized apps.

Conclusion

These examples cover only a portion of the vulnerabilities and issues found in smart contracts. As the development of Web 3 advances, more complex algorithms will be introduced to the world, algorithms that may play a big role in the next exploit and cause fund losses.

Analyzing past exploits and mitigating, auditing, and checking the code may prevent casualties in funds and make a huge difference to the future of decentralized applications.

Written By
Itay Cheredman

Itay is a security researcher at Sayfer. He’s passionate about understanding and researching attacking and defending vectors that appear in new emerging technologies.

Skip to content