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!

Understanding SushiSwap's MasterChef staking rewards - 1175

Mark Kop    Reference →Posted 2 Years Ago
  • Most staking contracts are simply a copy of the SushiSwap MasterChef contract. By understanding this, we will be able to understand most in-moment math handling and most staking contracts.
  • The original implementation that is shown uses a for loop for each of the stakers. Anytime an operation is performed, the rewards information is updated for each staking user. The following is used for the math:
    • blocksSinceLastReward: The amount of blocks since the reward information has been updated.
    • rewardTokensPerBlock: The amount of tokens that should be given per block.
    • stakerShare: The percentage of tokens of the whole staked. Since this is Solidity without floats, there is using fixed point arithmetic.
    • The algorithm for the rewards: blocksSinceLastReward * rewardTokensPerBlock * stakerShare
  • The problem with this implementation is the loop for every user. Altogether, the algorithm for this is defined below:
    (BlockARewards / TotalTokensBlockA) + 
    (BlockBRewards / TotalTokensBlockB) + ...
    (BlockNRewards / TotalTokensBlockN)
    
  • Instead of this, we can think about it differently: StakerAShareOnNtoM = StakerATokensOnNtoM / TotalTokensOnNtoM. This means that we're calculating the tokens we had over the timeframe of blocks and dividing this by the total tokens in this timeframe. This gives us the total shares of a user over a specific timeframe.
  • The trick to the Sushiswap algorithm is calculating RewardsPerShare instead of calculating the rewards per block for every user. This is because the RewardsPerShare is the same for every user. To reward a single user, we calculate the amount of shares for a given user.
  • At this point, we're keeping track of the rewards per share for each block but we ALSO need a way to subtract the previous rewards from a user. This is done by having a global AccumulatedRewardsPerShare that is stored for the last time a user performed some operation. Using accumulatedRewardsPerShare - previousAccumulatedRewardsPerShare allows us to calculate the proper amount for this.
  • Overall, a super interesting in-moment math handling to make the contract much more gas efficient. The article comes with numerical examples that make this easier to understand as well. I was surprised that the amount of tokens being added per block was static; I was expecting it to be dynamic to the some other value.

A smorgasbord of a bug chain: postMessage, JSONP, WAF bypass, DOM-based XSS, CORS, CSRF…- 1174

Julien Cretel    Reference →Posted 2 Years Ago
  • The author was hunting for a CSRF vulnerability when they noticed some odd functionality: a random subdomain was capable of performing authorized actions on the main API of the website. This was possible because of a cookie named sid with SameSite=None and Secure. This request required a valid CSRF token though.
  • From testing, the CORS policy was verbose, allowing all requests from subdomains of the main domain. What does this mean? XSS on any sub-domain from the website would lead to access to the sensitive API. They went after some out of scope subdomains on the site, which is a weird thing to do.
  • While on another site, they noticed a postMessage call without an origin check; this is a pretty obvious bad code sink. The functionality was calling arbitrary functions from app window, potentially leading to XSS. However, they couldn't find an easy eval() or something else simple.
  • This turned into a ROP-like challenge, where the user can call arbitrary functions within the application. The function APP.util.setCookie() allowed them to set arbitrary cookies on the various domains. Additionally, they found a powerful JSONP endpoint. The purpose was sending a call to a JSONP endpoint to lead an external script with the user choosing the domain.
  • This JSONP code that could load arbitrary JavaScript is real bad! Since the data could be reflected, by making a postMessage call to this, it would load inline JavaScript. How do we get a one-click interaction though? I don't believe that the postMessage call is accessible from other windows.
  • We need a reference to the page in order to send data via postMessage. But, the website had X-Frame-Options: SAMEORIGIN options, which prevented framing. As an attacker, this can still be opened in a pop-up window to get a reference, which is news to me!
  • Using the issues above, we can do the following:
    1. Get user to visit our page.
    2. Open a pop-up window that creates a reference to the page with the vulnerable functionality.
    3. Use the postMessage call to get XSS via the JSONP endpoint.
    4. Our attack payload is to ask for a CSRF token then make various API calls to modify and retrieve data.
  • Overall, a great bug chain leading to a horrible CSRF. To me, the poorly misconfigured CORS and lack of origin check on the postMessage were defense-in-depth issues that led to the exploitation of this; this is why these small things matter.

Enzyme Finance Missing Privilege Check Bugfix Review- 1173

