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!
EthCrossChainData and EthCrossChainManager. EthCrossChainData can only be invoked by contract owners. It is used to manage the keys for nodes and many other sensitive operations. EthCrossChainManager can trigger messages from another chain to the Polychain. To do this, the function verifyHeaderAndExecuteTx within this contract is called with a poly contract to execute. This is intended to ONLY call functions with a specific solidity function ID though.bytes4(keccak256(abi.encodePacked(_method, "(bytes,bytes,uint64)"))). A user controls the parameter _method. Since functions are literally just the code above, if an attacker could specify a DIFFERENT method ID, they could call arbitrary functions in the contract. This requires brute forcing the hash because of the function signature though. EthCrossChainData contract can only be updated by owners? Well, we can proxy the request with the previous bug, since EthCrossChainManager is an owner of it! By proxying a call to this with the previous bug, we can call sensitive functions! In particular, putCurEpochConPubKeyBytes adds a public key to the contract for verification. mintTimeLock locks the asset for a set period of time, mintValueLock will unlock only when the asset goes above or below a certain value and mintAddressLock will unlock assets when a specific account opens it. All of these paths call the same set of additional contracts for unlocking and locking contracts.depositAdditionalToFNFT is used to lock more underlying assets to an existing lock. When this happens, it assumes that the amount can be evenly distributing among the existing NFT owner. If it is not, then a new lock is placed on the ID and burns some quantity of the old FNFT tokens and mints the new quantity. This is done by adding up the old quantity with the new quantity to get a total quantity. depositAmount for a given FNFT is put BOTH into the old lock and the new lock! This means we can use our funds twice, which is a recipe for disaster. Instead, it should destroy the original lock and put the funds into the new one only. id gets updated. By creating an FNFT, the id should be increased. However, prior to incrementing the counter the variable fnftId, ERC-1155 standard's callback mechanism can be triggered. fnftId in the wrong state, we can create a update the previous FNFT (increment by 1) to update the depositAmount of a token. Why is this bad? funds in the contract but not the amount of tokens minted. As a result, an attacker can create a large amount of tokens with zero value (token 2) and update the value of token 1 to really update the value of token 2! By doing this, the cost of token 2 is not zero but there are a ton of tokens we can withdraw for a high value. bNFT or cNFT. This is a wrapper around the underlying NFT type (ERC-1155, ERC-721 or CryptoPunk). This demonstrates ownership over a specific NFT. cNFT for ERC-721 or Cryptopunks, the parameter amounts is NOT checked. This means multiple ownership tokens can be minted, while the NFT itself is still in the contract. Instead of a 1 to 1 relationship there is a 1 to many relationship, which is real bad for unique objects.cNFT to redeem there original NFT while still having leftover cNFT. Practically, since this is a unique token, if an attacker sold the NFT on this platform, they would simply be able to redeem it using the extra cNFT token! This steals the token from the other user. validatorState.amount and validatorState.stakerCount. The amount is the amount of tokens staked, which is the staking power. The stakerCount is the total number of stakers in the contract, which is likely to be 100. validatorState.amount updates by subtracting the amount of the user who provided the value. This allows users to stake and unstake as they would like, collecting rewards when they unstake. validatorState.amount is updated to subtract the amount of token delegated. Additionally, when the original user wants to retrieve their unstaked token, they still can! This means we can subtract from validatorState.amount multiple times. validatorState.amount variable to be very small. At this point, we could override updates with the 2/3 rule to control the network. Balance of token A * Balance of token B = Constant product called the "constant product" equation. The adaption is to generalize this formula to work for more than 2 tokens they called weighted math. decimals of a token. Because of this, the actual value and the slippage check are WAYYY different, allowing for oracle manipulation. 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.