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 Contract Vulnerability Post Mortem- 869

Multichain (AnySwap)    Reference →Posted 3 Years Ago
  • Multichain, formerly known as AnySwap, is an infrastructure for a multi-blockchain ecosystem. As with cross-chain protocols, it is simply a bridge that allows the transferring of tokens from one chain to another by creating their own token on each platform.
  • A white hat hacker reported a vulnerability in the platform. The issue was immediately fixed but the users needed to revoke permissions in order to completely fix the problem.
  • What was the issue? AnyswapERC20 and AnyswapRouter assume that the function permit is implemented for all token contracts. This function is used to make the gasless approval of token transfers. Since some of these contracts had fallback functions, that was triggered instead of the permit code.
  • Since permit checks to see if a user is able to send the funds, the complete removal of this code bypasses a very important security check. Hence, an attacker can steal all funds being used from permits. This is possible if two situations are met:
    • Permit is NOT implemented.
    • There is a generous fallback function on the token.
  • Why only permits though? The check that gets broken is validating the permit or from of the code. As a result, if there are ANY permits about an address, an attacker is able to steal these funds. To launch the attack, they need to trade a token that meets the criteria above. When they attempt to run this code for transferring funds from another user, there is no limitation on the amount of funds sent.
  • To me, it is interesting to note that the logic for a smart contract reverting is from the Solidity compiler itself. If a function exists, it is sane to think that the code wouldn't run - but the fallback breaks this assumption. If you try to call code at an address that has no code, this will run STOP (which is a success) instead of REVERT as well.
  • This researcher was paid out 2 million dollars (1M to each contract with this bug). The niceness of the smart contracts is fascinating to me; why wouldn't a function that cannot be resolved just revert? Hmmm, such as weird eco-system. An additional exploit link is here.

Qubit Finance Hack- 868

CertiK    Reference →Posted 3 Years Ago
  • Qubit Finance is a money market platform for lenders and borrowers. This functionality includes a Ethereum - BSC bridge (blockchain connector). In the case of Qubit Finance’s bridge, you deposit your ERC-20 tokens to the bridge and receive BEP-20 tokens in return.
  • The function safeTransferFrom is used to send tokens from the depositor and into the contract. When doing this, the user needs to provide an ERC20 token address.
  • When passing in this address, the contract attempts to use transferFrom to remove funds of the user to put into the contract. Then, on the other blockchain, the user gets newly minted tokens. However, if no code was at this address, the transfer of funds would never occur but the minting would still take place.
  • When no code exists at an address, the call return successfully. This is a weird quirk of this language! The attacker entered in 0 as the address. By using the attack over and over again, an infinite amount of qXETH could be minted. This drained an estimated $80 million from the protocol
  • This was a simple case of bad error handling and bad input validation. The usage of a custom ERC20 library, instead of the OpenZepplin, caused them issues as well. It is very strange how the address returns a success when there is no code there. An additional link can be found at here

The Wormhole Hack- 867

Certik    Reference →Posted 3 Years Ago
  • Solana Wormhole is a communication enabling the transfer of tokenzied assets from different blockchains. Wormhole has a set of guardians that sign off on transfers between the different chains.
  • A bridge works by having two contracts: one on each chain. In this case, there was a contract on Solana and another on Ethereum. What was the attack? Simply put: the attacker submitted transaction showed that it contained valid signatures from the guardians. They created 120K ETH out of thin air!
  • In order to do things cross chain, the guardians must sign off on it. Or do they? Four functions down in the call stack, a change to use load_instruction_at_checked instead of load_instruction_at was made. One of them makes the assumption that the verification has been done while the other does not.
  • Once the attacker realized the mistake in the code (removed the signature check), they provided their own smart contact to be used for verification. At this point, it was trivial to mint their own tokens.
  • The validation was passed down several times. This is an excellent case where a unit test or integration test for security based issues would have helped a ton.

The Meter.io Hack- 866

