Smart Contract Security

Ethereum & Smart Contracts

What are they and why should I care?

Ethereum
  • Digital currency. Can be sent to smart contracts.
    • Wallets are known as external accounts which can only send/receive ethereum
    • External accounts cannot host code
    • MetaMask is a common wallet available as a browser extension
Smart Contracts

A decentralized way to run code where virtual machines (EVMs) can send and receive digital currency.

  1. Decentralized, meaning they are hosted on thousands of different connected nodes (servers) instead of only one.
  2. Each node runs a virtual computer that hosts (deploys) the compiled code.
  3. Anyone can interact with the virtual machines

Pentesting Strategy

It’s important that we get comfortable interacting with blockchains. When hunting for vulnerabilities in the real world (on mainnet, explained below), we don’t want to spend our hard earned money when messing around with smart contract calls. Forking is an especially helpful technique that allows us to pretend we’re on mainnet for testing - like a sandbox.

Networks

Ethereum contains different networks that you can connect to. There are 4 networks that we care about:

  1. Mainnet
    • The real ethereum network that is online and is used for financial exchange and production use.
  2. Testnet
    • A decentralized blockchain that is online and is used for testing.
    • Testnet examples:
      • Rinkeby
      • Ropstein
  3. Fork
    • A copy of the mainnet/testnet being proxied into our local testing environment. We can unlock and control any wallets as if we had the private key.
    • This is useful if we want to interact with a smart contract in its current state, instead of recreating that state manually via local deployment.
  4. Local
    • Our local debugging environment. There’s a useful tool named ganache that can provide us a local blockchain.
    • To test locally:
      • Copy the solidity code to the local project
      • Deploy the smart contract to ganache
      • Recreate the state of the VM (sometimes)
        • Ex) A CTF challenge, the EVM is initialized and your account is given 20 tokens that you must use to hack the smart contract somehow. Instead of forking we can deploy locally, then deposit 20 tokens into our account manually.
Smart Contract Attack Surfaces & Tips
  1. Data within smart contracts (global vars) are not private. The EVM bytecode is published on the blockchain and there are simple ways to obtain private variables.
    • There’s a way to read any variable within the contract based on its slot index
  2. When money is sent to a smart contact without specifying a function name, this triggers the fallback function which is represented as fallback() or receive().
    • Vulnerable contracts may send money to your contract address in this way.
      • If this occurs and conditions are not checked before, this can be exploited.
        • Ex) A vulnerable contract with a withdraw() function may send the user’s funds BEFORE decrementing their balance
        • This is what’s known as a Re-entrancy attack (recursion)
    • When transfering funds, you must remember:
      • The recipient isn’t always an externally owned account (a wallet). It may be a smart contract, which will run code when the transfer is confirmed.
      • If an attacker contract’s fallback function is triggered, it may break things by reverting
  3. Modifiers are really important. They determine what functions you can leverage.
    • Ex) function isLastFloor(uint) external returns(bool);
      • This interface can be used for both reading and modifying data within the contract.
      • The view function modifier on interfaces can prevent state changes from occurring
  4. Composition can be used for working within the same block as the victim contract
    • Deploy an attack contact and pass the victim contract address as the constructor parameter so that calls can be made from the attack contract to the victim contract.
    • If “randomness” is computed based on block-related attributes, these results can be recreated within the attack contract. The only secure way to provide pseudo-randomness is using oracles.
  5. There is a difference between tx.origin and msg.sender that allows phishing attacks.
    • By convincing a victim to send funds to an attacker contract, the fallback function can be triggered - if a victim contract (e.g. victim’s bank) is then called, the tx.origin will be the victim and msg.sender will be the attacker.
    • tx.origin should never be used to authenticate identity
    • tx.origin should never be used to determine whose tokens to transfer
  6. Solidity has underflows and underflows.
    • Safe math libraries are built-in after 0.6.0 (TODO: double check this)
    • Earlier solidity versions must import SafeMath
  7. delegatecall is a way to call arbitrary functions in a contract or library.
    • This should never be in a fallback function!
    • It allows the attacker to call whatever function they want in the contract specified.
      1
      2
      3
      4
      5
      6
      fallback() external {
      (bool result,) = address(delegate).delegatecall(msg.data);
      if (result) {
      this;
      }
      }
  8. Even if a contract doesn’t mark their fallback as payable, there’s still a special way to send it money.
    • selfdestruct(_address); can be used by an attack contract to force a payment.
      • In this case the attacker function would be function attack(address _address) payable public
    • It’s important not to count on the invariant address(this).balance == 0 for any contract logic
  9. When doing type conversions (casting) it’s usually easier do it within an attacker contract.
  10. Bitwise operations are painful but elegant, and will help with smart contract hacking.
    • A ^ B = C then A ^ C = B
  11. extcodesize(caller()) is the .text section (opcodes, code instructions) size of the caller - You can hide the size of your attack contract by doing everything in the constructor
  12. For ERC-20 tokens there’s two ways to transfer funds:
    1. function transfer(address _to, uint256 _value) public
    2. function transferFrom(address _from, address _to, uint256 _value) public
      1. The approve(address _toBeApproved, _value) public function must be called first before transferFrom(toBeApproved, 100000000) will work
MEV
  • Copyrights © 2021-2024 blindCyber

请我喝杯咖啡吧~

支付宝
微信