On September 20, 2021, Vee Finance was attacked and contract funds were stolen.
Vee Finance is a lending protocol that is mainly forked from Compound Protocol and adds leveraged trading logic on this basis. Users can obtain loan vouchers through mortgage assets, and the loan vouchers can be leveraged in the protocol. When performing leveraged transactions, users will lend funds from the contract, and then create an order through the VeeProxyController contract. When the order is created, the contract will swap the borrowed funds into the target token in Pangolin. When the order expires or the stop-profit and stop-loss price is reached, a reverse swap will be performed, and then the loan will be returned.
The main cause of the accident was that in the process of creating an order for leveraged trading, only the price of the Pangolin pool was used by the oracle as the source of price feed, and the pool price fluctuated more than 3%. The oracle refreshed the price, causing the attacker to manipulate the price of the Pangolin pool. Manipulating the price of the Vee Finance oracle machine and the acquisition of the oracle machine price were not processed for decimals, resulting in the expected slippage check before the swap did not work.
- The oracle machine has a single source of price feed, and the refresh conditions are affected by the real-time number of tokens in the Pangolin pool (the pool price fluctuates by 3%, and it will be refreshed).
- Price acquisition has not been processed for decimals.
1. The check on the contract call is bypassed.
2. The target pair of margin trading is not whitelisted.
- When performing margin trading, the createOrderERC20ToERC20 function in the following code block will be called to create an order.
- When an order is created, the token exchange will be carried out through line 5 of the following code block.
- Before the token exchange, the expected slippage will be checked through the getAmountOutMin function on line 9 of the following code block.
- During slippage check, the priceA and PriceB quotes of the oracle will be obtained through lines 12 and 13 of the following code block, and then the number of TokenA that can be exchanged for TokenB at the current price is calculated through line 15 of the following code block. Finally, compare with the number of tokens acquired in the Pangolin pool. If the number of TokenB tokens that can be exchanged in the pool is greater than or equal to the expected number of TokenB that can be exchanged using the oracle, then it can be judged that the pool price is correct and not controlled, and the order creation logic is continued.
5. However, through on-chain records, when the oracle price is obtained, the obtained price decimals have not been processed. Therefore, if the decimals of TokenB is much greater than the decimals of TokenA, then there will be deviations in the calculation of the expected amount of exchangeable TokenB, amountFromOracle = priceA * swapAmountA / priceB will be smaller than expected.
6. At the same time, in most attacks, the prices of TokenA and TokenB obtained by the oracle machine are equal, which shows that the price obtained by the oracle machine is wrong.
7. After communicating with the project party, the project party reported that the source of the price feed for the oracle machine only uses the price of the Pangolin pool, and the price of the pool fluctuates more than 3%, the oracle machine will refresh the price.
8. Therefore, the attacker manipulates the number of Pangolin’s tokens to make Vee Finance’s oracle machine refresh the price. This directly caused the contract to obtain the wrong price from the oracle during the slippage check, which caused the slippage check to be bypassed.
- After the oracle machine obtains the token price, it should be processed with uniform decimals.
- No support for tokens with a single source of price feed.
- Modify the contract call check to: require(msg.sender == tx.origin)
- Whitelist restrictions on the target pair of margin trading.
- Whitelist restrictions on oracle price feeding permissions.
Disclaimer: Pangolin.Exchange is not affected by the attack and still safe to use.