Introduction
We have observed an attack event on the Base chain, transaction at https://basescan.org/tx/0x619c44af9fedb8f5feea2dcae1da94b6d7e5e0e7f4f4a99352b6c4f5e43a4661. The attacker profited a total of 310,570 USDC and 10 cbETH from this attack, amounting to approximately $350,000.
The victim project is SumerMoney, a lending protocol based on Compound fork, where users can profit by providing liquidity. SumerMoney supports various underlying assets including ETH, USDC, USDbc, DAI, wstETH, cbETH, suUSD, and suETH. Each underlying asset corresponds to a lending pool. Users interact with the respective lending pool to perform operations.
Attack Detail
Initially, the attacker utilized a flashloan from Balancer through attack contract 1, borrowing 645,000 USDC
and 150 ETH
.
Subsequently, attack contract 1 created another attack contract (attack contract 2) and transferred the borrowed 64,5000 USDC
from Balancer to the new attack contract 2, along with 1 wei
.
Next, in the attack function of attack contract 2, the attacker exchanged 645,000 USDC
for 643,630 sdrUSDC
through mint
. Then, the attacker borrowed 150.358797170664290045 ETH
from the sdrETH pool
via borrow
and repaid the 150.358797170664290046 ETH
to the sdrETH pool
through repayBorrowBehalf
. The implementation of repayBorrowBehalf
is as follows:
It can be observed that the contract returns the excess amount to the user. Since the sdrETH pool
received 150.358797170664290046 ETH
but only lent 150.358797170664290045 ETH
, it returns 0.000000000000000001 ETH
to attack contract 2. Upon receiving the ETH transfer, attack contract 2 triggers the fallback
function, which in turn calls attack contract 1 to perform borrow
and redeemUnderlying
once again.
It is noticed that the attacker redeems 150 ETH
with only 74.99998941541294 sdrETH
(supposed to be 150 sdrETH
).
Let’s examine how the contract calculates the exchange rate between sdrToken
and underlying token
.
/*
* We get the current exchange rate and calculate the amount to be redeemed:
* redeemTokens = redeemAmountIn / exchangeRate
* redeemAmount = redeemAmountIn
*/
From the above formula, we understand that the underlying token amount
exchanged is the corresponding sdr token amount
divided by exchangeRate
. Similarly, the calculation of exchangeRate
is as follows:
/*
* Otherwise:
* exchangeRate = (totalCash + totalBorrows - totalReserves) / totalSupply
*/
Where totalCash
is calculated by the following function:
The vulnerability becomes apparent. Operations such as mint
, borrow
, redeem
, repay
should all be atomic. However, repayBorrowBehalf
exhibits a reentrancy vulnerability. By constructing a specific amount (borrowsAmount + 1 wei
), the attacker causes the sdrETH
pool to resend the surplus 1 wei
to the attacker's contract, triggering the fallback
function of attack contract 2. In the fallback
function (at the time of ETH transfer, exchangeRate
has doubled, increasing the value of sdrToken
in the attacker's possession), the attacker performs a reentrant attack by calling lending functions, borrowing other assets. Finally, the loans of SumerMoney and Balancer are repaid, completing the attack.