Resources

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!

SolChat Messages Insecure Encryption Method- 1357

h0wl    Reference →Posted 2 Years Ago
  • SolChat claimed to be an encrypted chat application and audio calls using WebRTC. So, the author decided to take a look at it.
  • They first took to reviewing the JavaScript code. Since the JS map files were easy to download they could deobfuscate it using sourcemapper. While doing this, they discovered that the encryption/decryption of messages was happening client side with a large coded key!
  • In particular, these were stored within process.env.REACT_APP_API_URL. Since the client side needs this information, it was exposing the secret information when used there. I'm guessing that the developer didn't understand the difference between frontend and backend.
  • So, they took some messages off of Solana and decrypted them on the spot. Talk about a horrible blunder! Even after making this only on the server side, it's still bad that a single entity has all of the keys for decrypting all messages.
  • I love that the author went and verified these claims. People who make bad claims about the security of something need to be exposed in order for the world to be more secure.

Learning by Breaking - A LayerZero Case Study - Part 3- 1356

Trust Security    Reference →Posted 2 Years Ago
  • In the first two posts they found two vulnerabilities that were already patched in LayerZero. This time, they go through a vulnerability in a different section of code.
  • When calling an external smart contract, you can specify the amount of gas for the call. When doing this, you can pass in 63/64 of the gas in the call. Why? Because the current smart contract also needs some gas to finish executing! If all the gas was actually passed, there's a chance that the current one would run out of gas. Even with the 63/64 out of gas rule, it's possible to get the 1/64 to be eaten up, which is what this bug will exploit.
  • LZ has two cross-chain token implementations: ONFT(ERC721) and OFT (ERC20). In the ONFT contract, is implements the specification correctly, including the onERC721Received() function. There is no gas limit on this external call, which means we can eat up a lot of gas.
  • In the _blockingLzRecieve() function, it will try to store the reason for the revert. However, if we use 63/64 from the other contract (which we can from the onERC721Received() entrypoint) then we will be able to force this to revert which not enough gas.
  • To freeze an ONFT, a user can bridge a low-value NFT then have a malicious contract there. To unfreeze it, the NFT owner can call forceResumeReceive() to remove the payload from the endpoint. Then, it can be resubmitted for 1.5M gas.
  • When disclosed to LZ on Immunefi, it was pointed out that all NFT/ONFT payouts were limited to the low range of 1K-10K compared to the 25K-250K range for a high impact normally. The easy way to fix this is to override _safeMint() to pass the remaining gas for the transfer iteration and not 63/64th o f the gas.
  • The small payout for this bug and the previous 2 bugs being dups really disincentivized further research. To make it worse, it seems that many of the bugs are not fixed within the core contracts, making it very difficult to test issues. On top of this, crosschain things are just difficult to test in general.
  • The series was an interesting perspective into LayerZero bug bounty program and DoS bugs in weird spots. To me, some of the design decisions, like force removing a bad TX, are interesting for limiting the actual damage on the blockchain. LZ seems to generally have good defense in depth and design features.
  • Besides this, it seems like LZ has some sketchy fixes and practices for bug bounty. From these readings, I personally wouldn't tackle their bug bounty program for the claimed $15M as it's A) very secure with good design and B) complicated to test with the fixes being all over the place and C) a tad sketch on payouts.

Learning by Breaking - A LayerZero Case Study - Part 2- 1355

Trust Security    Reference →Posted 2 Years Ago
  • In the first part, the author goes over how the EVM part of Layer Zero works. In this part, they go over some bugs that they found within the ecosystem.
  • Being able to shut down an individual cross chain message arbitrarily is a rather bad issue. Not only as a single user but since things must be done sequentially on LZ, being able to force a transaction to revert could block the whole setup.
  • On a crosschain swap, the developers consider the scenario where transaction reverts by handling it within a try/catch block. So, even if this reverts, it should be able to handle it. But, Trust has an interesting piece of insight on this!
  • If the call to a contract is made but address is NOT a contract that exists then the try/catch block of code will NOT handle it properly. Instead, the transaction will simply revert. This leads to a persistent DoS attack, since it will block transactions occurring afterwards.
  • The developers thought of this situation occurring. So, they have function that will remove a transaction from the queue when this happens. Sadly for Trust, this was fixed at a different layer of the stack that they had not seen and was found internally by the team.
  • Looking at the same code they wanted to force a revert in a different way. If the try/catch was correct so they turned to gas related attacks. When executing an external call, the user pays a fixed 175000 gas.
  • First, they tried a return data bomb attack. This is when the copying of the reason for the external call eats up a ton of gas. In this case, it wasn't possible because to eat up all of this gas in this case.
  • What they did realize was that the failure case was copying the payload into storage. Since every zero to non-zero storage costs 22.1K gas. The payload was capped to 10K at the LZ level for a total of 313 operations or 7M gas. To attack this, we would send a swap with a very large payload then have it fail. To unDoS this a relayer would need to pay 7M gas, which is a lot.
  • Once again, the development team had found the vulnerability internally then sent it to people doing an audit. To fix the vulnerability they added some middleware to the router call that protections against many of these gas spending edge cases. The check appears to be within some router code, which is extremely strange place to put it.
  • Overall, it was interesting reading about various gas denial of service techniques. It's a bummer that neither of these bugs panned out for Trust but I believe in part 3 they get something.

