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!

Ethereum Smart Contract Auditor's 2022 Rewind- 1063

Patrick Drotleff    Reference →Posted 3 Years Ago
  • Article about all of the smart contract vulnerabilities of the year. I'm going through the hacks and findings one by one, but still wanted this documented for later. Good detailed writeups and a good list of articles!

Gnosis CallAfterTransfer- 1062

Mudit Gupta    Reference →Posted 3 Years Ago
  • Gnosis Chain's native token xDAI contains the non-standard hook callAfterTransfer in their token. This surprised many protocols, leading to security issues down the road.
  • Hundred Finance is a fork of Compound. It does not implement the checks-effects-interactions pattern that is recommended to prevent reentrancy, even though it mentions it. Because of this and the hook, a reentrancy attack is possible.
  • First, an attacker deposited 2 million as collateral of one asset. Then, they borrowed assets based upon their collateral - 1.5 million. However, the borrow amount variable update for a user is after the transfer.
  • Since we have the hook in the transfer, we can reenter the smart contract without the users borrowed amount being updated. As a result, an attacker can enter the contract and borrow funds from a different asset. This allows them to borrow more funds than their collateral if this is repeated.
  • Agave is a fork of Aave. Although Aave tries to do the checks-effects-use pattern, one path was not secure against this. Why isn't Aave vulnerable to this then? Aave governance actively checks for reentrancy bugs prior to listing tokens on the mainnet.
  • Overall, a silly issue in a standard token. Defense in depth matters!

TrueUSD <--> Compound Vulnerability- 1061

ChainSecurity    Reference →Posted 3 Years Ago
  • TrueUSD is a stablecoin backed by the USD dollar. Recently, ChainSecurity did an audit of the Compound cToken contract, which uncovered a new vulnerability.
  • The concept of Double-Entry Point Tokens is having several contracts that both interact with the same balances. In the case of TrueUSD, this was the case because of a previous legacy version. Practically, the legacy contract just forwards any calls to the primary contract; altering the price in one should alter the price of the other. Works perfectly!
  • The Compound protocol has cTokens. These are the liquidity provider tokens from the protocol. Within Compound, a function called sweepToken was implemented. This is for rescuing funds that were accidentally sent to a contract managing a specific underlying token. This function can called by anyone but the tokens are sent to the admin so it shouldn't matter.
  • sweepToken has a sanity check to ensure that the address of the token we are trying to recover does NOT equal the underlying token of the cToken contract. This is because being able to send out arbitrary tokens that back the cToken contract would destroy the protocol.
  • Remember how there are TWO entry points with TrueUSD though? Since either of these addresses can be specified in the sweepToken call, we can bypass this sanity check with the legacy version address. This means that the entirety of the balance of the underlying token can be sent to the Admin. Why is this bad? Finance calculations!
  • The exchange rate of the cTUSD is as follows:
    (totalCash + totalBorrows - totalReserves) / totalSupply
    The variables above are:
    • totalCash:T otal amount of TUSD in the contract.
    • totalBorrows: Amount of TUSD currently borrowed from the contract.
    • totalReserves: Amount of TUSD that belongs to the protocol.
    • totalSupply: Amount of TUSD that has been minted.
  • With our attack, we can force the totalCash to become 0 for TUSD in the contract. Since we are drastically changing the price of the token by doing this, an attacker can profit from it! The author gives a few ideas on how to do this:
    • Liquidate users who provided TUSD as collateral for their loans.
    • Borrow TUSD, execute the attack, then pay back less TUSD for the loan because of the new low exchange rate.
    • Execute the attack to mint cTUSD. When the funds are returned to the contact, rdeem the cTUSD for a profit.
  • TUSD cannot be used as collateral so option 1 doesn't work. Option 2 would have yielded a 12% gain without any user interactions, giving a 3.1 million dollar profit. Option 3 would have worked, since the price of cTUSD would have increased once the funds had been sent back. However, this would have required user interaction, which sucks for an attacker.
  • This vulnerability was fixed by putting admin privileges on the sweepToken function. Additionally, TrueUSD removed the second entry point. According to the authors of the post, the better solution would have been a sanity check on the underlying balance of the contract before and after the transfer though. Overall, a very novel vulnerability that is something to look out in the future when interacting with a contract with multiple entry points.

Reproducing the $APE Airdrop Flash Loan Arbitrage/Exploit- 1060

