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!
ft-get-supply with a non-existent address the Rust error handling doesn't consider the case where nothing is returned. So, the Clarity VM crashes in this case.Some/None in Rust. Overall, a really simple bug that paid out quite a bit. The more esoteric stuff you look, the lower the bugs will be on the tree.Cf-Cache-Status: HIT header in the URL, which made them think that something was off here. After playing around with the requests, they realized that anything under /share/ was being cached by Cloudflare./share/ to cache it but this came from a different path in reality?https://chat.openai.com/share/%2F..%2Fapi/auth/session?cachebuster=123
%2F..%2F after the share will be processed by the backend server but NOT the CDN. So, the CDN thinks we're at /share but the backend thinks we're at a different path. By setting this to be the /auth/session path, this creates the same web cache deception vulnerability as before. The author has a really good image on describing how the flow of this attack works to steal the session information via forcing something to be cached that shouldn't be.UnpackLog() to get further information, we have a problem though. UnpackLog() is used to take an Ethereum event log into a Golang struct. However, there is a missing check for the Topic of the code. Each event type has its own selector, just like functions do. From parsing, the only restriction was that it had to have the same amount of indexed parameters. With this, we have a type confusion vulnerability.StakeUpdate is call is the most interesting one to exploit but this affected a bunch of other issues. The goal is to use a SignerChange() event to trigger an StakeUpdate() function to steal all of the funds. The validatorIds correspond to the same field and the address in SignerChange is the amount (which would be a crazy large value). The final thing that lines up is the nonce from the change with the address on the update.MsgStakeUpdate() event.State-Sync is a mechanism used by Polygon PoS to push events from Ethereum L1 to the network. Since it processes incoming transfers from the Polygon bridge to Plasma bridge, it's a super interesting attack surface. The state sync is a feature that allows users to participate in Consensus even if there's an RPC outage by agreeing with the super majority. sendFrom() function is responsible for sending staked users' tokens across chains. Additionally, a user can allocate allowance to another user for these cross-chain calls. Inversely, the function _debtFrom() checks that the user calling sendFrom() has been allocated the proper allowance from the send address. If this is true, it burns the tokens then sends them off to another chain.spender->spender->amount. Since an attacker now controlled both of these, they could allow themselves to spend their own value then spend the funds of other users arbitrarily. Yikes!random library from Python. Since this is known to NOT be cryptographically secure, this is a red flag. However, breaking this remotely is feasible in some scenarios but not all. So, now what?seed. So, if you know the seed then you can predict what numbers are going to be used going forward, regardless of the randomness of the algorithm. In a crazy turn of events, the author found a way to leak the seed of the randomness.delegateCall() to share storage.Initializer pattern.initializer() modifier for us. This will allow the initialize function to be called once and only once during the lifespan of the proxy. initializer() is implemented with the functionality, this will not automatically call the children constructors like a real constructor will do. So, we must do that manually. To prevent these from being called by ours, we can use the onlyInitializing() modifier. There is another function called reinitializer() that can be used to allow for initializations after the initial one.ParseCoinNormalized, which is part of the Coin implementation. Fuzzers can quickly find issues in stateless code like this but it becomes harder to find weird issues in the combined and stateful ecosystem.Rand. They found a few bugs, which is awesome. To me, you always hear I modified their fuzzer to do XYZ because different fuzzers find different bugs.