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!

Multichain Auditor - 1247

0xJuancito - OpenSense    Reference →Posted 2 Years Ago
  • Different blockchains have different settings and quirks that lead to various items. For instance, the average block time, timestamp and many others. This talk is inspired by a this Github repository about the same thing as well. Many of the chains have documentation on how it differs from Ethereum as well.
  • The average block on Ethereum is about 12 seconds since the proof of stake merge. On Polygon, this is 6 seconds.
  • block.number may not reflect proper timing on other chains. On Arbitrum, this represents the Layer 1 block number and is updated once per minute. This means it can jump from 1000 to 1004. In some cases, calculations can be wrong, especially when using this as a time reference.
  • An L2 sequencer is a method of the transactions going from L2 to L1. If the sequencer is down then updates from things like Chainlink will not work. For things like pricing oracles, it's important to ensure these are up-to-date. Otherwise, unfair liquidation or oracle manipulations could occur. An additional chainlink feature is checking the price feed address, as it may differ between other chains or may not exist entirely.
  • On Uniswap, the token order matters. Typically, this is sorted by address. On different chains, the addresses may vary, changing the ordering on the call. This is true of the ordering on Chainlink as well.
  • Modified opcodes. Some chains may have an older version of EVM, such as not having PUSH0 or difficulty being removed in the proof of stake update being changed. tx.origin and msg.sender on the L2 have slightly different meanings. precompiles can be different as well.
  • Modified opcodes. Some chains may have an older version of EVM, such as not having PUSH0 or difficulty being removed in the proof of stake update being changed. tx.origin and msg.sender on the L2 have slightly different meanings. precompiles can be different as well.
  • Frontrunning is weird on L2s. Optimism has a private mempool, making frontrunning infeasible. However, this may be decentralized in the future, resulting in the ability to frontrun.
  • Frontrunning is weird on L2s. Optimism has a private mempool, making frontrunning infeasible. However, this may be decentralized in the future, resulting in the ability to frontrun.
  • Various tokens are different. Decimals, such USDC and USDT are 6 on Ethereum but 18 on BSC. Harcode coded addresses of tokens, such as WETH. Contract interfaces may be different, such as USDT on Ethereum missing the return value but this isn't the case on Polygon.
  • From the reports being read, it's fairly common that projects only test on a single blockchain. Considering all of the issues mentioned above, simply running the code on a different chain or reviewing these issues will net you some findings on occasion.

Browsing for Bugs: Finding and Reporting a $3M Bug in Premia Finance- 1246

Ayaz Mammadov - Zellic     Reference →Posted 2 Years Ago
  • The author commonly browses through DeFiLlama looking for new things to audit. If it has a TVL of 5M and has a bug bounty program, they will take a quick look for some low hanging fruit.
  • The low hanging fruit are the focus of the article. In DeFi, it's common for the code of a popular project to be forked over and over again with small changes, such as Aave and Uniswap. Once you know how these codebases work, non-standard functions or modifications to the underlying protocol become great targets.
  • The function sendFrom() appeared to be custom. This was used for allowing a user to transfer funds on behalf of another user. Essentially, it was a transferFrom() for cross-chain calls.
  • The function took in a from address. However, this was NOT used for the verification; only the use. Instead, the allowance check was performed on the sender for the mapping twice! allowances[msg.sender][msg.sender] ended up being performed, after removing from abstraction. So, by allowing yourself, you can steal tokens from another user. Yikes!
  • The bug is obvious; but the efficientcy of it is interesting. If you already know and have audited the top 10 most common codebases, then you can make quick work on a fork. Overall, a simple bug that led to a large payout.

#1 Bug Bounty: How I made my first $10,000 on a not supported FoT token bug- 1245

Beirao    Reference →Posted 2 Years Ago
  • The author of this post decided to look into DFX after only looking into smart contract security for a month. This is a decentralized exchange specifically for stablecoin swaps.
  • Fee on Transfer (FoT) are a specific type of ERC20 token that is diminished as they are spent. This breaks the math in many protocols, making it a common (yet well known finding).
  • First, they reported that FoT tokens can break the protocol. This is because a fee is paid by the liquidity providers, gradually losing their investment in the trading pair. Over time, all of the FoT token will be gone.
  • The second finding is a little weird to me that is was accepted. The repo assumes that 1 USDC = 1 USD in fiat currency. Everything revolves around USDC being stable and normal. Even a previous audit from Trail of Bits mentioned this.
  • The author tried to find a way to break the protocol using the knowledge of USDC. Since USDC is upgradable, a change to it could break the entire protocol. In particular, from the previous finding, changing it to a FoT token really breaks it.
  • They tried reporting these bugs as critical severity issues, which is completely ludicrous. Instead, they put these two bugs are mediums and paid out 5K a piece. To me, this is crazy to payout for since both are extremely theoretial issues.
  • Personally, I'm a little offended at this getting paid out. I felt like they over reported the issues and tried reporting something that was theoretical in nature yet still got paid. Typically, I think about what an attacker can completely control. In this case, it was USDC updating the implementation or usage of an FoT token that caused an issue; something completely out of an attackers control.
  • They did include a PoC for each bug and an explanation of the impact. So, I do believe this helps a bunch. Should I change my perspective on what is reportable? In my mind, I should probably think about actors besides myself in the game. Additionally, potential usability, such as FoT tokens in this case, can be useful for saving the protocol from a hack prior to it occurring.

