On June 16, 2021, the alETH contract of the Ethereum DeFi project Alchemix is suspected to have a security problem. On the 17th, Alchemix released an accident analysis report. The SlowMist safety team quickly intervened in the analysis and sorted out the entire context and core key points of the incident based on the official analysis report for your reference.
Conclusion
This analysis article is very long. Let me talk about the conclusion first, so that everyone can have a general understanding. The main reason for this accident was that Alchemix added 3 vaults through transmuter, which resulted in the revenue information being recorded on a wrong element, and the correct index value was not passed in when the harvest function of transmuter was called, resulting in passing the wrong element. The wrong income was obtained, and the wrong income of 4300 ETH was sent to the adapter contract to help the user repay the loan of alETH, which caused the problem of increased income and led to tragedy.
Analysis-Round 1
According to the official accident analysis report, the cause of this accident was that the official alETH deployment script accidentally created additional vaults, which caused Alchemix to use the wrong index in the vaults array and calculate the wrong reward, which caused the transmuter to take all The reward is used to repay all the user’s liabilities. I know that this brief analysis alone is a bit confusing and confusing, so we can only focus on the official transaction and see if we can find the truth.
According to the official transaction, analyzed by the ethtx.info analysis tool, it is not difficult to find that this transaction called the harvest function of the AlchemistEth contract, and passed the parameter _vaultId=0, and finally returned the two values “4308144937764982868765” and “4308144937764982866415”.
In order to better understand the role of the harvest function, we need to analyze the entire function:
The harvest function actually contains two important operations, namely harvesting rewards and distributing the rewards to the transmuter contract. Among them, vault is a library contract, and the harvest logic is implemented as follows:
The harvest function of the vault library contract actually checks the total amount of funds in the external adapter, and then calculates the profit based on the amount of funds in the adapter minus the user’s recharge amount.
Here we can understand this adapter as a strategy pool used to manage users’ funds and income. Then we return to the harvest function in the AlchemistEth contract at the beginning of the user, and find that the returned two values “4308144937764982868765” and “4308144937764982866415” actually correspond to the number of tokens that need to be withdrawn and the value calculated by the harvest function of the vault library contract. The number of tokens retrieved in the adapter (strategy pool). Since the income token corresponding to this adapter is WETH with a precision of 18 digits, the value of “4308144937764982866415” is converted to “4308.144937764982866415” WETH.
In other words, this harvest operation yielded more than 4,300 ETH, and then this income was distributed to the transmuter contract through the _distributeToTransmuter function in the next step. Let’s see what the logic of the distribution process is:
The logic of the _distributeToTransmuter function is only three simple lines. Our main concern is the last external call-the lowerHashMinted function. The xtoken corresponding to this function here refers to alETH itself. Because alETH itself is borrowed by users through loans, the operation of lowerHashMinted is actually to use harvest proceeds to reduce the total amount of alETH loaned out, thereby reducing the loan for each user. In summary, the proceeds of harvest 4300 ETH will be used to repay the user’s alETH loan.
Here is a summary of the process, that is, the AlchemistEth contract obtained 4300 ETH through the harvest function, and distributed this income to repay the user’s alETH loan, which led to the situation we saw-alETH has been loaned Of users can get back their pledged ETH without repayment. So why is there a profit of 4300 ETH? How did this extra 4300 ETH come from? In response to this problem, we start the next round of analysis.
Analysis — Round 2
To understand why the extra 4300 ETH was added, it is necessary to understand AlchemistEth’s fund storage process. In the AlchemistEth contract, the total recharge status of the contract is recorded using the Data structure of the Vault library, and then the corresponding recharge amount (totalDeposit) is updated through the flushActiveVault function.
Then the depositAll function will transfer the amount of the recharged tokens to the corresponding adapter (strategy pool), then in the next harvest, the totalValue obtained through the adapter (strategy pool) will be the user’s principal plus the strategy pool income. In order to calculate the principal part of the income process, we debugged the official transaction and found that the principal was only 9000 ETH, and the income obtained from the adapter plus the principal amounted to 13000 ETH, which means that the principal of 9000 ETH was generated With a gain of 4300 ETH.
However, according to the logic analyzed above, the user’s principal will not generate such a large income. The problem must be the totalValue obtained by the adapter. In other words, the adapter not only has AlchemistEth recharge tokens, but also has other income channels. In order to verify our ideas, the SlowMist security team analyzed all the token income of the adapter and found an abnormal transfer behavior, and the amount was just right for the extra 4300 ETH income. In other words, the problem lies here.
By looking at the transaction data, it is found that this is a transaction that invokes the harvest operation, and the contract invoked is the transmuter contract:
In other words, there is a problem with the harvest function. The logic of the harvest function is as follows:
It also calls the harvest function of vault, a familiar formula, and a familiar taste. We debugged again and found an amazing fact-when the revenue was carried out, the totalDeposit of vault turned out to be 0, resulting in 4300 ETH of the revenue being directly distributed to the adapter, which caused the totalValue obtained by the adapter to be wrong, and 4300 ETH was added. , The reason is here.
At this point, we are very close to the truth. What remains to be solved is why totalDeposit is 0? We inquired where the totalDeposit can be changed in the transmuter contract, and found that only the _plantOrRecallExcessFunds function can change this value, and the upper layer of this function calls the distribute function. The distribute function of the transmuter contract is called by the AlchemistEth contract when it earns. In other words, the process itself should be:
1. AlchemistEth contract calls harvest for profit
2. The AlchemistEth contract calls the distribute function of the transmuter contract to record the income and sends the income to the adapter
3. The adapter receives the proceeds from the transmuter and repays the user’s alETH loan based on the proceeds
But the problem lies in the _plantOrRecallExcessFunds function. Since _vaults.last() is used to obtain the latest vault when recording the recharge information, the recharge information is actually superimposed on the last element. But the project party called the setActiveVault function three times, so the recharge information is actually superimposed on the 3rd element of the _vaults array, that is, the vault element with index 2. However, the _vaultId passed in during harvest of the transmuter contract is 0, and element 0 has no recharge record, so the transmuter contract mistakenly gave all the proceeds to the adapter. Caused the tragedy to happen.
Summary
For some reason, the Alchemix project team added 3 vaults through transmuter, which resulted in the revenue information being recorded on a wrong element, and when calling the harvest function of transmuter, the correct index value was not passed in, resulting in passing the wrong element. The wrong income was obtained, and the wrong income was sent to the adapter contract, resulting in increased income and tragedy.
The SlowMist security team hereby reminds that DeFi is a complex system. When performing DeFi operations, remember to check every process in the business logic to prevent accidents. If necessary, you can contact a professional security team to do it. Professional safety audit to prevent accidents.
About us
SlowMist Technology is a company focused on blockchain ecosystem security. It has served many top or well-known projects around the world through “the security solution that integrated the threat discovery and threat defense while tailored to local conditions” and has thousands of commercial customers. SlowMist’s security solutions include security audit, threat intelligence (BTI), bug bounty, defense deployment, security consultant, and other services. SlowMist is equipped with cryptocurrency anti-money laundering (AML), false top-up scanner, vulnerability scanner, and vulnerability monitoring (Vulpush), hacked project archives (SlowMist Hacked), smart contract firewall (FireWall.X), Safe Staking and other SAAS security products. It has been widely concerned and recognized by the industry.
Reference link
Official accident analysis report:
https://forum.alchemix.fi/public/d/137-incident-report-06162021
Earning calculation error transaction: https://etherscan.io/tx/0x3cc071f9f40294bb250fc7b9aa6b2d7e6ca5707ce4d6d222157d7a0feef618b3