Amber Group    Reference →Posted 3 Years Ago
  • An NFT airdrop is how freshly minted NFTs go into multiple wallets at once. ApeCoin was distributing a large amount of NFTs to Bored Ape Yacht Club (BAYC) and Mutant Ape Yacht Club (MAYC) NFT owners.
  • A Bored Ape owner had to invoke the claimTokens() function of the AirdropGraphsToken contract to claim their free ApeCoin. In order to verify if you deserved a token from the airdrop, the contract would validate the balanceOf() a particular user. Down the road, when the tokens were distributed, it was marked in a list who owned which ID.
  • Free giveaways are complicated to do in the Defi world. This is because there are many ways for users to get into the game momentarily just to get the free token. Normally, the eligibility of a Airdrop is NOT determined by balanceOf calls.
  • NFTX is a platform for creating liquid markets for illiquid Non-Fungible Tokens (NFTs). The vToken is how this is done. A user deposits their NFT into the vault and the protocol mints an ERC20 vToken in exchange. Since this platform is meant to increase the liquidness, it has aflash loan functionality for ERC721s. The function flashLoan allows a user to borrow an arbitrary amount of vToken, which is backed by the NFTs.
  • When taking out a flash loan to get vTokens, a user can burn these vTokens to get a specified NFT. Additionally, the burning has a fee of 1.04 as well.
  • Let's say that the BAYC NFTXVault has 10 apes as liquidity in it. An attacker can exploit this with the following steps:
    1. Flash loan to borrow the 10 BAYC vTokens.
    2. Burn the vToken to redeem the BAYC NFT from the vault.
    3. Claim the ApeCoins.
    4. Mint 10 BAYC vTokens from the newly AirDropped tokens.
    5. Pay about the 10 BAYC vTokens alongside the fee for performing the exchange.
  • After running through all of these steps, we have gained ApeCoins for doing nothing. That's a pretty neat exploit! The attacker walked away with 14.15 ETH and 60,564 APE for about 350K. Novel functionality in the NFTX vault made this exploit possible.

Bad things come in large packages: .pkg signature verification bypass on macOS- 1059

sector7    Reference →Posted 3 Years Ago
  • Code signing applications is an essential part of the macOS security model. Being able to bypass signing and verification steps would be a major flaw in the system, leading to users being at risk.
  • Installer packages are the files used to install the actual package, which are typically signed as well. The installer packages are simply xar files. A xar file contains three parts:
    1. Header: A header of the fields. Includes a hash and the size of the next section.
    2. Table of Contents (TOC): A zlib compressed XML document that lists each file in the archieve.
    3. Heap: Holds the information about the package, signing information and a few other things.
  • When viewing signed packages, two checks need to pass: calculated TOC hash is equal to the one stored on the heap and the signature + certificate belong to the TOC hash. Are the calculations of these the same? Not exactly! We have a C parsing bug!
  • The checksum value form the XML document is loaded in as a 64-bit integer. For obtaining the TOC hash for validating the signature, a 32 bit integer is used. This difference in the offset value means that we can point the signature check to a different location! For instance, 0x1 0000 0000 would resolve to 0x1 0000 0000 for 64 bit but 0x0 for 32 bit.
  • How do we actually do this?
    1. Take a legit and properly signed xar file.
    2. Change the checksum offset to a number larger than the 32-bit int max. Make the changes after this point.
    3. Write the TOC back to the file and compute the new TOC hash. This is the first check.
    4. Add padding to the heap until it's 32-bit max in sizes
    5. Place the new TOC hash at the heap offset of 32-bit max, leaving the original TOC hash at the heap offset.
  • Why is this useful? First, System Integrity protection can be bypassed using this bug. By signing an Apple signed package with high TCC permissions, it can be bypassed to read sensitive information or modify the kernel. Secondly, the Gatekeeper (check against known malware) verification is broken too. They also found that it was possible to escalate privileges in several popular applications as well.
  • The fix was to simply change the 32 bit int to a 64 bit int. A similar vulnerability was found in 2010 as well. Overall, a super fun post on the fun of parsing differences.

The Last Breath of Our Netgear RAX30 Bugs - A Tragic Tale before Pwn2Own Toronto 2022 - 1058