4 Strategies for picking the perfect bounty hunting targets- 1244

Joran Honig     Reference →Posted 2 Years Ago
  • Bug bounty sounds great! How do you pick a target? Since this is code you're going to be looking at for vulnerabilities and attempting to profit off of, you better make a good decision on this.
  • First, pick something that interests you. If you're spending a spare time looking for vulnerabilities in a complicated codebase, you better enjoy looking into it.
  • Second, pick something at your skill level. Looking at something that's too complicated will lead to frustration and lack of motivation. Something too easy will make you bored while looking for bugs. So, something just barely outside of comfort zone is perfect.
  • Third, something that is feasible to look at in the time frame you have. Looking at something crazily complex for a single day will likely result in failure. Do something where the complexity fits your timeframe.
  • Fourth, the desired income. If you're trying to make a job out of this, looking at programs with higher bug bounty amounts is important. View programs that only have larger payouts.
  • Especially with companies with larger payouts, watch out for scams. Many projects will immediately downgrade critical bugs to lows or out of scope. How do we find these scammers?
    • Payout well for medium and lows. If they only payout for critical, they may downgrade to a lower severity to avoid paying out.
    • Public exposure. Many programs will have information about money paid, response time and other features. Ensure that this is done well.
    • Submit quickly. If you find a medium or low that is adequently handled, they will likely pay out properly for more impactful bugs. If they squash your bug, you should just move on to something else.
  • Now, the most important part: fewer eyes. Bug bounty rewards people who are first or with niche knowledge. So, keep an eye out for projects with new bounty programs or codebases without an audit. Additionally, things that are extremely complex or niche are likely to have people really understanding what's going on with it.
  • Overall, a good article on how to pick a good project to hack on. I particularly enjoyed the advice on figuring out if the program is a scam or not.

CVE-2023-4039: GCC's -fstack-protector fails to guard dynamic stack allocations on ARM64- 1243

Tom Hebb - Red Team X    Reference →Posted 2 Years Ago
  • Stacking smashing protections, also known as stack canaries, is a memory corruption protection put on the stack. This is done by putting a special variable on the stack called the canary, that is random per process. If this value is different within the stack frame than the saved value when calling ret, then the program crashes.
  • Compilers can implement this via added code; no extra architecture-level things are required. Stack canaries protect against contiguous buffer overflows on the stack very well. To defeat this, an indexed write primitive or a stack canary leak must be found.
  • In AArch64, the protection does not detect/defend against overflows of dynamically sized variables like variable-length arrays (VLAs) using alloca() or user controlled increases of the stack frame in other ways.
  • Why does this only happen in AArch64? GCC backend lays out the stack frame differently than on other architectures. Instead of saving the return address (LR register) at the top of the stack frame (highest address), it saves it near the bottom of the frame. This allows for local variables to not be a problem when overwriting vars. Hurray!
  • This feels like a feature. If there is no LR to modify then who needs a canary? In practice, there's always another stack frame below with one. So, with a larger overflow, we can modify the LR register and other items on the stack.
  • The picture for the stack frame in the GCC source code doesn't have a stack canary. Why is this? The stack canary is treated as a local variable that it adds manually to the frame at compile time. In the compiler, there is an assumption that locals will occupy a single space but this assumption doesn't hold true for AArch64.
  • Dynamic allocations live at the very bottom of the stack frame, below the saved registers. This means that there is no intervening guard in this stack frame setup.
  • The ending contains a super simple PoC that causes a crash but doesn't catch the overflow. Overall, a really strange yet impactful bug in the GCC compiler. An assumption was made about how all stack frames were generated for the canary, which broke this codes effectiveness. Neat!

Aave v3 bug bounty part 3 — `LTV-0` `AToken` poison attack! - 1242