Immunefi    Reference →Posted 2 Years Ago
  • Cosmos is an SDK for creating blockchains. Ethermint is a Cosmos blockchain that uses the Go EVM for executing smart contracts under the hood. It is the backbone of Cronos, Kava and Canto as well.
  • To run an EVM transaction, a user executes MsgEthereumTx. Prior to getting executed on the EVM, Ante Handlers are ran. These are functions that are run on each transaction and check the validity of a given transaction based upon the current context.
  • Ethermint uses its own Ante Handlers to provide functions like signature verification, gas handling and more within the ecosystem. When processing a transaction with the /ethermint.evm.v1.ExtensionOptionsEthereumTx option, it will go through a large list of decorators. But what if we use a different path? This allows us to execute the code that we want but skip the gas fees.
  • To fix this problem, Cronos added a check to block all non-ETH Ante Handlers from using this code path. This itself has a bypass though. The Ante Handlers assume that a transaction is being executed on messages. However, Cosmos provides a way to submit messages to the chain without directly embedding them into a transaction. Several of the modules support nested or embedded messages.
  • To fix this vulnerability, the Ethermint team added an additional Ant Handler that restricts the types of messages inside of an AuthZ message. However, if other modules allow for nested messages, it opens up the possibility for more bugs. This could have been used for a complete bypass of gas or a full on denial of service (DoS) attack.
  • An interesting bug and attack surface within the Cosmos ecosystem with a wonderful write up.

Election Fraud? Double Voting in Celer’s State Guardian Network- 1171

Felix Wilhelm    Reference →Posted 2 Years Ago
  • Celer is a blockchain protocol that enables cross-chain communication. It is built on top of Cosmos with the Stage Guardian Network (SVNv2). Validators of SGN are responsible for monitoring the on-chain contracts then forwarding them to the destination chains. The author points out that the on chain code is usually public while the internal infrastructure is not.
  • The bridge works by using send to one of the liquidity bridge contracts. This locks the tokens on that bridge then emits a Send event to describe the details of the transfer. This will be picked up by a SGN node to call MsgProposeUpdates to update the contract.
  • To prevent malicious updates, there is a voting mechanism in place. A node votes on the outcome of the proposal by sending MsgVoteUpdates, which consists of yes and no for active proposals. The sync module takes in votes and adds them to a structure. At the end of the block, the function iterates through the updates to ensure that all items passed with a 2/3 vote.
  • The EndBlocker function does not validate if somebody has voted multiple times. Using this, an attacking validator could vote multiple times. Yikes! Breaks the entire voting system. This results in directly lost funds, since they can spoof onchain events such as bridge transfers and much more.
  • Celer has several mechanisms in place to prevent complete stealing of all the funds. First, there is a delay for large values on the bridges. This Volume Control contract will give time for the developers to act. Secondly, the contracts are pausable, with automatic triggers to stop contracts. Overall, a fairly simple bug in complicated architecture.

The deposit Transaction lack of expiration timestamp check and slippage control- 1170

Sherlock    Reference →Posted 2 Years Ago
  • Two parameters are used in order to prevent validators from holding onto transactions until it's convenient for them to spend. First, there is slippage. This is used in order to prevent a frontrunner giving you a bad price. In particular, you choose the minimum amount of tokens out that you're okay with.
  • The second is the deadline. The transaction could wait in the mempool for a long time when trading is intense. Setting the maximum amount of time it waits in the transaction pool is important in order to prevent bad positions for users.
  • In this report, both of these parameters were messed up. The time was set with block.timestamp instead of setting a real timestamp. This vulnerability ended up being a bad interaction with Uniswap if the transaction is left in the pool for a super long time.

CS:GO: From Zero to 0-day - 1169

Neodyme    Reference →Posted 2 Years Ago
  • CS:GO is a popular online game from 2012 with many public servers. Mods, textures, 3D models and more all create a great attack surface. The source engine has it's own TCP-like network stack over UDP.
  • Reversing this game is interesting. Debug symbols were accidentally put into the MacOS version in 2017, making it easier to learn about. Additionally, there are many public exploits, cheating communities and more which offer good resources. They found a custom CS:GO server written in Python, which was useful for attacking.
  • The server takes in commands. There are some commands that only work locally and/or with a single user playing. Whether this is the case is controlled by the user controllable value m_nMaxClients. Using this, an attacker can execute privileged commands on the server, such as quit.
  • Source engine servers (what this game was built on) can send additional game files to the client via HTTP. When asking for a file, there is validation done on the end of the file on the type. However, when this is copied into a buffer, it's done by snprintf, which will truncate the string. So, we can bypass the filter!
  • One of the privileged commands con_logfile writes to an arbitrary *.log file. This suffered from the same snprintf truncation issue, but on a write. This gave them an arbitrary file write vulnerability.
  • The service validates DLLs. However, it falls back to an insecure mode so that it can boot if there's an issue. Additionally, this can be replicated with the -insecure flag. Using this flag, DLLs from outside the bin/ directory too.
  • There are several logic vulnerabilities here. How can we take this to code execution?
    1. Use the arbitrary file download to download a malicious DLL to be used later.
    2. Corrupt one of the DLLs. This will trigger the fallback mode to search for DLLs in a different location.
    3. Replace the gameinfo.txt so that the malicious DLL is loaded on start up. All Source Engine games are just add ons to Half Life so this is common.
    4. Use the privileged command bypass to reboot the server.
    5. On start up, the malicious DLL will be loaded because of the fallback mechanism.
  • Valve requires a real PoC in order to prove impact for RCE. Although they found these bugs individually and quite quickly, it took them a while to chain everything together. Each bug gave them 7.5K, which is quite a bit. Overall, good report on interesting logic bugs.

