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!

RocketPool and Lido Frontrunning Bugfix Review- 909

Immunefi    Reference →Posted 3 Years Ago
  • RocketPool and Lido are both third party staking pools for Ethereum 2.0. Proof of stake, vs. proof of work, is the future, as it should make the Ethereum much more performant. Instead of random people computing a hash, a staker will propose and validate blocks from other validators. A person is only a staker if they have deposited funds into the staker contract themselves.
  • Currently, to be a staker in Ethereum 2.0, it is 32ETH or around 112K. Because this is so expensive, staking pools were created. The purpose of this is to pool together user funds to become a staker as a group. Then, from staking, you get rewards for doing so.
  • When an Ethereum transaction is made, it isn't made instantaneously. Instead, it is stuck into a queue of our transactions. The validators will choose which transactions to put into the next block, depending on the amount paid to perform the transaction. So, what would happen if somebody abused their knowledge of the queue to do something malicious? This is called frontrunning.
  • The deposit contract is used for sending the money to be a validator. This takes in three main parameters: BLS public key for validator, withdrawal credentials and signature of the data above.
  • This is how a user signs up for the service. When value is added to deposit for a NEW user, the user is created within an IF statement. However, if the user, which is defined by the public key is already in the system, the funds are simply added to that validator.
  • An attacker can SEE the presence of a deposit using a specific public key. Once they see this, they can create their own transaction linking their account to this public key PRIOR to the original user. Once the original users transaction goes through, the funds will be added to the attacker account, because of the else clause, instead of being in the new users account. Damn, that's crazy!
  • Frontrunning appears to be more of a problem within the cryptocurrency bot community. However, it does have serious consequences in the security world as well. In this case, the identification of a user was NOT their address; instead, a value that could be spoofed. Interesting bug that the developers explain well here as well.
  • It should be noted that this required a node operator to go rogue to perform this attack; this is considered a privileged user nonetheless but still got a 100K bounty for each project this effected. The authors include a few possible mitigations as well.

PancakeSwap Logic Error Bugfix Review- 908

Immunefi    Reference →Posted 3 Years Ago
  • PancakeSwap is a platform for swapping tokens and many other functionality. In this blog post, the author goes into the lottery functionality. The vulnerable code persisted in several other projects, since they had forked PancakeSwap.
  • When claiming a ticket, an array of tickets can be used. The flow of code verifies that the ticket has not been claimed yet and adds the reward for each ticket in a variable. Once the verification has been done, the reward is sent to the user.
  • This becomes a problem when the tickets are NOT all unique. In particular, the verification step does NOT mark the ticket as used. So, the same ticket can be provided multiple times. None of the checks validated that this happened.
  • To pull this off, you would need to win a lottery ticket, which isn't very hard. Once you win the lottery, call multiClaim() with the same winning ticket up to 255 times. I think there is a hard limit on the amount of elements for dynamic arrays in Solidity.
  • Overall, a really simple bug that could have lost all user funds. The fix was to set the lottery ticket specified to have claimed the reward during the verification step.

Two Rights Might Make A Wrong- 907

