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!
orderID. This should have been valid as long as the collateral (NFT) was still in the system. orderID was still valid, even if the collateral had been taken out of the contract. This allows for the using of the receipt to take out loans, while the contract not longer possessed the collateral necessary to force the payback of the loan. LP tokens/total LP tokens. This allows them to obtain a total for the underlying assets proportional to what they put in. At this point, the tokens will be burned (destroyed). This math is where the vulnerability occurs at because of bad state handling. onSwap() function of the Space AMM pool. onSwap() would update the prices of the token depending on the actions performed on the actual swap. In particular, updating the amount of tokens available. onSwap() method does NOT validate who makes this call. As a result, an attacker could provide arbitrary values to this to control the oracle. By controlling the oracle, the price of tokens could be set, giving an attacker an easy time stealing money. delegateCall() from the proxy to the contract. A final point is that the initialization function has to be a regular function instead of a constructor. init function set the caller to be the owner of the contract; this is another constructor related problem! With this, the attacker could upgrade the contract to their own malicious values, then set the new location to be their own contract. Finally, calling selfdestruct would drain the funds and make the proxy useless. SELFDESTRUCT would have bricked the contract and stolen all of the money. To fix this, they simply had to call initialize themselves. branchMask is used to keep the system secure and must be unique. This is important, since this was the ID that was being confirmed for a exit being executed or not. MerklePatriciaProof.verify and once again at WithdrawManager.verifyInclusion. The decoding of the second function ignores several values while decoding it. Since the branch mask id can have many different variations from this bad decoding and this is used for an ID we have a problem. 0x0 for the mask. Since this was not check, the particular attack mentioned above was possible. Overall, a fascinating bug from the improper validation of cryptography. calcGav() to mint tokens based upon the cost. The cost of the share is based upon the cost of the Idle Tokens, such as idleUSDCYield.price = totNav.div(totSupply0;. If we control the total supply, then we could manipulate the cost of assets in the pool. How can this be done? The contract has internal flashloan functionality, which can drastically change the price of the IdleToken. IdleUSDCYield tokens. This will affect the GAV calculations for the pool._beforeTokenTransfer that does some validation steps when transferring out yield to an externally allowed entity. After the transfer occurs, there is an if statement to update the state of the contract. from and to are neither 0x0 or the vault contract itself. Is it possible to perform the transfer without updating the state? _burn is called. When the code is called with _beforeTokenTransfer(account, address(0), amount), the address is sent to zero. In particular, delegated funds being removed do not update the main state. This means we can arbitrarily increase the delegated funds!FutureVault vault. withdraw to redeem on Principal Tokens for the interest gained. This works because the redeem balance for a user compared to their delegated amount is not run._withdraw function. Overall, great bug that was from the over-reuse of code. ERC20 is hard to implement correctly with all of the custom logic needed for specific applications.