On Nov-26–2021 06:48:42 PM +UTC, a hacker constructed an attack transaction: https://bscscan.com/tx/0xb5365a299c07c81670e52934893793ad7c225a5cf30b641e20b451b2b5815593
First, the Lever attack contract A flashloaned 2,100 BNB from PancakeSwap and deposited 2000 BNB on Lever’s BNB vault.
It then borrowed 1500 BNB from Lever’s BNB vault and transferred it to Lever attack contract B. The Lever attack contract B deposited 1500 BNB and used it to drain 32.78 ETH, 1,068.05 BAKE, 167.25 XVS, 1,042.89DAI, 64,157.79 BUSD, 54,335.19USDT ,2.8806 BTC, 1,930.01CAKE, 463.0078DOT, and 332.9184 WBNB. (The total loss equals $652941.949 at present market price.)
The key step is the Lever attack contract A used Lever attack contract B’s 1500 xBNB (which had been collateralized to borrow other assets) to repay the 1500 dBNB it borrowed, by calling the repay() function in our MarginPool.Sol contract:
function repay(
address asset,
uint256 amount,
address onBehalfOf
) external override whenNotPaused returns (uint256) {
DataTypes.ReserveData storage reserve = _reserves[asset];uint256 variableDebt = Helpers.getUserCurrentDebt(onBehalfOf, reserve);
address xToken = reserve.xTokenAddress;
uint256 userBalance = IERC20(xToken).balanceOf(msg.sender);ValidationLogic.validateRepay(
reserve,
amount,
onBehalfOf,
variableDebt,
userBalance
);
function validateRepay(
DataTypes.ReserveData storage reserve,
uint256 amountSent,
address onBehalfOf,
uint256 variableDebt,
uint256 userBalance
) external view {
bool isActive = reserve.configuration.getActive();require(isActive, Errors.VL_NO_ACTIVE_RESERVE);
require(amountSent > 0, Errors.VL_INVALID_AMOUNT);
require(variableDebt > 0, Errors.VL_NO_DEBT_OF_SELECTED_TYPE);
require(userBalance >= amountSent, “deposit is less than debt”);require(
amountSent != uint256(-1) || msg.sender == onBehalfOf,
Errors.VL_NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF
);
}
The contract didn’t check the liabilities of the caller at this step. Thus, the Lever attack contract B was able to repay the attack contract A’s dtoken with its xtoken.
So now the Lever attack contract A didn’t have any debt on Lever and withdrewed the 2000 BNB it initially deposited. The Lever attack contract A repaid the flashloan on PancakeSwap. The hack was completed.
This loophole had been there since the first contract deployment, but was neglected by the team and our third-party auditing partners, until it was exploited by the hacker.
We are now using all of our resources to retrieve the funds and will release a compensation plan shortly.