Slow Mist: Analysis of the whole process of Lendf.Me being stolen and defense suggestions
慢雾科技
2020-04-19 09:23
本文约2480字,阅读全文需要约10分钟
According to the preliminary statistical analysis of SlowMist's anti-money laundering (AML) system, the cumulative loss of Lendf.Me being attacked is about 24,696,616 US dollars.

Editor's Note: This article comes fromSlow Mist Technology (ID: SlowMist)Editor's Note: This article comes from

Slow Mist Technology (ID: SlowMist)

, reprinted with authorization from Odaily.

WETH: 55159.02134,
WBTC: 9.01152,
CHAI: 77930.93433,
HBTC: 320.27714,
HUSD: 432162.90569,
BUSD: 480787.88767,
PAX: 587014.60367,
TUSD: 459794.38763,
USDC: 698916.40348,
USDT: 7180525.08156,
USDx: 510868.16067,
imBTC: 291.3471

According to the preliminary statistical analysis of the Anti-Money Laundering (AML) system of SlowMist Technology, the cumulative loss of Lendf.Me being attacked is about 24,696,616 US dollars. The specific stolen currency and amount are as follows:

The following is the detailed analysis process:

attack details

secondary title

attack details

The address of the attacker who attacked Lendf.Me this time is 0xa9bf70a420d364e923c74448d9d817d3f2a77822, and the attacker attacked Lendf.Me by deploying the contract 0x538359785a8d5ab1a741a0ba94f26a800759d91d.

By viewing one of the attacker's transactions on Etherscan: https://etherscan.io/tx/0xae7d664bdfcc54220df4f18d339005c6faf6e62c9ca79c56387bc0389274363b

We found that the attacker first deposited 0.00021593 imBTCs, but successfully withdrew 0.00043188 imBTCs from Lendf.Me, and the withdrawn amount was almost double the deposited amount. So how did the attacker get doubled balance from a short transaction? This requires us to deeply analyze each action in the transaction to see what happened.

By viewing the transaction on bloxy.info, we can know the complete transaction process

Here, we can easily find that the attacker's withdraw() call occurs in the transferFrom function, that is, when Lendf.Me calls the user's tokensToSend() hook function through transferFrom. Obviously, the attacker re-entered the Lendf.Me contract through the supply() function, causing a re-entrancy attack, so what are the specific details of the attack? Let's follow up with the contract code of Lendf.Me next.

code analysis

secondary title

code analysis

After a series of processing, the supply() function of Lendf.Me will call a doTransferIn function to deposit the currency provided by the user into the contract, and then assign some information of the market variable. Looking back at the attack process just mentioned, the attacker called the withdraw() function to withdraw cash through reentrancy in the second supply() function, that is to say, in the second supply() function, after line 1590 The operation will not be executed before withdraw(), and the code after line 1590 will continue to execute after withdraw() is executed. The operations here lead to an increase in the attacker's withdrawable balance.

Let's analyze the supply() function in depth:

According to the above figure, we can see that at the end of the supply() function, the balance of the market and the user will be updated. Before that, the user’s balance will be pre-acquired at the beginning of the function and saved in localResults.userSupplyCurrent, as follows:

By assigning a value to the localResults variable, the user's transfer information will be temporarily stored in this variable first, and then the attacker executes the withdraw() function at this time. Let's look at the code of the withdraw() function:

There are two key points here:

1. At the beginning of the function, the contract first obtains the storage market and supplyBalance variables.

According to the normal withdrawal logic, when withdraw() is executed alone, the user’s balance will be deducted and updated normally, but because the attacker embeds withdraw() in supply(), the user’s balance is updated in the withdraw() function After the balance (supplyBalance), the next code to be executed in the supply() function, that is, after line 1590, the user's balance will be updated again, and the value used for the update will be saved at the beginning of the previous supply() function The user's original deposit in localResults plus the value of the attacker's first call to the supply() function deposit.

Under such an operation, although the user's balance has been deducted after the withdrawal, the logic of the next supply() function will overwrite the value when the user has not deducted the withdrawal amount again, causing the attacker to perform the withdrawal operation , but instead of deducting the balance, it caused the balance to increase. In this way, the attacker can withdraw cash in an exponential amount until Lendf.Me is emptied.

The contract should set the pause switch as much as possible, so as to detect and stop the loss in time when a "black swan" event occurs

  • Add a lock mechanism to key business operation methods, such as: OpenZeppelin's ReentrancyGuard

  • When developing a contract, use the writing style of first changing the variables of this contract, and then making external calls

  • Before the project goes online, an excellent third-party security team is invited to conduct a comprehensive security audit to discover potential security issues as much as possible

  • When multiple contracts are connected, it is also necessary to check the code security and business security of the multi-party contracts, and fully consider the security issues under the combination of various business scenarios

  • The contract should set the pause switch as much as possible, so as to detect and stop the loss in time when a "black swan" event occurs

  • Security is dynamic, and each project party also needs to capture threat intelligence that may be related to its own project in a timely manner, and promptly investigate potential security risks

Attached:

OpenZeppelin ReentrancyGuard

慢雾科技
作者文库