On January 3, 2023, the GDS project on Binance Smart Chain was hacked. Soon after that followed the post-mortem articles from:
While all of them more or less wrote the same thing (someone checks for plagiarism? 😉); I do not believe even a single one of them was successful in explaining how the hacker executed his / her work. Quite a shame!
How do I know, you wonder 🤔? Well, I am a security researcher in this field. I have spent some time understanding how these hacks are executed and best of all, I try and re-create them so that I exactly know the what and the how!
So if you are a smart contract developer, intending to make sure that you write “safe code”, and you are keeping a tab on the hacks that happen; you need to make sure that you understand this hack very carefully
Now, this hack is not one of the biggest hack (in terms of $$ lost); but it is one fine job done over multiple blocks and hence you should understand that this hack is NOT the job of the FLASHLOAN ALONE!
The other auditors f***ed up in not being able to understand and explain the hack that was executed over 1370 blocks [24451036 less 24449667 (including the last block)] and NOT through a SINGLE transaction that has been identified.
To prove the above, I have documented the exact manner in which the hack was executed, by (a) explaining the events and (b) re-creating this hack.
The Base Attacker Contract does the following things in a loop for almost 30 times:
Createsthe first “Attacker” contract (eg:0x0f8D735c0b67f845068Bb31684707851f9D2767D)
Transfers~0.19 LP tokens to this attacker contract.
Swaps120USDC from Base Attacker for GDS token and feed into this Attacker Contract
Transfersthe GDS Token to the dead account (0x000000000000000000000000000000000000dEaD) (from the Attacker Contract
Transfers Back0.19 LP tokens to his wallet.
lastEpochof the attacker contract to the current
epoch(in this case, from 0 to 29). This allows the attacker to use the updated
lastEpoch(now equal to the current
epoch, 29) to take advantage of the flaw in the contract when the attacker does the transfer in Transaction2.
The one that is believed to be the only transaction in which the hack was executed. ROFL 🤦♂️
withdrawcreated in the attacker 1 contract, which does the following:
weionly] of GDS tokens to dead account
withdrawfunctions of same.
Due to the Transaction 1, the lastEpoch of the caller (Attacker contracts in this case) was set to the currentEpoch. Now when the GDS Tokens is transferred to the dead account, the second condition
currentEpoch > lastEpoch[_from] satisfies. This will NOT be possible if the attack was executed ONLY through the flash loan contract. This is where all the other audit companies focused only.
And as the large amount of LP tokens are acquired, the calculated rewards are also high and the caller ends up with the good amount of GDS tokens.
_refreshDestroyMiningAccountwill be called in
_afterTokenTransferwhich eventually gets called after token transfer of GDS.
toaddress of the transfer is equal to the dead account, the first branch gets executed.
isOpenLpMiningis true at the time of attack, it will enter the nested branch inside and execute
I have re-created parts of the hack in this GitHub Repo
Base Attacker contract : Used in Transaction 1 (Epoch Computation Transaction):
Attacker1 contract: Used in both transactions 1 and 2:
transferToken()called in Txn1: burn GDS tokens equivalent to 100USDC.
withdraw()called in Txn2: burn 10k wei of GDS tokens.
FlashLoanExample contract: The FlashLoanExample contract performs the following steps:
withdraw()function is called. This function burns a small amount of GDS tokens to obtain large rewards.
It’s important to note that in this demonstration, only one attacker contract was used, resulting in a loss due to the incurred fees for internal transfers. If more than 50 attacker contracts were used, as the attacker in the actual attack did, the rewards would have been higher. But it can be clearly seen in the above log that by burning only 10k wei of GDS token, a large amount of GDS tokens are received as reward, hence an PROFIT!
2. Fork this Github Repo: https://github.com/toxicsudo/flash-loan-exploit-tutorial
3. Update your
.env file for the RPC from buildbear and your private key:
4. Visit the faucet,
Add Network to Metmask, and get a good amount of Native tokens (BB ETH) and the ERC20 token
npm i in your terminal, and then the command
npx hardhat run scripts/deploy.ts --network buildbear
You should see something like this:
6. Update the addresses in
7. Visit the Faucet again, for getting more Binance-Pegged USD Token
0x55d398326f99059fF775485246999027B3197955 into the baseAttacker contract and the flash loan example contract. See the example image below:
NOTE that I have updated the address in the first box in which I want the USD token. That is the address of the baseAttacker contract.
This is where I could not find anything better than BuildBear (shout out 🎉🔈). It is easy to get any ERC20 token that I might need in a matter of seconds.
Do the same for the Attacker_contract and also the Flash Loan Example Contract.
8. Job done. Just run the script
npx hardhat run scripts/flash.ts --network buildbear
At the end of the script, you should see something like this:
You have successfully hacked the smart contract of GDS for the computation error.
NOTE: HAD YOU DONE ONLY THE FLASH LOAN TRANSACTION YOU WOULD HAVE MADE A LOSS AND NOT A HACK!!!