In January 2022, Qubit Finance, a Binance Smart Chain (BSC)-based project, was the victim of an attack. The attacker exploited vulnerabilities in the protocol to steal $80 million in tokens.
Inside the Attack
The attacker called the deposit function in the QBridge ETH contract with malicious data that passed all of the contract’s checks. However, the transaction had no ETH attached.
Within this function, the contract calls the safeTransferFrom function within the token’s contract to send the tokens from the depositor into the contract. This call to safeTransferFrom would cause the deposit function to fail if the provided token address was invalid.
However, the contract did not use OpenZeppelin’s SafeERC20 library. If the contract had used this library, the exploit would not have been possible as the SafeERC20.safeTransferFrom function makes use of functionCall() (function from OpenZeppelin’s Address.sol contract) which verifies that the target address contains contract code. This is not the case with the 0 address.
The exploited contract used a modified safeTransferFrom() function which instead of making use of functionCall() to verify that the target address contained contract code, used the call() function directly. As the 0 address has no code at all, no code is run, and the call is completed successfully without reverting. As a result, the deposit function executed successfully but no real tokens were deposited.
The Ethereum QBridge caught the Deposit event and interpreted it as a valid deposit of ETH. As a result, qXETH tokens were minted for the attacker on BSC.
By repeating this process multiple times, the attacker was able to build up a large amount of qXETH without depositing any real tokens into the protocol. The attacker then was able to convert these tokens into BNB, draining about $80 million in assets from the protocol.
Lessons Learned From the Attack
The Qubit hack was made possible by the fact that a legacy function (deposit) remained in the contract after it was replaced by a newer function (depositETH). Their overlapping functionality and vulnerabilities that allowed fake deposits made this hack possible.
The existence of legacy code in the contract indicates issues with code review and change management. Similarly, the vulnerabilities that allowed the fake deposit might have been detected during a smart contract audit.