Vu Thi Lan & Nguyen Hoang Thach - Star Labs    Reference →Posted 3 Years Ago
  • The authors of this post come from Star Labs - a usual team at Pwn2Own. They detail several cool vulnerabilities that were patched a little bit before the event. They targeted a Netgear router, in this case.
  • From the LAN side, many services including upnp, hostapd, smb and others are exposed. From reverse engineering a binary in Ghidra, they discovered that the hostname field has a command injection vulnerability.
  • However, they are only given 63 bytes to work with. One thing I would have considered doing is constantly appending to a file until it is large enough. Then, executing the binary once we're done. In this case, they simply created a file. Sadly, this was fixed. The fix used execve instead of system. They did a good job fixing this issue.
  • While reviewing the device, they noticed a plethora of out bound connections to several netgear domains on the WAN interface. One of the domains was responsible for checking for firmware updates. While using the curl library to make these requests, two crucial settings are turned off: CURLOPT_SSL_VERIFYHOST and CURLOPT_SSL_VERIFYPEER.
  • Since the certificate verification is turned off, an attacker can setup a fake DHCP and DNS server to impersonate the update server. The firmware is likely signed... the point is that this opens up an entirely new attack avenue though!
  • When performing the firmware update, several requests are made to the update server. One of these is a URL for downloading files. This input is vulnerable to a command injection vulnerability though! This was also fixed by using execve instead of system though.
  • What's strange about this fix is that it is NOT complete. The root cause of the problem is the lack of certificate verification. As a result, the patch wasn't sufficient, allowing for a malicious firmware image to be sent to the device. The caveat is that the bug can only be exploited once per day (lolz).
  • Overall, a trivial list of bugs in the Netgear router. Interesting to see such bad bugs inside of such a popular product though.

Keeping the wolves out of wolfSSL- 1057

Max Ammann - Trail of Bits    Reference →Posted 3 Years Ago
  • wolfSSL is a TLS implementation written in C. The author of this post had previously written a protocol fuzzer called tlspuffin, which they decided to target wolfSSL with since several bugs had been discovered in it this year, it had a coverage metric to ensure the tool worked as expected.
  • The fuzzer is agnostic to any particular protocol, with SSH and other protocols supported. To fuzz wolfSSL, writing had to be created, since the tool is written in Rust. The goal of the fuzzer was to find strange states! The author claims that standard fuzzers wouldn't find this either.
  • The first bug is within the function AddSessionToCache, which is called when a client receives a new session ticket from the server. Whenever a new session ticket arrives, the client will reuse a previously stored cache entry. The ticket is 700 bytes, meaning it is given its own heap allocation with XMALLOC.
  • There is a strange logic bug here though. When a cache entry is used, this allocation leads to the initialization of cacheTicBuff again. But, as ticBuff is already initialized, cacheSession->ticketLenAlloc is 0. Practically, we have a confused state where a value doesn't get properly initialized. This improper initialization leads to a crash down the road because of an invalid free.
  • To find this vulnerability, the fuzzer created a very large session token. Additionally, since this is an issue with the cache, about 30 prior connections had to be made in order to discover this vulnerability.
  • The next vulnerability is a multi-step buffer overflow. By resuming a TLS session with two maliciously crafted hellos, the bug occurs when parsing cipher suites. The core issue of the bug is that the list of cipher suites has a maximum size, but this is not respected when copying data into the buffer.
  • Fuzzing for memory corruption vulnerabilities isn't new. What's hard though? Formally verifying if a protocol has logic flaws in it from the specification. The author decided to tackle this problem by using the Dolev-Yao model. Messages are modeled symbolically using a term algebra. The model allows for MitM attacks and other malicious scenarios as well.
  • The author introduces the concept of Dolev-Yao Traces in order to test out logical issues. By representing the protocol in the Dolev-Yao format, arbitrary execution flows can be emulated quickly. They give an example of a flaw in the Needham-Schroder protocol to demonstrate how this can find bugs.
  • The author didn't find any bugs with the changes this time but is hopeful for the future. One interesting feature is finding security violations that aren't memory corruption bugs such as authentication bypasses and protocol downgrades. Overall, good post on fuzzing and a different approach to it!

AsksV1.1 Deployed to Fix Vulnerability- 1056

0x7A6f    Reference →Posted 3 Years Ago
  • Zora has a module for allowing users to list any NFT for sale at a fixed price and currency. This is referred to as AsksV1 or Buy Now in the ecosystem.
  • A potential buyer is able to fill that listing by calling a method in the AsksV1. With this, Zora transfers the funds out of the buyers account and sends them to the seller for payment. In return, the NFT is transferred to the new owners account.
  • In Ethereum, a transaction is put into the mempool where a miner selects which transactions will be put into a given block. If a transaction will pay out more (higher gas), it is more likely to be selected. The key component is that this is NOT a queue that is FIFO.
  • Because of the transaction pool not being a queue that is FIFO, it creates an attack known as frontrunning - a type of race condition. This happens when something is in the mempool but somebody else can put another transaction at a higher gas price to do something malicious to you.
  • In the case of our NFT trading site, there is a frontrunning issue. To make the payment for the NFT, a user must first approve Zora to pull funds from the account. A common pattern is for an infinite amount of user tokens to be allowed by the contract. This allows for a user experience improvement and saves on gas costs.
  • What's the problem with this though?
    1. A seller places an NFT at a very discounted price.
    2. A buyer sees the good deal. They allow for Zora to spend an infinite amount of a users token.
    3. The buyer calls the functionality to purchase the token.
    4. The seller sees the purchase has been made in the mempool. As a result, they make a higher priority transaction to increase the price of the NFT.
    5. The buyer purchases the NFT for a bogus cost because of the infinite approval of spending on the token.
  • This vulnerability does require user interaction in order to pull off. However, the frontrunning attack is very slick and could have been used to wipe the users wallet clean of a particular coin. To mitigate this bug in the future, V3 now has a field to specify the cost you would like to pay for it. Overall, neat bug!

