DeFi protocol Akropolis vulnerability detailed explanation: hackers reproduce "classic re-entry attack" to steal 2.03 million DAI


At 19:50 yesterday evening, the DeFi protocol Akropolis was attacked by hackers.

The security personnel of the blockchain security company PeckShield quickly identified the problem that the SavingsModule contract of the Akropolis project had a certain flaw in handling user storage assets. Hackers used this flaw to implement 17 reentry attacks in a row, leading to its YCurve And the sUSD fund pool lost 2.03 million DAI. 

Technical summary:

The reasons for this attack are as follows:

1) The contract does not perform whitelist verification on the Token stored by the user

2) The key deposit function is not protected against reentry attacks

Simply put: hackers used the storage asset verification flaws of the Akropolis project to launch multiple consecutive reentry attacks on the contract, causing the Akropolis contract to issue a large number of pooltokens out of thin air without new asset injection, and then reuse these pooltokens withdrew DAI from the YCurve and sUSD pools, resulting in the loss of 2.03 million DAI in the project contract.

Detailed explanation of the attack process:

Recurrence of the attack process:

We analyzed the transaction hash of the hacker attack (0xe1f375a47172b5612d96496a4599247049f07c9a7d518929fbe296b0c281e04d) and found that the attack came from a malicious ERC20 contract address (0xe2307837524Db8961C4541f943598654240bd62f)

This malicious contract implements a hook function so that the function will be executed when transferFrom() (function signature: 0x23b872dd) is called.

The attacker first calls the deposit() function in SavingsModule.sol (https://github.com/akropolisio/delphi/blob/release-1.0/contracts/modules/savings/SavingsModule.sol#L217-L277) and sets himself The malicious contract written at the beginning of 0xe230 is passed in as the token address to be stored. When the transferFrom() function of its malicious token is called, its hook function will call the deposit() function again and deposit the real DAI assets.

Since the amount of additional issuance of pooltokens is calculated by the difference between the balances before and after the token deposit. So the real DAI assets of the second deposit will be calculated twice and used to mint pooltokens. The first time was when the malicious contract 0xe230 was stored, and the second time was when DAI was stored. In other words, if 25K DAI is deposited during the second storage, the total pooltokens minted will be doubled due to the reentry attack, which is 50K DAI.

By analogy, hackers launched a total of 17 reentry attacks and obtained a total of 2,030,841.0177 DAI assets.

It is worth noting that at the very beginning of the attack, the attacker also used dYdX's flash loan function (https://etherscan.io/tx/0xddf8c15880a20efa0f3964207d345ff71fbb9400032b5d33b9346876bd131dc2).

Detailed explanation of core vulnerabilities:

Next, we analyze the token storage logic with loopholes. Akropolis users can store tokens in Delphi Savings Pools, and the funds pool will mint corresponding pooltokens to users. The core logic is in SavingsModule::deposit() (line 1,944).

The first step: The attacker calls the deposit() function and provides the _tokens parameter. This function will calculate the balance of tokens before and after further calling depositToProtocol(_protocol, _tokens, _dnAmounts), and determine the number of poolTokens to be minted through the change of token balance (line 1,970). The depositToProtocol() function will call the safeTransferFrom() function of the target token to transfer the token (line 2,004). However, the deposit() function does not detect reentry attacks, nor does it check whether the deposited tokens are malicious tokens;

Step 2: When the transferFrom() function of the malicious token is called, trigger the hook function to call the deposit() function again;

Step 3: Because the attacker deposits real DAI tokens when the deposit() function is called for the second time, which changes the token balance of the pool, the attacker can obtain poolTokens minted by the fund pool;

Step 4: When the second deposit() function call ends, the code execution flow will return to the context of the first depositToProtocol() function call to store tokens. At this time, the token balance change will be calculated again. The change of coin balance in this era is the same as the change of coin balance in the second call to deposit() function. Therefore, the attacker can obtain the corresponding number of poolTokens again.

Situation of stolen assets:

The stolen assets of this attack are currently stored in the wallet [0x9f26](https://etherscan.io/address/0x9f26ae5cd245bfeeb5926d61497550f79d9c6c1c). CoinHolmes, a digital asset tracking platform under PeckShield, is monitoring the address in an all-round way, and further locking analysis and tracking of its capital flow, in order to assist the project party in recovering the stolen assets. For the industry's leading blockchain security

Recommended reading:

PeckShield: hardcore technology analysis, bZx protocol was attacked by hackers

PeckShield: Detailed explanation of the vulnerabilities in the Opyn smart contract of the DeFi platform-the attacker's empty glove white wolf!

PeckShield: DeFi platform Balancer was attacked by hackers and technical disassembly of the whole process

PeckShield: The technical fate behind the bZx protocol being hit by hackers again

Analysis of the principle of 0x protocol vulnerability: Malicious pending orders can disrupt normal transaction order

The whole story of Uniswap and Lendf.Me being attacked: "Domino" collapse under the DeFi Lego combination

AirSwap smart contract vulnerability explained: user assets can be maliciously eaten by attackers?

Guess you like

Origin blog.csdn.net/PeckShield/article/details/109685041