A few days ago FEGexPRO got hacked. This article gives a detailed explanation of vulnerability by the SmartState team.
Example of the attack transaction in BSC:
Example of the attack transaction in ETH:
In all, the attacker was able to steal 143 ETH (approx. $305k) and 3277 BNB (approx. $ 1mil).
FEGexPRO system allows users to trade their tokens using a FEGexPRO contract at https://bscscan.com/address/0x66b425a3002c7076ab73ad3af9bb6eb106597170.
This contract has a swapToSwap function which allows users to migrate their funds to another swap contract.
This function accepts a path argument which becomes a new swap address. However, the path argument is not validated and users can put in any address they want. In order to migrate to another swap, the user needs to have tokens of one of the required assets in their account balance.
The user can deposit tokens to FEGexPRO via the depositInternal method described below.
The depositInternal function accepts one of two required assets and increases the user’s balance. In case of Main asset, amount of deposit is calculated by subbing sum of _totalSupply2, _totalSupply7 and _totalSupply8 of the required asset from the balance of that asset on the contract.
Such depositing logic and the unvalidated path parameter in the swapToSwap function creates a vulnerability.
Let us assume that the attacker wants to drain Main asset and the current sum totalSupplies of Main asset on the contract is 300. He deposits 100 tokens of Main asset to the contract, which makes his Main asset balance equal to 100, and the sum totalSupplies of Main asset is now 400.
Now he calls swapToSwap contract providing his contract address as path, the address of Main as asset, the argument to his address and amount ad 99 tokens. The function swapToSwap will approve the desired amount of tokens to the address provided by the attacker.
Then it will decrease _totalSupply2 and _balances2 by the provided amount and call several functions on the attacker’s contract which are supposed to transfer tokens from this contract to the attacker’s.
However what if the attacker’s contract functions will not actually transfer tokens from this contract but will basically do nothing?
The attacker’s contract will get approval for desired amount, _totalSupply2, and the attacker’s balance will be decreased by the desired amount and nothing else will happen. Which means that now FEGexPRO contract has Main asset tokens on its balance, but _totalSupply2 is decreased.
Next step is to call the depositInternal function again and provide the minimal possible amount (1 wei). So the current sum of totalSupplies of Main asset is 301, because the user has decreased _totalSupply2 by 99 when calling the swapToSwap function, and the balance of Main asset on the contract is 400, because the attacker’s contract didn’t do anything with tokens in the previous step.
So, the difference now is 99 tokens + 1 wei; however, the attacker has deposited only 1 wei and his balance along with _totalSupply2 will be increased by 99 tokens + 1 wei.
Now the attacker can call the swapToSwap function again with the same arguments; he will get approval for another 99 tokens for his contract, _totalSupply2 and his balance will be decreased and nothing else will happen.
As a result of these actions, the attacker now has received approval for 99 + 99 tokens, which is 198 tokens, but has only spent 100 tokens to conduct the attack. By repeating this action, the attacker can gain infinite approvals for the Main Asset and drain all tokens of the Main asset from the contract.
The same attack vector is applicable for the Token asset which allows to drain all Token assets from the contract.
(Special thanks to @blocksecteam transaction explorer.)