samczsun    Reference →Posted 3 Years Ago
  • The author beings with a quick statement: "A common misconception in building software is that if every component in a system is individually verified to be safe, the system itself is also safe". This article is really into the mind of the blockchain hacker.
  • SushiSwap has a MISO platform (sidenote: if you want to be taken seriously, please don't use ridiculous names like this). This platform has two types of auctions: Dutch and Batch Auctions. The author quickly skimmed through all of the contracts to see nothing wrong obviously wrong with it.
  • While scrolling through all of the files, they found two libraries: SafeTransfer and BoringBatchable. The BoringBatchable library is added in order to easily introduce batch calls to any contract that imports it. This is done with a simple delegateCall flow.
  • In a previous time, the author of the post was on a call with the Opyn team about trying to protect user funds after a horrible attack. The contract allowed for the batching of multiple calls and would REUSE the msg.value in the loop. This was the same bug in a different form.
  • In the context of an auction... you could send 1ETH to the batch functionality. When calling this functionality, you could call commitEth for an auction once. Then again. And over and over for all auctions, while only ever spending the 1ETH.
  • Is there anything else we can do besides win auctions for free? Yes! We can call refund on repeat. By sending ETH that went over the auctions hard cap, the contract would simply refund the ETH. By doing this over and over again via the batch call, all of the funds from the contract could be drained.
  • The timeline of this is absolutely wild. From discovery to being on a call with the Sushi team, it was only 2 hours. No funds ended up being stolen.
  • Overall, the post is awesome and really puts you into the mind of a hacker. Sometimes, taking a few extra minutes to look at something can change the world. Welcome to the world of hackers!

MCDEX Insufficient Validation Bugfix Review- 906

Immunefi    Reference →Posted 3 Years Ago
  • MCDEX is a decentralized exchange and layer 2 platform that allows users to trade perpetual contracts.
  • When performing batch trades, a user can provide the liquidity pool contract. The liquidity pool is expected to do several validations on the data. However, since this contract is controlled by the attacker, this is a major problem.
  • Later, the Broker contracts reimburses gas expenses by transferring funds from a user's balance to a destination address. Since both of these can be chosen by an attacker, we can steal funds from any user! Simply put: we can set the pool contract to say this is a legit transaction from the pool. When, in reality, it is not.
  • All funds in the Broker contract can be stolen by specifying that the user with funds that should pay the gas fee and putting a large sun for this. Neat bug, where calling an outside contract ended up being a catastrophic failure.

Tidal Finance Logic Error Bugfix Review- 905

Immunefi    Reference →Posted 3 Years Ago
  • Tidal Finance is a discretionary mutual cover protocol that offers the DeFi community the ability to hedge against the failure of any DeFi protocol or asset. In normal person terms, this is insurance.
  • A user is able to stake or add assets to the pool. By providing assets to the pool, they are entitled to rewards from fees for using the service. The protocol needs to distribute these rewards evenly, depending on the amount of value contributed.
  • The payout for rewards has four steps: startPayout, setPayout, doPayout, finishPayout. The administrator of the contract can initiate a payout for Asset at some index. Then, the four payout functions are called in order. The payout is marked as finished.
  • A user has two main fields: rewardAmount and rewardDebt. The rewardAmount indicates the amount of money a user should be paid out. rewardDebt shows the amount taken out already. Since the pool had already finished, the rewardDebt for the asset is 0 (default value of Solidity).
  • When doing the payout, the math user.rewardAmount - user.rewardDebt is performed. But, user.rewardDebt starts with 0, giving the user more money than they are really entitled to. In particular, the unset variable of the user for a specific field is what is causing the problem here.
  • Many of the bugs in smart contracts are logic issues with the handling of money. It is really easy to have small/subtle mistakes with multi-step processes.

Belt Finance Logic Error Bugfix Review- 904

Immunefi    Reference →Posted 3 Years Ago
  • Belt Finance has a strategy token. This represents shares within the pool of assets. Each token is given out proportionally (pro rata) for assets put into the strategy contract. The strategy token is an interest baring asset as well.
  • To understand the bug, we need to understand the withdrawal flow of the contract. There are two main variables for keeping track of funds: balanceSnapshot and wantLockedInHere. wantLockedInHere is the balance of the contract not being put to work to generate yield on the assets. balanceSnapshot holds the balance of the contract.
  • When calling withdrawal, two paths can be hit. First, if the contract has enough funds in wantLockedInHere it will send this. Otherwise, it will liquidate the yield-generating asset and decrease the value of balanceSnapshot.
  • When making a withdrawal from the contract directly, instead of through a different contract, there is a double counting bug that occurs. In particular, both balanceSnapshot and wantLockedInHere will be subtracted from. Why does this happen when calling directly? There's an if statement that fails to actually NOT withdraw the money but update the state variables.
  • By making these variables very small, the contract has much MORE assets than it believes. Because the perceived value is so low, the attackers amount of shares appears to be much higher than it actually is. Now, when the attacks deposits money again, the contract will mint too many shares because of how low the balance appears to be. A attacker could call earn to get the real value of the contract. Practically, this means that this attack can be performed multiple times.
  • Finally, an attacker calls withdraw to claim all of the shares they have earned from the contract. They now have more money than what they started with.
  • Calling contracts in weird ways causes problems! A great bug find for a 1 million dollar payout.

The Alpha Homora DeFi Hack- 903

Rob Behnke    Reference →Posted 3 Years Ago
  • HomoraBankv2 allows for the usage of a custom smart contract for providing logic called a spell. The only check performed is that the loan is greater than the borrowed amount for custom contracts. When this was exploited, the bank was prepped for an upcoming release, with no UI and nothing publicly announced. This fact meant that an attacker can fully manipulate the market, since there is no liquidity inside the contract yet.
  • First, the attacker takes out a loan of 1,000e18 sUSD from the bank. When paying back the loan, the attacker should have to pay back 1000.000098548938710984 sUSD. However, a rounding error in the protocol only required them to pay back 1000.000098548938710983 sUSD. By doing this, the debt is now at 1 minisUSD and 1 debt share. Finally, by calling resolveReserve, the debt increases but the shares remains at 1. Because of the off by 1 error, the contract believes that NO money has been taken out!
  • By exploiting this bug over and over again, they are able to take out loans with only a single share. This bug only occurs when you are the only owner of shares in the contract. Neat! The pre-release aspect of this attack was quite important then.
  • After performing the attack above, they couple this with a flash loan attack to extract the sUSD from the contract to steal even more money. I don't understand this part of the attack though.
  • To me, there are few takeaways. First, a single off by 1 error resulted in a horrible security bug. Second, having this functionality public prior to launch seems strange as well. Finally, this contract was audited twice and still fell victim to attack. This is the second largest heist of an audited smart contract platform, only behind Wormhole.

The PancakeBunny Protocol Hack- 902

Rob Behnke    Reference →Posted 3 Years Ago
  • Pancake Bunny is a yield farming aggregator and optimizer for Binance Smart Chan (BSC) and Ethereum. The attacker took out a flash loan before doing this attack on BNB and Tether (USDT).
  • First, the author minted a large amount of Liquidity Provider (LP) tokens for the pool. The price of BUNNY (the LP token) is based upon the BNB compared to the amount of USDT in the pool. Another description of this attack with more detail is found here.
  • Then, they swapped a large number BNB into the pool for USDT. By swapping such a large number of tokens into the pool, the exchange rate was drastically modified. The pricing of tokens depends on the balance on the swapping. By taking a ton of them out, the pool becomes unbalanced. This makes the BNB token very expensive and USDT very cheap.
  • Here is the main issue: the BUNNY tokens that are minted is strictly based upon the amount of BNB compared to the amount of USDT in the pool. So, by exchanging their LP tokens, they claim more BUNNY tokens then they should be entitled to.
  • Finally, they repay all of the locations of the flash loan by swapping the BUNNY for something else on other exchanges. Wow, flash loans are crazy complicated... The simple remediation is to require multi-transaction operations. Additionally, using a pricing oracle, such as Chainlink, could have solve this problem as well.
  • The same company was hit by ANOTHER flash loan attack on their Polygon version of this. In this case, the flash loan allowed them to get a crazy performance fee to mint too much BUNNY (again).

The CREAM Finance Hack- 901

Rob Behnke    Reference →Posted 3 Years Ago
  • Cream is a decentralized lending protocol (just like a ton of things on the blockchain). In the Cryptocurrency space, an Automated Market Maker (AMM) is used for calculating the cost of an asset algorithmically instead of users calculating the price. This sounds great for usability. However, there are problems with this.
  • What happens when the user has control over an insane amount of funds? When the creation of flash loans, a user can a large amount of funds for a very short amount of time (one transaction to be exact). If the algorithm does not take this type of control into consideration, then a malicious actor can use this to steal a lot of money.
  • In the case of Cream, there is an issue in the price calculation of wrappable tokens. The price oracle for yUSD tokens calculates the value of the crYUSD value shares based upon the total supply of yUSDVault tokens. The cost yUSD balance / totalSupply yUSDVault.
  • The attacker redeemed $500 million yUSD from the vault. Then, when they added 8 million into the vault that they owned. By dropping the amount of tokens in the vault to nearly zero, this doubled the perceived value of the shares owned by the attacker because of the algorithm used above to calculate the funds; it is based upon the percentage of funds in the vault. By taking out flash loans from AAVE and MakerDAO, the attacker made 130 million off of this.
  • On twitter, Cream claims that if the attacker gives all of the money back they will pay a bounty cost of %10 the stolen funds. This seems ridiculous to me... here's an additional link from Immunefi that came out years later.

How the Mirror Protocol got Exploited- 900

BlockSec    Reference →Posted 3 Years Ago
  • Mirror Protocol allows users to take long or short positions on tech stocks. To bet on a stock, you must lock collateral for a minimum of 14 days. After the trade concludes, they can unlock the collateral to release the funds back to their wallet.
  • What keeps track of this betting? A simple contract generated ID number that is returned to the user. The code takes in a list of IDs to use when withdrawing the collateral.
  • The Mirror Protocol does not check the duplication of a position ID within this list. As a result, an attacker can unlock the collateral of a position over and over again. In the real attack, the position 43186 was duplicated 437 times.
  • I would bet there was code to remove the usage of a particular ID from being withdrawn after this. However, the issue appears to be in the passing of the list.
  • To fix the bug, a for loop checks to ensure that no duplicate IDs are included to be unlocked. Interesting! Even code written in Rust can have security issues.