On 11th November 2022, a Re-entrancy attack occurred, which resulted in draining a total of ~$7.5M from DFX Finance’s Polygon liquidity pools. The attacker could only transfer $4.3 million worth of assets into their wallet. The remaining portion–about $3.2 million– was extracted by an MEV bot in a front-running transaction, also called a sandwich attack.
Smart Contract Hack Overview:
GitHub code link:
Decoding the Smart Contract Vulnerability:
- The attacker first called the “
usdc-xidr” pair contract’s
viewDeposit()function, which accepts lptoken as an input and returns an array of how many
xidrtokens are required. This function also mints enough
- The attacker borrowed enough
XIDRby invoking the flash function of DFX because the same project included a
flash()code for flash loans. Link to Code — https://github.com/dfx-finance/protocol-v2/blob/90e5ae656f64ecf13e13b03b1cb7fb1d79e09f5b/src/Curve.sol#L645
- The borrowed USDC and XIDR were then redeposited into the transaction pair contract by the assailants using the deposit () method. Link to the code — https://github.com/dfx-finance/protocol-v1-deprecated/blob/5fbeac837e57ded52e25572390a90c189ef363b1/contracts/Curve.sol#L489
- The deposit function called the function
ProportionalLiquidity.proportionalDeposit(), which registered the attacker’s LP token and deposited the attacker’s borrowed funds into the transaction pair contract. https://github.com/dfx-finance/protocol-v2/blob/90e5ae656f64ecf13e13b03b1cb7fb1d79e09f5b/src/Curve.sol#L659
- Because there was no outstanding amount at the attacker’s address, the transactions satisfied the validation when the transaction pair contract’s balance was checked, bypassing the necessity for transaction pair checks to pay back the flash loan. Link
flashCallback()method, in this case, allowed the attacker to withdraw the whole contract’s reserves of USDC and XIDR tokens and run arbitrary logic with no upfront cost in order to complete the flash loan transaction. Because the total token borrowed from the flash loan had now become zero, the attacker’s deposited lptoken remained in the contract’s balance.
- To finish the attack, the attacker executed the withdraw () function, destroyed the lptoken, and grabbed the
- The withdraw () function had a Re-entrancy protection modifier which could not be triggered since the flash loan was completed but the lptokens deposited by the attacker still existed in the lending contract, which actually belonged to the attacker, thus allowing an attacker to call the emergencyWithdraw() function any number of time until attacker withdraw all the deposited tokens.
- Due to the MEV bots being activated, the attacker lost a significant amount of money to the owner of those bots and was only able to recover about $4M in stolen money.