StErMi    Reference →Posted 2 Years Ago
  • Aave is a very common loan protocol in the web3 space. This works by depositing your collateral to the system, which allows you to take out loans from any token in the eco-system. A user can withdraw or deposit assets. The method of keeping track of this is Atokens, which act similar to collateral tokens (cTokens) on other protocols.
  • Denial of services (DoS) are particularly nasty in the blockchain space because contracts are commonly built to be not-updatable. So, being able to lock assets in the contract would be awful. Forcing reverting or crashes is a common way to do this.
  • When the user owns at least an AToken with a LTV of 0 and this AToken is used as collateral, restrictions aer put onto the types of interactions that can be performed. Withdraw, transfer and a few others do not work. I don't understand why this is the case but that's how this functions.
  • When an AToken is received by a user without any balance on a transfer, it is automatically added as collateral. However, this conflicts with some of the checks mentioned above. We can transfer over a poisoned token that will cause reverts on most functions because of the account having value it shouldn't have.
  • It is possible to escape this with specific set of actions. However, if this is an EOA, then these actions would have to be done in sequence, giving a bot the opportunity to send over more funds to cause a crash. Neat!
  • To fix this, the token shouldn't be added as collateral right away if the received AToken has a LTV of 0. Compared to web2, there are so many ways to interact with a system to cause problems; from sending unexpected tokens to reentrancy, these bugs are wild. This concludes 3 bugs in Aave that required very obscure scenarios in order to exploit.

Squashing a Pesky Bug in UniswapX- 1241

Kebabsec    Reference →Posted 2 Years Ago
  • Uniswap X is a signature-based order book protocol. This allows for order fillers to perform arbitrary execution during the fulfillment, as long as the result of the execution is the order being filled properly. This allows a filler to get the best price from all of the various protocols, not just Uniswap, to fill the order.
  • The author of this post had recently written a similar protocol called FloodPlain. In this protocol, the author wrote a bug that was caught by Spearbit. So, he wondered if the issue existed within the Uniswap X protocol. And, it turns out that it did!
  • UniswapX will do a balance check before and after on the user to ensure that the user got the proper amount of tokens. However, this has a consequence: what if the user has another order in another protocol going on at the same time for the same token? If this happens, then it's possible to trick the accounting of the system.
  • For instance, say a user made an order for 70 USDC on OpenSea and Uniswap at the same time. While taking the order from Uniswap (via the callback), the other platform could be resolved. Now, the user has 70K USDC in the account. So, upon entering back into the function, Uniswap believes that the user fairly got their funds. However, this came from ANOTHER order. So, the attacker gets a large amount of money in profit from it.
  • The author got 200K USDC reward for this vulnerability; I appreciate how seriously Uniswap takes these bugs! Although this situation was possible, it wasn't something that could be trivially triggered. Additionally, I enjoyed the outlook on this from a developer perspective. A video going into the details can be found at here.

Aave v3 bug bounty part 2: Aave liquidation process uses the wrong value... - 1240

StErMi    Reference →Posted 2 Years Ago
  • Aave is a very common loan protocol in the web3 space. Liquidation is the process of getting back the tokens of a loan in exchange for a discounted rate on the users collateral.
  • For instance, imagine I took out a loan for 1 ETH for 2000 USDC with a threshold of 10% (USDC to ETH is probably around 1800). If I wasn't repaying my interest or the cost of USDC dropped,another user could come in to supply the 1ETH and get 1850 USDC back for this. This keeps the protocol solvant.
  • e-mode (efficiency mode) is an asset that is consistent in price with a pegged value, like USDC. When getting the price of user assets, the program will check to see if the price should be gotten from the default location or the e-mode location. This is done by passing in the collateralPriceSource and debtPriceSource variables.
  • The assumption is that the debtPriceSource should be the price source for the e-mode category. This is imposed by the restriction that if a user is in e-mode, they should only be able to borrow assets that belong to this users e-mode. Can this be violated?
  • If the following situation occurs, this can be violated:
    • E-mode is using a custom oracle.
    • The debt asset that is liquidated has been removed from e-mode.
  • What's the consequence of this? The calculations get messed up on the liquidation process. Because the user being in an e-mode state is checked while adding the tokens, the logic explained above takes a weird turn. It will get the wrong price as a result. The e-mode price returns a nice pegged value, which is much cheaper than using an oracle like chainlink.
  • If USDC was depegged from $1 to $2, then the liquidator would only get half of what they deserve. If it depegged from $1 to $0.5, they would get double they deserve. This is a pretty drastic consequence, especially with large loans.
  • To fix this issue, the e-mode price should only be used if the e-mode flag on both the user and the oracle is set. This felt like a reasonable optimization but the complicated system led to this issue.
  • To me, a single thing stands out: this is a completely crazy situation that the author of this conjured up. However, this is a legit scenario that could happen with an admin acting in a sane manner. Did they see the separation between the user e-mode flag and the oracle e-mode flag and come up with this?

