深入Ethernaut CTF:智能合约漏洞实战解析
上周Zeppelin发布了以太坊CTF项目Ethernaut。该CTF是了解区块链交互方式和智能合约漏洞基础的绝佳入门项目。CTF部署在ropsten区块链上,可免费获取测试以太币。通过浏览器开发者控制台和MetaMask插件与CTF交互。
我有幸成为首个完成所有挑战的选手。以下是我的解题过程。
1. Fallback
这是首个挑战,更像是一个API使用引导。让我们详细观察Solidity智能合约的结构。
挑战描述
合约包含一个构造函数和四个函数。目标是成为合约所有者并提取所有资金。
构造函数(与合约同名)仅在部署时执行一次:
|
|
contribute()函数将调用者发送的以太币(msg.value)存入contributions映射。若该值超过合约所有者的贡献值,调用者将成为新所有者:
|
|
getContribution()是简单的取值器:
|
|
withdraw()允许所有者提取全部资金(onlyOwner修饰符限制调用权限):
|
|
最后是回退函数(fallback function),需满足调用者曾贡献过资金:
|
|
回退函数机制
理解回退函数需了解以太坊的函数选择器和参数机制。调用函数时,交易数据包含函数ID(函数签名keccak256哈希的前4字节)和参数。例如transfer(address,uint256)的函数ID是0xa9059cbb。
智能合约执行时首先通过调度器检查函数ID。若无匹配则调用回退函数(如果存在)。可使用开源反汇编工具Ethersplay查看调度器结构。
解决方案
分三步操作:
- 调用
contribution存入初始资金 - 调用回退函数成为所有者
- 调用
withdraw提取资金
具体操作:
- (1) 在开发者控制台执行
contract.contribution({value:1}) - (2) 通过MetaMask直接向合约地址转账
- (3) 调用
contract.withdraw()
2. Fallout
挑战描述
目标是成为合约所有者。合约看似包含构造函数,但函数名与合约名存在差异:
|
|
该函数并非构造函数,而是可公开调用的普通函数!此漏洞在真实合约中确实存在(如ZiberCrowdsale和PeerBudsToken),可通过静态分析工具Slither检测。
解决方案
直接调用contract.Fal1out()即可成为所有者。
3. Token
挑战描述
初始拥有20个代币,目标是获取大量代币。transfer函数用于代币转账:
|
|
虽然存在溢出检查require(balances[msg.sender] - _value >= 0),但_value和balances均为无符号整数,该条件恒为真!因此可在balances[msg.sender] -= _value处触发下溢,使余额变为极大值。
解决方案
调用contract.transfer(0x0, 21)触发下溢,使balances[msg.sender]变为2^256-1。
4. Delegation
挑战描述
目标是成为Delegation合约的所有者。虽然该合约无法直接修改owner,但其持有的Delegate合约包含pwn()函数:
|
|
关键点在于Delegation的回退函数使用delegatecall:
|
|
delegatecall允许被调用合约在调用合约的上下文中执行代码(包括状态修改),曾导致3000万美元损失的Parity钱包黑客事件就利用此特性。
解决方案
调用回退函数时在msg.data中填入pwn()的函数ID(0xdd365b8b),使delegatecall在Delegation的上下文中执行pwn(),从而修改所有者。
5. Force
挑战描述
需向空合约转账以太币。由于没有payable回退函数,直接转账会失败。但可通过以下方式绕过:
- 调用
selfdestruct(address) - 设置挖矿奖励地址
- 合约创建前向地址转账
解决方案
创建合约并调用selfdestruct向目标合约强制转账:
|
|
使用payable构造函数可在部署时存入以太币,这些资金将通过selfdestruct转移。可通过Remix浏览器在ropsten测试网部署测试。
6. Re-entrancy
挑战描述
最终挑战!需在创建时向合约转账1以太币并最终取回资金。重点关注两个函数:
donate用于向合约捐款:
|
|
withdraw用于取回资金:
|
|
问题在于:通过msg.sender.call.value(_amount)()转账时,若接收者是包含回退函数的合约,该函数可在balances[msg.sender] -= _amount执行前再次调用withdraw,形成重入攻击。此漏洞曾导致DAO黑客事件。
解决方案
需使用代理合约:
- 编写包含回退函数(内部调用withdraw)的合约
- 调用donate向漏洞合约存款
- 调用withdraw触发重入
可在智能合约数据库中找到通用攻击框架,读者可自行适配。同样可通过Remix在ropsten测试网部署测试。
结论
Ethernaut CTF体验极佳,其界面使智能合约安全入门变得简单。感谢Zeppelin的出色工作!
如果您对文中提到的工具感兴趣,或需要智能合约安全评估,欢迎联系我们!
原文发布于2017年11月6日,涵盖区块链、夺旗赛和符号执行技术