Table of Contents
Overview
Flow-of-Money Walkthrough
Step-by-step Explanation
Questions
References
1. Overview
On July 27, 2023, the Carson token on the Binance Smart Chain was exploited by a flash loan price manipulation attack, which resulted in a loss of ~$150,000. The exploit caused the price of the Carson token to drop by 99%, from $0.34 to $0.001 as of writing.
This attack is extremely interesting because the exploit was repeated 4 times in the span of 6 minutes (not sure if it’s the same attacker or 4 different attackers), netting the attacker $100,677 in the first attack, $27,079 in the second attack, $15,027 in the third attack and $998 in the fourth attack.
Attack 1: 0x37d921a6bb0ecdd8f1ec918d795f9c354727a3ff6b0dba98a512fceb9662a3ac
Attack 2: 0x6a94a644856c3aa1db8249b700956379b6b3a18de9b8811566fbd56c89240a01
Attack 3: 0xed52a42b2f45e4dea8d07f70d645931e4f6da65e9ab281cd3dbb9d9d21febb45
Attack 4: 0xcd781fec34b46f8c7372a9fcc7604c210b40442dc4f1a5c274fc3f9283df39ce
DexScreener: https://dexscreener.com/bsc/0xe0a3e0f7e4c789747eb29a212ea915a7a1e1ac69
I believe that the token is still exploitable, just that you will not gain much compensation anymore since there’s only about $12 left in the liquidity pool.
Now, let’s look at the step-by-step explanation of the attack. Do note that the compromised code is not open-sourced, so every explanation out there is based on assumption.
2. Flow-of-Money Walkthrough
This section will focus solely on the flow of money. Take note of the change in numbers as I’ll be explaining what supposedly happened in the next section.
In the Carson-BUSDT liquidity pool before the exploit, there were 419,360 Carson tokens and 143,796 BUSDT. One Carson Token was worth about 34 cents.
Carson Tokens in Pool: 419,360
BUSDT Tokens in Pool: 143,796
Attacker Balance of Carson: 0
Attacker Balance of BUSDT: 0
Price of Carson: 143,796 / 419,360 = ~0.34
The attacker flash-loaned a total of 1,739,844 BUSDT from various flashloan lenders and deposited 1.5 million BUSDT into the pool. he received 355,794 Carson in return. As a result of a 1.5 million BUSDT deposit, the BUSDT Tokens in the pool increased by 1.5 million and the Carson tokens in the pool became 36,785. The price of Carson shot up to about $45 per token.
Carson Tokens in Pool: 36,785
BUSDT Tokens in Pool: 1,643,796
Attacker Balance of Carson: 355,794
Attacker Balance of BUSDT: 239,844
Price of Carson: 1,643,796 / 36,785 = ~$44.68
After the (first) exploit, there were 169,141 Carson tokens and 43,119 BUSDT in the pool. The attacker earned 100,677 BUSDT from the exploit. One Carson Token was worth about 25 cents.
Carson Tokens in Pool: 169,141
BUSDT Tokens in Pool: 43,119
Attacker Balance of Carson: 0
Attacker Balance of BUSDT: 1,840,521
Attacker's Profit in BUSDT = 1,840,521 - 1,739,844 = 100,677
Price of Carson: 43,119 / 169,141 = ~0.25
3. Step-by-step Explanation
From the above flow, this exploit looks like a flashloan price manipulation attack on the Carson-USDT pool. However, there’s more than meets the eye. Let’s look at some discrepancies from Section 2 first before trying to understand what is happening step-by-step. Before we get started, I’ll be using Phalcon Explorer to trace the transaction, so feel free to follow me.
Take a close look at steps 1 and 2 in Section 2. Focus on the amount of Carson tokens in the pool and the amount of Carson tokens that the attacker obtained from depositing 1.5 million BUSDT into the pool.
Carson Tokens in Pool (Before 1.5m swap): 419,360
Carson Tokens in Pool (After 1.5m swap): 36,785
Attacker Balance of Carson: 355,794
The calculation actually doesn’t tally! Subtracting 419,360 with 169,141 will give you 382,575 tokens, but the attacker only received 355,794 tokens. What’s going on? Where did the 26,781 Carson tokens go?
In line 45 of the Phalcon Explorer, you can see that the attacker calls UniswapV2 swapExactTokensForTokensSupportingFeeOnTransferTokens function and passes in 1.5 million BUSDT tokens.
Line 54: You can actually see that the amount is supposed to be 382,575 (round up).
Line 55: Notice that 355,794 tokens are transferred to the Carson Exploiter
Line 56: 7,651 tokens are transferred from the liquidity pool to a contract address
Line 60: 7,651 tokens are transferred from the liquidity pool to another contract
Line 64: 7,651 tokens are transferred from the liquidity pool to the dead address
Line 65: 3,825 tokens are transferred from the liquidity pool to an EOA address
Total: 382,575 Carson Tokens obtained from swapping 1.5m BUSDT
355,794 Tokens to 0x9cffc95e742d22c1446a3d22e656bb23835a38ac, Exploiter
7,651 Tokens to 0x12dc1423dbc7b0bfa33ee9d9468dde6c5e0031a8, Contract Address
7,651 Tokens to 0xe77a017db4c7754bf272d19ffe0149779b9800b5, Contract Address
7,651 Tokens to 0x000000000000000000000000000000000000dead, Burn Address
3,825 Tokens to 0xfae58c88e28920387c21a12506a42ff8fd8efe43, EOA Address
355,794 + 7,651 + 7,651 + 7,651 + 3,825 ~= 382,575
(Note that all the numbers are actually in 18 decimal places)
(This means that the calculation will not be exact)
There seems to be something going on with burning Carson tokens when interacting with the liquidity pool and transferring a certain percentage of tokens to some contract address. One EOA address also plays a part in the transfer/tax.
What happens now? Let’s refer back to this block. The attacker has a bunch of Carson tokens and the liquidity pool has a lot of BUSDT tokens.
After 1.5M deposit
Carson Tokens in Pool: 36,785
BUSDT Tokens in Pool: 1,643,796
Attacker Balance of Carson: 355,794
Attacker Balance of BUSDT: 239,844
Price of Carson: 1,643,796 / 36,785 = ~$44.68
From lines 75 to 1726, the attacker calls swapExactTokensForTokensSupportingFeeOnTransferTokens again, but this time he deposits 5,000 Carson tokens into the pool and repeats this deposit about 50 times. Since the Carson token is worth quite a lot now, swapping 5,000 Carson tokens will net the attacker quite a lot of money. If you’re following along, you’ll probably have these two questions in your head:
Why does he deposit 5,000 tokens and repeat the deposit 50 times and how does it exploit the pool? The answer lies in the fact that the Carson token is a deflationary token, so every time a swap happens, some Carson tokens are burned.
How does burning Carson tokens help the attacker get money? To answer this, let’s break down the transaction flow.
Line 75: The attacker deposits 5,000 Carson Tokens into the pool.
First Swap: 5,000 Carson
Carson Tokens in Pool: 38,645
BUSDT Tokens in Pool: 1,459,816
Attacker Balance of Carson: 350,794
Attacker Balance of BUSDT: 423,823
Amount of BUSDT gained: 423,823 - 239,844 = 183,979
First things first, note that after depositing 5,000 Carson tokens in the pool, the amount of Carson tokens went up from 36,785 tokens to 38,645 tokens. That’s not even close to 5,000. Only 1,860 tokens were actually deposited. Again, where did the 3,140 tokens go?
Line 77: 4,650 tokens went to the Pool
Line 78: 100 tokens went to Contract Address
Line 82: 100 tokens went to Contract Address
Line 86: 100 tokens went to Dead Address
Line 87: 50 tokens went to EOA
Before the swap even happen, 350 Carson tokens already went to different addresses, of which 100 went to the dead address. It can be speculated that the tokens are for paying fees/tax; the EOA is the fee receiver, and the contract address is the protocol receiver. (Since there is no actual code published, every line in the transaction is simply speculation)
Now that we know the mystery of the 350 tokens, where did 2790 tokens go?
Line 98: 1,395 tokens went to the dead address
Line 100: 1,395 tokens went to a contract address
What is this contract address for? Frankly, I have no idea. The dead address simply means the burn address.
After looking at all the transfers taking place, we know that every time Carson tokens are swapped for BUSDT, a certain portion of the Carson tokens are burned and transferred away. Of the 5,000 tokens deposited into the pool, only 1,860 tokens were actually deposited. The other 3,140 tokens were distributed, of which 1,495 tokens were burned.
In other words, for every 5,000 tokens deposited, 1,495 tokens will be burned, making Carson a deflationary token.
When Carson tokens are continuously burned, the total supply of Carson gets smaller and the price of Carson will increase. Since the attacker got 355,794 tokens for 1.5m BUSDT, if he continues burning the Carson tokens, he will receive more than 1.5m BUSDT when swapping back the 355,794 tokens. If there are lesser Carson tokens in the pool, the Carson token will be worth more each time a trade happens.
I’ll show you the next few trades in numbers
First 5,000 Swap
Carson Amount in Pool: 38,645 (+1,860)
BUSDT Amount in Pool: 1,459,816 (-183,980)
Attacker balance of Carson: 350,794 (-5,000)
Attacker balance of BUSDT: 423,823 (+183,980)
-------------------------------
Second 5,000 Swap
Carson Amount in Pool: 40,505 (+1,860)
BUSDT Amount in Pool: 1,303,450 (-156,366)
Attacker balance of Carson: 345,794 (-5,000)
Attacker balance of BUSDT: 580,189 (+156,366)
-------------------------------
Third 5,000 swap
Carson Amount in Pool: 42,365 (+1,860)
BUSDT Amount in Pool: 1,169,586 (-133,864)
Attacker balance of Carson: 340,794 (-5,000)
Attacker balance of BUSDT: 714,054 (+133,864)
-------------------------------
Fourth 5,000 swap
Carson Amount in Pool: 44,225 (+1,860)
BUSDT Amount in Pool: 1,054,222 (-115,364)
Attacker balance of Carson: 335,794 (-5,000)
Attacker balance of BUSDT: 829,417 (+115,864)
-------------------------------
Last swap (Remaining Carson Balance in Attacker Contract)
Carson Amount in Pool: 169,141
BUSDT Amount in Pool: 43,119
Attacker balance of Carson: 0
Attacker balance of BUSDT: 1,840,521
The amount of BUSDT will decrease every swap because of x*y=k formula
Carson amount increases linearly by 1,860 until the last swap
Carson amount in the pool is significantly lesser than what the attacker had (355,794) because most of it was burned
There are still about $86,400 worth of liquidity after the first exploit, so 3 other transactions happened with a similar attack path to drain the pool completely
If you’re interested to see the whole balance change, I will provide you with a test-suite to test it out yourself!
The most important point to get out of this exploit is to take extra precautions when creating deflationary tokens because the liquidity pool might be susceptible to hacks.
4. Questions
After reading through and analyzing this hack, I want to leave you with 3 questions:
If the attacker simply swapped his 355,794 Carson tokens that he obtained from the 1.5m BUSDT back to the pool (instead of swapping 5,000 at a time, will he earn any profits?
Question 2:
If the token is not deflationary, will this attack still work? Why or why not?
Bonus question:
Which do you think is better? Swapping a larger sum (eg 20,000 tokens) with lesser iterations or swapping a smaller sum (eg 1,000 tokens) with more iterations? Or are they the same?
I’ll leave you to find out by yourself! Do comment your answers down below with an explanation!
5. References
Attacker: 0x25bcbbb92c2ae9d0c6f4db814e46fd5c632e2bd3
Attack Contract: 0x9cffc95e742d22c1446a3d22e656bb23835a38ac
Attack Tx: 0x37d921a6bb0ecdd8f1ec918d795f9c354727a3ff6b0dba98a512fceb9662a3ac
Phalcon Explorer: https://explorer.phalcon.xyz/tx/bsc/0x37d921a6bb0ecdd8f1ec918d795f9c354727a3ff6b0dba98a512fceb9662a3ac
DexScreener: https://dexscreener.com/bsc/0xe0a3e0f7e4c789747eb29a212ea915a7a1e1ac69
PoC Exploit by DeFiHackLabs:
https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/Carson_exp.sol