The Cosmos Security Handbook- 1354

Fault Tolerant    Reference →Posted 2 Years Ago
  • Several folks wrote about issues to look for in Cosmos-based blockchains. I have a personal list of these but it's nice to see a large external list! Cosmos is built via writing Go code at the infrastructure layer, as opposed to writing code in a virtual machine like the EVM. This creates many unique issues.
  • The first mentioned class is non-determinism. If different groups of validators see different proofs at the end of execution of a transaction then the blockchain will come to a halt. Randomness, Go maps (randomly determine where to start in a map), timestamps from the local machine, concurrency and floats by different platforms.
  • The second class are in protocol panics. Code executes within the BeginBlock/EndBlock that panics will lead to a chain halt. Hence, panics should be handled in a nice way. This can happen via bad math operations, bulk coin sends for blacklisted addresses and added in panics.
  • A third thing to watch out for is unmetered computation. Only stateful things have gas meters. For instance, reading/writing to state within a callable message. However, BeginBlock/EndBlock do not. So, if a user can setup code to end in these locations, it can be real bad. Additionally, bad loops on non-state operations can cause halts as well.
  • The next one is Key Malleability and Prefix Iteration. In Cosmos, all state is simply a key-value store. For storage, it's recommended to add a prefix to a data type so that you don't overwrite the wrong things or create collisions. Even checking for existence by a prefix needs to be done carefully. I remember thinking about these collisions myself but most developers are smart enough to add a large string to the beginning of the data to signify a datatype.
  • An iterator is created for going through a KVS space. When using an iterator, adding the prefix to the store is crucial. Otherwise, unintended data could be iterated over the top of. Iterators are inclusive of the first byte but exclusive of the end byte. So, this can create a bad iterators over the top of data.
  • Cosmos developers need to consider gas. The KVS does contain automatic gas charging for storage reads and writes but sometimes more needs to be done. However, adding more gas for specific operations can be necessary to prevent spam. Cosmos is known to have high levels of congestion and not having a good way of handling this during peak load. The gas stuff on Cosmos feels like a blackbox to me atm.
  • Overall, a good series of issues! They're going to do a CosmWasm and IBC post in the future, which will be good for everyone in the space. I look forward to seeing the post and adding to my list of test cases on Cosmos based blockchains.

zkSync Era Findings & Analysis Report- 1353

C4    Reference →Posted 2 Years Ago
  • zksync is a zero knowledge (ZK) project that was building out a ZK EVM. The contest had 1.1M in rewards. The winner Winnie had never touched ZK stuff before but decided to ramp up on it before the event. I purposely didn't do this because I thought that the competition would have too many specialist but I was wrong about that!
  • ZK logic uses arithmetic circuits which act as gates with logic similar to binary logic in traditional computers. The circuits must be implement this strictly otherwise there may be more than one correct answer, which is unacceptable for a EVM execution.
  • On the div operation the circuit wasn't constrained enough. There is a missing check on the result of the subtraction. This means that the division with a smart contract could be given an incorrect result, which is awful. The exact same missing constraint exists on shr as well.
  • The lack of constraints is a very common critical vulnerability within ZK systems. This was done with log emitting, AND, OR and out of bounds memory reads as well. For the AND and OR values, the constraint only must be less than 128. For XOR, there is an integer truncation bug that leads to the same affect.
  • The final high finding is a denial of service via an unprovable via an over constrained circuit. If a dividend is nonzero and the divisor is zero, the quotient and remainder are both zero. Interesting that BOTH the over and under constraints are bad.
  • There are also a plethora of medium severity and low findings. Many of these are wrong values or differing implementations between the EVM and the zEVM. These are worth reading but there are too many to make notes about.