Aave v3 bug bounty part 1: Security concerns and improvements about the `executeFlashLoan` function- 1239

StErMi    Reference →Posted 2 Years Ago
  • Aave is a very common laon protocol in the web3 space. A flashloan is a loan that occurs within a single transaction. By doing this, a user can get access to a large amount of money without the owner risking anything. Usually, there is a fee associated with providing the flash loan.
  • On Aave, this fee is called a premium. The user asks for a specific amount, then a percentage is added on top of this for what they are required to pay back. Aave calls a callback function in the users smart contract for this.
  • Once a user goes to repay the flashloan, the function validates that the original funds were paid back. On the premium, it checks to see if this was paid back OR opens a position on the amount that needs to be repaid.
  • When specifying the position that needs to be taken, there is a miscalculation on the approval price. Some of the funds will not be sent in this case, resulting in leftover funds that Aave could spend. If a hack occurred of Aave, this would be a bad avenue for exploitation. Additionally, this logs a wrong event.
  • USDT does not follow the ERC20 standard for both returning a bool on successful execution or approvals with a value greater than zero. Both of these can lead to reverts, sadly. This issue is present in the standard template for flashloans on Aave, which makes it more interesting of an issue.
  • Overall, this is a defense-in-depth issue that was fairly complicated to encounter and understand. Not a very big problem in Aave.

AMM MEV BACKRUNNING - 1238

@NBFinanceTech - Open Sense    Reference →Posted 2 Years Ago
  • Different types of bot actions with frontrunning/backrunning with various markets:
    • Sandwiching: Increase the price of the asset, let the trade happen, then trade back to make a profit.
    • Sniping: Buy coins or assets as soon as they are listed.
    • Backrunning: Arbitrage price inefficienies within an AMM. Usually the result of large trades.
    • Just in time liquidity: Provide assets for a very short period of time for a single trade. Then, remove the assets after the trade to get the fees for providing liquidity. Only valuable for large trades.
  • This talk is about creating a backrunning bot that are mathematically optimal, which was prior to flashbots. The goal is to arbitrage (go from market A to market B to make a profit from the spread of the price) the differences between various pools. The author treats this as a graph theory problem where each token is a node and each AMM is a line to a different node. Using this, we can find a list of tokens to AMMs that could make a profit.
  • The first step is getting all of the data. These are pairs with tokens that have value (not meme coins) and the ratio (price) of these in the pool. From there, the author does an initial pass of these tokens from WETH to another token to see if the value went up or down in a breadth-first search pattern to see if the trading situation is good. If not, these tokens/pools are simply dropped.
  • Next, a depth first search is performed to find the best cycle possible. With the removal of meme coins and bad pools, this makes the search space tenable. To find the most optimal amount to trade on Uniswap is fairly easy. With Balancer, this is a much more complicated problem with the amount of tokens at place. So, this required using Newton's Method to find the profitability of various operations.
  • To execute this, the transaction is put on the network; but the work isn't done. Now, we need to check to see if there's any competition. If there's a collision in the tx.data, we need to see if our transaction will go through first or not. If it won't then will "fold" our previous transaction sent to minimize the loss, which is done by checking profitability at run time. We could also raise our gas price to see to try to get the transaction executed first.
  • Folds are important because only a single person can win the arbitrage. Additionally, there is some game theory here. The goal isn't to win a single opportunity; it's to win the most over time. So, sometimes, losing money by raising is more optimal than letting your opponent win. Over time, if you have a deeper pocket, you'll knock competitors out of the market. Obfuscating the code that is running to prevent people from doing this as well.
  • To make this more efficient, there are many things that you can do. First, caching is crucial. The paths can be cached and only updated on changes. This also goes for network requests for the pair information as well.
  • How is slippage dealt with on these calls? I'm not 100% sure. However, I've got some thoughts. Putting a transaction in the mempool will result in one of the pools being arbitraged or changed from a simple trade. If we can use the right amount of gas to ensure we're at the beginning of a block, this doesn't matter though. Additionally, there is a time of check vs. time of use (TOCTOU) issue, since the blockchain is always being updated. From my understanding, there's a gap (since transactions happen in blocks) so this isn't as much of a problem though.
  • At the end, the speaker gives advice on how to get into this: find a niche. JaredFromSubway will kill you if you try to start in his territory. So, finding a weird protocol on some sidechain with little competition works well for starting off. Then, you can work your way up to bigger and bigger things as you learn and build out infrastructure.
  • Overall, a good trip into the dark forest of MEV. It's fascinating seeing the optimization that goes into all of this.