Rob Behnke    Reference →Posted 3 Years Ago
  • Meter.io is cross-chain bridge. A Wrapped token is a token that is tied to the value of another currency, such as the USD. In this case, it was wrapped ETH vs. unwrapped (regular) ETH handling.
  • For wrapped tokens, there are assumptions being made. For instance, wrapped tokens should not be able to burn (remove coins from circulation) or locked while wrapped.
  • A user could make a deposit to the contract with two different functions: depositEth and the ETH20 deposit function. One of the functions validates that the callData and msg.value are the same. However, the other does not make this check in a require statement.
  • Why is this bad? An attacker can specify how much money to deposit in the callData without sending over any money. Then, they can withdraw this money, draining the contract of funds.
  • This was an open source project that got forked into this. Unknown code and assumptions can lead to major security flaws, when not handled correctly.

The Paraluni Hack- 865

Rob Behnke    Reference →Posted 3 Years Ago
  • Paraluni is a financial project in the metaverse. The function depositByAddLiquidity contains a reentrancy vulnerability in it.
  • When calling depositByAddLiquidity, an internal call is made that transfers the caller deposit into a new pool. However, the pool contract can be controlled by an attacker. Once the flow is sent to the attacker contract, we can reenter this contract again.
  • The funds have already been transferred to the other pool put the internal state has not been updated. As a result, an additional call to this contract to deposit can allow for moving the money TWICE. This can be repeated indefinitely to drain the contract of money. A twitter thread showing the code is at here from PeckShield.
  • Typically these reentrancy bugs are only within the fallback functions. So, it was cool to see this vulnerability happen in a place that is impossible to fully fix in Solidity. Cool bug!
  • Another write up, from Slowmist, has good analysis on it as well. The main new insight is that the incoming array of tokens was not validated to against the pool. This resulted in the controlling of our own ERC20 token.

Fantasm Finance Post Mortem- 864

Fantasm Finance    Reference →Posted 3 Years Ago
  • The Fantasm Fianance Protocol had its own coin (just like everything else!). The code for minting (creating) these tokens had a brutal flaw in it.
  • In a require statement, there is a validation that the user deposits enough other tokens in order to mint the new token. However, we are dealing with FSM, FTM and ETH are input, all at the same time. These require statements must be on point in order for this to work.
  • The code compares ONLY the value of msg.value (ETH) and not the minimum amount of FTM tokens. As a result, an attacker could ONLY send ETH and FSM tokens but send NO FSM tokens. This error allowed an attacker to mint XFTM without depositing any FTM.
  • In this case, _minFtmIn variable contains ETH instead of FTM token minimum amount. Since this already passed, it was a major problem. The code is shown below:
    require(_minFtmIn < ftmIn, "Pool::mint: Not enough FTM input");
    
  • As a result, an attacker could mint XFTM without ever entering in any FTM. So, here is how they stole 2 million dollars:
    1. Mint XFTM token without entering in FTM tokens.
    2. Collect XFTM token.
    3. Sell XFTM token to FTM. Remember, we created these out of thin air.
    4. Do this process over and over again with more and more money.
  • Overall, writing concise code for each edge case is hard. In this case, the check is there but not working properly. This would have passed a code review but failed dynamic testing. An additional link for this is at Halborn.

Cashio Hack- 863

Rob Behnke    Reference →Posted 3 Years Ago
  • Cashio is a stable coin (CASH) that is linked to USB. This runs on the Solana blockchain.
  • To mint new tokens, a user needs to deposit collateral for the token. The collateral can only be deposited if the account goes through a series of checks first.
  • An attacker could create a new account with worthless collateral tokens. Because of a lack of checks on the account and collateral, this allowed for infinite tokens to be minted.
  • Damn, this seems quite obvious to me. If you are going to take in collateral, it better be legitimate. Otherwise, you are going to be robbed blind. No wonder banks want so much information on you!

The Beanstalk Hack- 862

