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!
Content-Type. If it's a malicious file extension, such as HTML, it's rejected though. So, what happens if the file type is not known? No Content-Type is sent!Content-Type. If this header is not included, it attempts to deduce the type of the file based upon the content! This is called MIME Sniffing. By sending a file with a random file type, the browser will sniff the type of it based upon the content. This allows for malicious content, such as HTML, to be included in the file that will be executed!ton.twitter.com in the browser. What can an attacker do with this? _moveDelegates is executed during the voting delegation process to give the votes to a representative. Although the voting power is moved to the representative, the delegator's tokens are NOT. So, they can transfer the tokens to another user who can now vote or delegate further these further. This is known as a sybil or double spend attack.approve ERC20 function. Since we trust Sushiswap and they need the approval to spend our money in the smart contract call, this is a pretty standard thing to do. processRoute took in a variable called route, which was generated off chain. This route is used for the path of tokens to be traded, with the first token being the sold and the final being the bought token. route variable had no verification performed on it though. When using the swap command code with a UniswapV3 pool, the user can provide an arbitrary address. uniswapV3SwapCallback function. There is validation that the sender is the pool, which is true since an attacker controls the contract address! Now, the smart contract thinks that the caller is a UniswapV3 pool.safeTransferFrom call, where the attacker controls the from parameter. Since the router has approvals from other users to spend their money, an attacker can use this to send the money to themselves! Additionally, all of the funds of a token can be drained from the router itself, since it has a call to safeTransfer on itself.borrow() function has the modifier solvent to check to see if it's above water. If it's not, then the transaction will revert. This is compared against the exchangeRate. Of particular importance is that this variable is mistakenly NOT upgraded in this function call.liquidate, the updating for the exchangeRate is done in the beginning of the function. If two prices are different between withdrawal and deposit, this can be a major case for exploitation.borrow function was 250 * 10^27. On the liquidate function the exchange rate was 328 * 10^27. By borrowing at one rate then liquidating at another, they were able to make a huge profit off of this. I'm guessing that the hacker was sitting on a large price difference for a while in order to exploit this bug._joinOrExit. It invokes the function _callPoolBalanceChange before calling _setMinimalSwapInfoPoolBalances. This is important because _setMinimalSwapInfoPoolBalances updates the token balances for balancer. The interesting part is that the protocol will always send back unused Ether to the user part way through execution via sendValue._callPoolBalanceChange but before _setMinimalSwapInfoPoolBalances. This means that the Balancer pool tokens have been minted but the vault has NOT been updated at this stage. This puts the contract into a funky state. exitPool function. BondGroup could be made at no cost by providing an empty array. By setting the maturity to be the same as BondGroup 10 (with 25K Eth), it would be a valid. exchangeEquivalientBonds. At this point, they had essentially create a worth bond and turned it into a valid one. Wild!burn function on the Uniswap core contract, the contract measures its own LP balance and burns it. Then, the withdrawn tokens are outputted to the caller of the function. Within the contract was 12K worth of liquidity tokens just waiting for somebody to take it. This is a ticking time bomb, since anybody could burn their LP tokens and accidentally receive the funds. get, then the contract would revert. The thought was that by the time the set and get had both been executed, the bot wouldn't know what hit them.get call was rejected by Infura even with manual gas overrides. Oh gosh... this means that the set had been done and the get was getting ready to go. The transaction slipped into the next block... they got a INSUFFICIENT_LIQUIDITY_BURNED error from Uniswap, meaning that somebody had performed the call and stolen the funds.getfailed was because it should have failed for the current blockchain state but not what they were updating it with. Or, know a private miner.