Sherlock Yield Strategy Bug Bounty Post-Mortem- 1055

Sherlock    Reference →Posted 3 Years Ago
  • Sherlock is a blockchain auditing platform in the form of contests. They had a staking pool setup with Euler.
  • The function balanceOf on the EulerStrategy.sol is used to determine the current value held by the EulerStrategy. This result is then used to calculate the price per share of the user's withdrawing position.
  • The function balanceOf calls EUSDC.balanceOfUnderlying() underneath it. The problem with this is that the actual value being returned may be manipulated. Although the contract believes this will be an atomic call (no other paths ran), this is NOT the case.
  • Specifically, the totalAssets is updated prior to the call but the totalBalances is NOT updated yet. This results in computeExchangeRate() being higher than the actual value. An attacker could use this to change the price and obtain more money than anticipated.
  • This attack is only possible because arbitrary calls from 1inch can be made while swapping. To manipulate the pool, an attacker needs to transfer a large amount of USDC to Euler, inflating the price of computeExchangeRate. Since the price per share is higher, an attacker can redeem more USDC than they should be able to.
  • In the proof of concept, the author has to setup the stage for the swaps and stake money. In Foundry, it is possible to fast forward the chain to a different time in order to allow this POC to be possible. Neat! Once they have performed the swap, they setup the callback on 1inch.
  • With the setup done, we can make the call. The callback is hit on the withdraw. Upon hitting the callback, USDC is transferred to Euler while in the callback of 1inch. This cross-protocol reentrancy creates a manipulated exchange rate. At this point, we exit the position, collect our winnings and repay the flash loan.
  • To fix this vulnerability, a reentrancy lock was added in Euler. This bug was missed by Trail of Bits for Sherlock but found during an audit of Euler. However, it was only noted as a medium severity finding and never fixed as a result. Overall, an interesting article for a crazy cross protocol reentrancy. The POC is very well explained as well.

Different parsers, different results- 1054

Nnez    Reference →Posted 3 Years Ago
  • Gearbox is a composable leverage protocol. It allows a user to take leverage on collateral asset and use the borrowed funds through CreditAccount across DeFi. A common functionality for every lending and borrowing platform is a simple health check. This helps check is a users account is solvent or not.
  • How do users get their funds? There are routers (adapters) for every protocol, such as Uniswap and Curve. There is a sanity check at the beginning of the router that parses the user input for what tokens to swap. On UniswapV3, this same information for the path is parsed as well. However, this parsing is done slightly differently from a byte array.
  • Gearbox takes the byte array and parses the first 20 bytes as token A. It then takes the FINAL 20 bytes and parses this as token B. For Uniswap, it returns the first 20 bytes for token A, the next 3 bytes as the fee and the next 20 bytes as Token B. The big difference here is the parsing in token B because of the little fee in there!
  • The author came up with the following payload:
    abi.encodePacked(WBTC, poolFee, WETH, DAI)
    
  • Token A will always be WBTC. However, for Gearbox, token B will be DAI and Uniswap will see DAI. This discrepancy means that the health check for Gearbox can be bypassed.
  • To launch this attack, the author performed the following steps:
    1. Deploy a fake token on Uniswap. Make the pool 1fake=1WETH=0.0000000000001WBTC
    2. Provide a small amount of liquidity for our fake token with both WBTC and WETH with a big exchange rate.
    3. Make a swap payload with our fake token and WBTC. The sanity check will be on the pool for fake token and WBTC, while the actual money will be taken from WETH. abi.encodePacked(WBTC, poolFee, fake, poolFee, WETH).
    4. This results in an attacker swap for the ratio of the fake token to the other tokens. For instance, 1WETH for 0.0000000000001WBTC.
    5. Hint their fake token and claim they lost a bunch of WETH from the pool.
  • Overall, a super interesting bug! The path variable is a commonly used pattern for swaps. So, this is something to watch out for and something I've looked for in the past.