More secure Facebook Canvas Part 2: More Account Takeovers- 1352

Youssef Sammouda    Reference →Posted 2 Years Ago
  • Youssef specializes in finding vulnerabilities in clientside JavaScript code. Specifically, with Facebook integrations. In this article, he goes through a chain of issues that led to an account takeover but looking at the authentication process of Facebook Games.
  • When trying to authenticate using OAuth in this iFrame, there is some postMessage communication that occurs. Once this happens, a request to Facebook is made that pops up on a dialog box and returns back authentication information for the requesting application.
  • Of course, doing this as some security risk. So, they try to validate the redirect_uri and requesting origin. When getting the response from the Facebook OAuth endpoint, the redirect URI was the location for the postMessage sending origin. So, if you specified instagram, it would try to send the information to the instagram domain in the postMessage.
  • If the origin is null or undefined, then the value of "k" is set. This setting gets set automatically to the URL of the game application (aka iframe URL). Additionally, there is a check to verify that the app ID matches the URL for the postMessage is being sent to.
  • Since Youssef spends so much time at Meta, he knows everything about the authentication process. He knows that all Facebook apps have fbconnect://success as a valid redirect_uri.
  • So, what's the attack here? There's a race condition on the checking of the k value for the origin. First, we send the request with fbconnect URL with the instagram app, which is valid. While the OAuth request is in flight we submit a new k value to replace the postMessage origin to send it. With this, we're able to get the OAuth code for any app!
  • This was fixed by checking that the app_id is the current one. While testing the fix they noticed that if you included a parameter with app_id[0]="" that this was interpreted as the parameter but could only clear it for some reason. While looking around they found a new parameter called encrypted_query_string which could contain the same information as before. The author found an encryption oracle via a server side redirect that encrypted the information for them.
  • So, using this, we submit a postMessage with the app_id and redirect_uri with the empty string to clear it. Next, we use the information provided by the encryption oracle with the malicious redirect_uri. Now, we have bypassed the protection once again. To fix this once and for all, an allowlist of JSON parameters was made for the postMessage call.
  • All of Youssef's post require a very deep understanding of the Meta ecosystem and rely on various small primitives to achieve a goal. Parameter pollution, encryption oracle... so many little things! I think this shows the power of specialization and note taking on little quirks discovered within the application to create huge impact. Youssef was on the Critical Thinking podcast recently and has some further good insights on this.

Every known way to get references to windows, in javascript- 1351

Daniel Brain    Reference →Posted 2 Years Ago
  • This article is a list of different ways to get window references. When doing client side security, getting a reference to a window is big way to cause havoc.
  • First, looking at the window. Using window.open() on both a new window and an existing window. Second, if you're inside of a popup window you can get a reference to the parent with window.opener(), even with a cross domain setup.
  • Next, we have iFrames! window.top can get the reference to the top level window when in an iFrame. window.frames shows all frames within a given window. Additionally, if it's named, then windows.frames['frameName'] can be used too.
  • A window object can be sent via a postMessage even in the cross domain case. If a window has the same domain as another window, you can reference globals on that window.
  • Most of these rules can be chained together as well. The author mentions that it's not always possible to get a reference to something. In particular, a cross-domain iframe or an iframe that opens a popup window.
  • Good article on a very esoteric JavaScript concept!

Learning by Breaking - A LayerZero Case Study - Part One- 1350