Rob Behnke    Reference →Posted 3 Years Ago
  • Beanstalk uses a decentralized governance protocol. The smart contract contains a 2/3 vote emergancyCommit function that happens in 24 hours.
  • Voting in the Beanstalk protocol is determine based upon the donations to the contract itself. Right before the time for the vote was about to happen, they performed a flash loan or took out an insane loan for a short period of time.
  • This flash loan gave them 79% control of the protocols votes. With this power, they could approve their proposal with the emergancyCommit(). Since the proposal was just sending the money to the Ukraine and the attacker, the money was gone. At this point, the attacker used the money to pay back the Flash loan.
  • Flash loans are part of the eco-system and must be accounted for. Otherwise, attackers will steal all of the money then pay back their flash loan.
  • This contract was audited by a security firm, shockingly. However, they were not allowed to audit the governance functionality of Beanstalk.

Predicting Random Numbers in Ethereum Smart Contracts- 861

Arseniy Reutov    Reference →Posted 3 Years Ago
  • Randomness on the blockchain is a known problem. Regardless, people are still trying to make random functions for themselves, only to fail. This article dives into HOW this is done.
  • Many of them try to use block variables in order to provide randomness. block.coinbase is the miner of the block, block.number is the height of the current block and so on. An attacker can manipulate these if they have enough resources.
  • Or, even worse, they could write a smart contract that executes code within the same block used in the PRNG contract. Then, they have all of this information and have completely broken the randomness. The author shows examples using SHA3 of timestamp, block.number and a combination of many others.
  • blockhash is the verification hash of the block being mined. Why is this bad? blockhash.number will always yield 0, since the data is not known until after execution. blockhash.number - 1. For this one, an attacker can execute code within the same block of the smart contract.
  • Wait, there is more! block.blockhash() seems great. However, Ethereum only keeps track of the most recent 256 block hashes. After this, 0 is return. As a result, the random number could be predicted, if the new code was executed 256 blocks later. This happened within a SmartBillions lottery.
  • Want to use private modifier for a seed? That will not work either! Although this is private to the smart contracts, it is trivial to get this information off-chain then make the transaction with this information inside of it.
  • In Ethereum, the miners choose transactions to create a new block based upon the gas used by the transaction. Hence, the ordering can be manipulated by adding more gas to transaction that you wrote. This can be abused when the execution flow depends on its position in a block; the attack is called Frontrunning.
  • The article has a great example of this being abused. A lottery could use an external oracle to get a random number. An attacker could observe the pool of pending transactions and wait for the oracle to put data in. The attacker could see this random data, use it to break the randomness and put their data in with a higher gas price to go sooner.
  • How to fix this problem? Oraclize is a service for distributed applications to go between the blockchain and internet. Here, the random data could be grabbed, then used for verification of previously gathered data. A few other math-y ways were mentioned as well, such as signidice and a commit-reveal approach.

An In-Depth Look at the Parity Multisig Bug- 860

Hacking Distributed    Reference →Posted 3 Years Ago
  • Parity had wallets for cryptocurrency These had to be deployed by users, where their money was inside of it.
  • The wallet had two contracts: Wallet and WalletLibrary. If the withdraw() function was not called, then the call was sent to the WalletLibrary code. What's the problem here?
  • Any function within WalletLibrary can be called within the context of the Wallet. In particular, the function initWallet() could be called to change the owner of a contract through this arbitrary delegated call. Yikes!
  • Since the attacker is the owner of the wallet, they can drain all of the funds from the wallet. Wow, that's pretty horrible. Why did this happen?
  • All functions are default external and public. If the contract would checked for double initialization or put this function as internal, then this would not have been possible. It is believed that the authors thought that since the function had NO modifiers for the outside visibility, they were safe.
  • A white-hat hacker identified and drained all remaining wallets to give back to the rightful others. Blockchain is great because it's permanent. But, humans make mistakes, which lead to these issues.