People often ask me "How did you learn how to hack?" The answer: by reading. This page is a collection of the blog posts and other articles that I have accumulated over the years of my journey. Enjoy!
safeTransferFrom of ERC20 had a reentrancy vulnerability in it. Once the transfer occurred, a callback to the address (contract) was made._pool amount is set the current balance of the pool, not what has been taken out by the recursive call beforehand. Additionally, it would mint 5 times the expected shares as well. GroupSize property is used to keep track of the amount of transactions in play that are signed. However, this is not checked and is assumed to be 5. If this was larger than 5 then additional transactions can be included to drain other pools. AnyswapERC20 and AnyswapRouter assume that the function permit is implemented for all token contracts. This function is used to make the gasless approval of token transfers. Since some of these contracts had fallback functions, that was triggered instead of the permit code.from of the code. As a result, if there are ANY permits about an address, an attacker is able to steal these funds. To launch the attack, they need to trade a token that meets the criteria above. When they attempt to run this code for transferring funds from another user, there is no limitation on the amount of funds sent.fallback breaks this assumption. If you try to call code at an address that has no code, this will run STOP (which is a success) instead of REVERT as well.safeTransferFrom is used to send tokens from the depositor and into the contract. When doing this, the user needs to provide an ERC20 token address. transferFrom to remove funds of the user to put into the contract. Then, on the other blockchain, the user gets newly minted tokens. However, if no code was at this address, the transfer of funds would never occur but the minting would still take place. qXETH could be minted. This drained an estimated $80 million from the protocolload_instruction_at_checked instead of load_instruction_at was made. One of them makes the assumption that the verification has been done while the other does not. depositEth and the ETH20 deposit function. One of the functions validates that the callData and msg.value are the same. However, the other does not make this check in a require statement. callData without sending over any money. Then, they can withdraw this money, draining the contract of funds. depositByAddLiquidity contains a reentrancy vulnerability in it. depositByAddLiquidity, an internal call is made that transfers the caller deposit into a new pool. However, the pool contract can be controlled by an attacker. Once the flow is sent to the attacker contract, we can reenter this contract again. deposit can allow for moving the money TWICE. This can be repeated indefinitely to drain the contract of money. A twitter thread showing the code is at here from PeckShield. require statement, there is a validation that the user deposits enough other tokens in order to mint the new token. However, we are dealing with FSM, FTM and ETH are input, all at the same time. These require statements must be on point in order for this to work. msg.value (ETH) and not the minimum amount of FTM tokens. As a result, an attacker could ONLY send ETH and FSM tokens but send NO FSM tokens. This error allowed an attacker to mint XFTM without depositing any FTM. _minFtmIn variable contains ETH instead of FTM token minimum amount. Since this already passed, it was a major problem. The code is shown below:
require(_minFtmIn < ftmIn, "Pool::mint: Not enough FTM input");
XFTM without ever entering in any FTM. So, here is how they stole 2 million dollars:
XFTM token without entering in FTM tokens.XFTM token.XFTM token to FTM. Remember, we created these out of thin air.emergancyCommit function that happens in 24 hours. emergancyCommit(). Since the proposal was just sending the money to the Ukraine and the attacker, the money was gone. At this point, the attacker used the money to pay back the Flash loan.