Contract Audit

Contract Audit

合约审计定位于审计步骤,审计工具和审计中需要注意的地方.

审计步骤: 运行slither工具, 执行测试文件, 人工审计

Slither

  • Detector

  • inheritance-graph

  • call-graph

Reentrancy

A reentrancy attack is a type of vulnerability in smart contracts that allows attackers to execute a function multiple times before the previous call completes. This can lead to unexpected and harmful behavior, such as the theft of funds or unauthorized access to data.

use check-effect-interaction pattern

use mutex

  • same method for the same contract

    pragma solidity 0.8.9;
    
    contract VulnerableContract {
        mapping (address => uint256) public balances;
    
        function withdraw(uint256 amount) public {
            require(balances[msg.sender] >= amount, "Insufficient balance");
            (bool success, ) = msg.sender.call{value: amount}("");
            if (success) {
                balances[msg.sender] -= amount;
            }
        }
    
        function deposit() public payable {
            balances[msg.sender] += msg.value;
        }
    }
    
  • different methods for the same contract

  • different methods for different contracts

  • read-only reentrancy

Access Control

Arithmetic issue

unchecked, 溢出

Bad randomness

Timestamp dependence

External data source dependence

Chainlink

Transaction order risk

ENS front attack

Dos Attack

  • Dos with (unexpected) revert
  • Dos with block gas limit

forcibly send ether to contract

Deprecated attack

  • call depth attack

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.5;
    
    // DO NOT USE!!!
    contract Auction {
        address highestBidder;
        uint256 highestBid;
    
        function bid() external payable {
            if (msg.value < highestBid) revert();
    
            if (highestBidder != address(0)) {
                payable(highestBidder).send(highestBid); // refund previous bidder
            }
            highestBidder = msg.sender;
            highestBid = msg.value;
        }
    }
    

    Prior to the implementation of EIP-150, the contract above could be susceptible to “Call Depth Attack.” This is because a malevolent bidder could initiate a recursive call to itself, causing the stack depth to increase to 1023 before calling the bid() function. As a result, the send(highestBid) call would fail silently, meaning that the previous bidder would not receive a refund, while the new bidder would still become the highest bidder.

    EIP150: all gas would be consumed well before reaching the 1024 call depth limit

闪电贷

  • 去中心化借贷平台攻击事件bZx

    • 黑客通过闪电贷从去中心化数字资产衍生交易平台dYdX借出10000枚ETH;
    • 使用其中的5000枚ETH抵押在去中心化借贷平台Compound借出112枚wBTC;
    • 剩下的5000枚ETH到去中心化借贷平台bZx开空单;
    • 用借出的112枚wBTC到去中心化交易所Uniswap砸盘,让wBTC价格下跌;

常见的攻击原理

Testing And Verification

Gas Optimization

  • Packing variable into a single slot

  • Use Merkle tree proof

  • stateless contracts

  • calldata, memory, storage之间的复制

  • Stop using bools for True/False

    EVM将bool存储为uint8类型,占用两个字节;因为EVM字长32字节,需要额外的逻辑来解析此值.

    将false存储为uint256(0),true -> uint256(1), 这也会产生大量的gas, 因为将零值flip转为其它值需要支付固定费用.

    最有效的方法是1表示false, 2表示true.(非零值会使合约部署成本高一些)