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!
SYSTEM this really means ANYTHING. Amount * (1 - feeRate) * (srcRate/destRate - newSrcRate/newDestRate). When the user withdraws their money, there is a bug in the actual code to take this out. Instead of using the variable sourceAmountAfterSettlement, sourceAmount is being used. amountReceived is calculated from the full sourceAmount instead of the sourceAmountAfterSettlement. In other words, the sourceAmount is slightly inflated. This earned the author a 150K bounty. 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.