Curve LP Oracle Manipulation: Post Mortem- 1168

Chain Security    Reference →Posted 2 Years Ago
  • Curve is an automated market maker (AMM). User can trade token X against a liquidity pool for token Y. For performing these trades, there is a small fee that goes back to the liquidity providers.
  • Curve uses a liquidity provider (LP) token to represent a share of the liquidity pool. When a share is redeemed, users get their assets back proportional to their share to the total supply. The trade price is proportional to the tokens in the pool. For instance, if the trade ratio for DAI to ETH is 1848, then the pool would have 1848 DAI for every ETH.
  • Pools commonly have a mechanism for evaluating how much value an LP token has. This is commonly done by computing the underlying tokens for each share then dividing by the total supply (amountOfX/TotalSupply). Since these tokens are pegged to each other, this can be used to estimate the amount of total value of underlying tokens in the pool.
  • In Curve, the function get_virtual_price() implements an oracle for the conversion rate. In particular, dividing the underlying tokens by the total supply will give a good oracle. This is used to estimate fee growth or estimate the value of LP tokens.
  • What if we could manipulate the totalSupply or amountOfToken for a brief moment? If we did this, then the function get_virtual_price() used by other contracts could cause major problems. In this case, there is a read only reentrancy bug in this.
  • The remove_liquidity() function is used to remove liquidity from a contract. This happens in a few steps:
    1. Tokens are burned in burnFrom, which decreases the supply of LP tokens.
    2. Share of burned tokens is computed.
    3. The tokens are sent back based upon the shares provided.
    4. If the tokens are ETH, then a call is made to the fallback function of the contract.
  • During the execution of this code, there are 'N' tokens that run in a loop. If the callback function is hit, there is a major problem! The totalSupply has already been changed but the amount of each token has not been updated yet. By executing other code during the fallback, we can interact with the contact while it's in this state.
  • There is a reentrancy guard on most of the state-altering functions. However, there is NOT one on get_virtual_price(). Protocols that use this function can be manipulated with the following steps:
    1. Deposit a large amount of liquidity. This could even be done with a flash loan to cause crazy problems.
    2. Remove the liquidity
    3. Use the callback with the pool in a weird state.
    4. Profit.
  • Who was vulnerable? All of the pools with ETH could be exploited. Additionally, tokens with callbacks were also vulnerable to the same types of attacks. By calling this, it was possible to drastically change the price. Overall, a super interesting post on a well-known contract.

See this page fetch itself, byte by byte, over TLS- 1167

subtls    Reference →Posted 2 Years Ago
  • This website gets itself over TLS and comments on every single byte. It's a really neat visualization of the process.

I Hack, U-Boot- 1166

SynAcktiv    Reference →Posted 2 Years Ago
  • Das U-Boot - the universal boot loader, is a bootloader for embedded boards for ARM, MIPS and several other processors. This is typically installed early on in the boot process to initialize things components. It has support for an unreal amount of things, such as various drive types, a network and USB stack...
  • U-Boot has an interactive shell that is turned on by default. If enabled on embedded devices, attackers can modify or dump the firmware quite easily. The article contains many ways of doing this, such as using TFTP, over USB or even over serial.
  • Using TFTP, we can boot into a modified file system to backdoor the device. Pretty neat! They mention a tool called DepthCharge as well. This is a tool for jailing U-Boot with a lot of automated information dumping. If you're doing an audit of U-Boot, this appears to be the tool to use.
  • The age-old trick of modifying the init script to be set to /bin/bash did not work. So, instead, they choose to modify the flash memory chip with their own version. They dumped the code, used unsquashfs to unpack the file system, backdoored it, resquashed it and wrote it back.
  • The bootdelay variable is used to determine how long to wait prior to booting into the OS. If this is set to -1 or -2, this check is skipped entirely. Practically, this means that it's not trivial to get into U-Boot shell. To bypass this, a forced error in the reading of flash will drop you into a U-Boot shell - this is called Pin2Pwn. Can we stop this!? You can't.
  • The variables bootdelaykey and bootstopkey are passwords for stopping/delaying autoboot. If you don't know these passwords, then you can't go into the shell (even with a glitch like before). This is simply a plaintext password in an ENV variable. By either reading this from the NVRAM or brute forcing the password, it's still possible to break in. bootstopkeysha256 is similar but a sha256 hash.
  • This begs the question: how do we secure U-Boot?
    • Sign and authentication U-Boot with a secure chain of trust.
    • Disable the autoboot interrupt and the serial console.
    • Ensure that bootargs for Linux are not trivial to modify.
  • Overall, a very in-depth post! I didn't cover everything in my recap since some of it is already in my notes. But, will be a go to article for U-Boot going forward.