Trust Security    Reference →Posted 2 Years Ago
  • LayerZero is a very large blockchain bridge that holds a large amount of value, as well as many cross-chain applications made by other developers.
  • The functionality for calling is fairly simple on the smart contract side. On the sending side, call send() to the UltraLightNode (ULN) contract which emits an event. On the other chain, receive() is called from an entrypoint of LZ after a Merkle proof is done. Trust includes a nice callstack for this as well.
  • A user can pick a specific relayer while doing this. When getting a relayer to send over the message, you need to pay for gas as well.
  • In September of 2022, ULNv1 was deprecated. Why? Trust claims it was a silent fix of a vulnerability with no public information about it. If you look at a diff of the codebases, instead of just srcAddress it's using srcAddress,dstAddress for the PacketReceived event.
  • When calling validateTransactionProof() on a transaction the nonce used to be pulled from the mapping [srcChainId][srcAddress]. Now, it's [srcChainId][srcAddress][dstAddress]. Why is this a big deal?
  • The incoming transactions must come in order as well... 1,2,3... A client contract can pick their own relayer/oracle pair. Given this information, we have a slot collision. Another user can submit a transaction from the same client contract with a different relayer to the same destination and chain to spoof it. Since there is replay protection, this made the real message unreceivable. Boom!
  • $250K was paid to the first whitehat then samczsun reported it after but got a $50K good will bounty. To me, the slot collision is something I've never seen before and I'd be curious to see more of these. Thinking in depth about mapping and user controllable values seems like a good way to go.

The Risks of the #MonikerLink Bug in Microsoft Outlook and the Big Picture- 1349

Haifei Li - Checkpoint Research    Reference →Posted 2 Years Ago
  • Outlook is the most popular email client in the world with all of the other Microsoft Office Suite of products being equally important. Finding vulnerabilities in this can have devastating consequences.
  • Everyone knows what URLs are - http, https, etc.. However, there are many other URLs for other apps, such as Skype. The file:// URL can be used to reference local files on the system. By default, this is blocked from execution on Word when clicked on.
  • Some researchers found a weird bypass for this check. If the path has an exclamation point then some text afterwards then the check is bypassed. For example - file:///\\10.10.111.111\test\test.rtf!something. The exclamation point has some special meaning in Outlook that changes the meaning of the parsing from a file to a Moniker to try to find COM objects.
  • What's the impact of this? First, a request to a file at a remote location would leak the NTLM hash. Second, since this tries to parse the item as a COM object, it may be possible to escalate this to RCE. However, there is no real example of this and it feels like scare tactic.
  • A fairly simple mishap on the parsing of the URL. When deep knowledge of a system comes obvious bugs.

0Kage Diaries Chapter 1 — Enzyme Finance- 1348

0Kage    Reference →Posted 2 Years Ago
  • Enzyme Fiance is an on-chain asset management protocol. Users can create access rules, trading limits and other various rules. Within this, there are three main roles: asset manager, end users and the protocols being interacted with.
  • The manager handles the funds in order to create a secure yield generating platform, which the users will benefit from. However, who should have to pay for the gas on a rebalancing? To solve this problem, there is a gas station network relayer for handling exactly this case. This works by having the users pay the expected gas charges up front then executing the transaction via a network of relayers.
  • Within this architecture, there are a few different roles. First, the relay server is a web server that takes in a request for a transaction. The paymaster is the contract setup by the application using the GSN to pay for the gas prior to executing the call. The relayhub is the entrypoint for the relayer itself. Finally, the forwarder is the contract on the network for the GSN that is trusted to execute the call, where the application should implement a caller interface for this.
  • We have an anonymous user from the GSN network making calls to the contract on behalf of another user. So, access control is complicated to do with this setup. One job of the forwarder is to call the function preRelayedCall() to ensure that the recipient matches the forwarder in the relay request. When overriding the original implementation of this function from GSN, they removed this check.
  • For integration into the network, the relayer is trusted and must be allow listed by the network. Since the check was removed, an malicious forwarder can abuse this to profit from it. Remember, the forwarder is the entity actually performing the transaction and getting paid out in gas for their troubles. Since the forwarder isn't validated, an attacker can send a fake message then receive payment for it!
  • How much payment? According to the GSN docs, the gas information should be validated within the paymasters preRelayedCall() as well. But, there is no verification of the gas parameters for this. As a result, a malicious relayer can set very high gas values then get paid out a lot by the paymaster. To make matters worse, since the paymaster is replenished with 0.5ETH magically, this can be done on repeat to steal more and more ETH.
  • To fix this, there are two bugs that should be remediated. First, checking the forwarder address. Second, checking that the gas parameters are sane.
  • The author leaves us with a few takeaways. First, small features can still have big impacts. Second, more misconfigurations can lead to the amplification of impact, such as with the missing gas price check. Finally, design is key. GSN rewrote some of their code to do this verification themselves instead of putting the blame on the developers.
  • Overall, a very in depth read onto a GSN integration vulnerability. It's interesting how a small access control issue turned into the stealing of money from the protocol. Great find and write up!