Ethernaut靶场题解
Ethernaut
Fallback
要求是,成为这个合约的所有者,并使这个合约的钱为零
分析;
从这个合约中可以看到,要成为owner
1,我们的钱必须大于合约持有者的钱,但是最开始合约本身就有1000ether,这个时候就走contribute函数不行,因为我们没有那么多钱
2,此时发现receive,只要我们的钱和合约的钱大于0,就可以成为这个合约的持有者
方法:
1,在remix上不能部署这个合约,因为部署后,合约初始化,owner就是我们自己了
constructor() { |
直接使用合约地址At Adress
2,调用contribute函数,转账1wei;
2,调用receive函数,转账1wei,就完成了,注意记得将钱收回来
Fal1out
要求是,成为这个合约的所有者
分析:
注意这个的solidity的版本是0.6,没有影响关系,只是构造函数的写法不一样,如:
//solidity 0.8 |
看与owner相关的函数,再去分析构造的合约名字Fallout,仔细发现有个Fal1out的函数,它实际上是命名错误的,这个时候我们就可以调动这个函数,刚好成为合约的所有者
方法:直接部署不行,因为里面有其他的import,所以我们就外部调用这个合约,使用接口,另创建一个合约
pragma solidity ^0.8.0; |
Coin Flip
要求是,连续猜对10次猜对硬币的结果
分析:就是要想办法调用flip()函数10次,让猜测的结果与side一致
方法:直接上攻击合约,在目标合约内,部署攻击合约,就是一样的调用目标合约的flip函数之后,得到的guess在进入一样的目标合约之中,这样我们的猜测值guess就与side一样了
contact Hack { |
Telephone
要求是,获得该合约的所有权
分析:代码还是挺短的,主要就是弄懂tx.origin是什么意思,tx.origin是发起交易的账户,msg.sender是当前直接调用这个合约的即时账户,只要这俩者不一样就成功了
详细解释:
- A调用B合约
- tx.origin=A
- msg.sender=A
- A调用B合约,B合约调用C合约
- tx.origin=A
- msg.sender=B
方法:直接调用Telephone合约,不能满足条件,所以我们要写一个攻击合约来调用Telephone合约
contract Hack { |
_target接受Telephone的地址,我们部署Hack合约,msg.sender就是我们的地址
Token
要求是,我们有20个代币,需要让代币余额增加到20以上
分析:还是看solidity版本,0.6版本没有内置Safemath,所以就可以执行溢出或者下溢的操作
Safemath:在Solidity 中,SafeMath是一个常用的库,用于防止整数溢出和下溢的安全数学运算工具。由于Solidity的整数类型(如 uint 和int )是有限的,当进行加法、减法、乘法或除法运算时,如果操作结果超出了类型范围,就会导致溢出或下溢
如,0-1会导致下溢,就会生成最大的无符号整数,并且大于0
方法:调用攻击合约中,再调用transfer函数,给msg.sender转账1个代币,由于攻击合约没有代币,就会发生下溢
pragma solidity ^0.8.0; |
Delegation
要求是,获得这个合约的所有权
分析:理解delegatecall,就是调用pwm函数,通过触发fallback函数,然后进行委托用
方法:部署Delegate合约,但是不调用,我们是在delegation合约上调用pwn函数
Force
要求是,使这个合约的余额大于零
分析:代码还是很简单,就是一个空合约,什么函数也没有,就考虑到自毁合约的功能
自毁合约:自毁合约是一种智能合约,通常基于区块链技术,其设计初衷是在特定条件下自动执行某些预定的操作,最终将合约自身的功能或者存储的资产销毁。这种合约的设计可以确保在特定情况发生时,例如某个时间点到达、某个条件达成或者特定的事件发生,合约内部的资产或者代码可以被永久删除或者无法访问。
方法:使用通过selfdestructh功能删除一个合约,合约内所有的余额将被强制发送到另一个合约,就是写一个攻击合约自毁
contract Hack { |
部署攻击合约时记得发送1wei
Vault
要求是,解锁保险箱
分析:password是个私有变量,不能直接获取,但是它也是个状态变量,可以访问的,通过回到ethernaut网站,在控制台获得密码即可
方法:在控制台输入 await web3.eth.getStorageAt(contract.address,1)
, 获得密码
king
要求是,破坏游戏规则
分析:通过合约来看就是阻碍别人调用receive函数,拒接别人的转账,
方法:写一个攻击合约,不要有fallback,receive函数,防止新玩家转账,成为新国王
contract Hack { |
在执行攻击合约时,首先检查当前的奖金值,然后部署攻击合约时,发送该奖金值
Re-entrancy
要求是:窃取合约中的所有资金
分析:这是一个典型的重入攻击
重入攻击:重入攻击的典型示例是以太坊的智能合约中发生的情况。以太坊的智能合约是按照以太币(ETH)的传统交易方式执行的,合约可以调用其他合约或发送ETH到外部账户。如果一个合约在调用外部合约时先转移ETH给另一个合约,并且在接收ETH后再执行其他逻辑,那么这个外部合约可以在接收ETH后调用原合约,重新执行发送ETH的逻辑,导致重复的ETH转移,从而造成资金损失。
通过不断调用withdraw函数,窃取资金
方法: 写一个攻击合约
// SPDX-License-Identifier: MIT |
部署攻击合约,调用attack函数发送1ether,即可
Elevator
要求是,到达建筑物的顶楼
分析:第一次的building.isLastFloor(_floor)要为false,满足if的条件,第二次的building.isLastFloor(_floor)要为ture,使电梯到楼顶
方法: 就是在攻击合约中写isLastFloor函数,达到要求,
contract Hack { |
Privacy
要求是,解锁该合约
分析:就是将locked初始值ture,改为false,调用unlock函数
方法: 首先要知道每个私有状态可变的储存slot,如图:

再使用Web库来获取这个数据,然后截取为16字节,即为密钥,再调用unlock函数,将其作为参数传递即可
Naugth Coin
要求是,将代币余额变为零
分析:难点有个时间锁,在转移代币时,必须等待10年,所以就思考其他的方法,然后就是熟悉ERC20合约,目标合约importl了ERC20,后面的攻击合约也要使用,建议先熟悉ERC20合约再来解题,我这就直接上攻击合约
方法:
pragma solodity ^0.8.0; |
Preservation
要求是,获得该合约的所有权
分析:委托调用,注意看函数签名,bytes4 constant setTimeSignature = bytes4(keccak256("setTime(uint256)"))
,当我们调用setFirstTime函数时,委托调用会执行setTime函数,此时timeZone1Library的地址将会更新,如果我们再次调用setFirstTime函数,又更新地址,这样就能成为owner
方法:还是写一个攻击合约,进行调用
contract Hack { |
Recovery
要求是,恢复丢失的0.001以太币
分析:这是一个工厂合约,由于不知道代币的地址,无法找到丢失的以太币,所以我们的任务是找到这个代币合约的地址,然后进行自毁合约,有俩种方法找到代币合约的地址,一是通过区块链浏览器Etherscan,查询调用generate Token交易,找到代币合约地址,二是通过计算,在Ethereum Stack Exchange上可以查询计算方式
方法:采用计算地址的方法
pragma solidity ^0.8.0; |
MagicNunmber
要求是,调用whatIsTheMeaningOfLife()函数,并且返回数字42,但是攻击合约不能超过10个合约
分析:如果直接调用
contract Hack{ |
这个合约将超过10个合约码,不符合要求,这时就使用汇编编写一个智能合约,然后手动部署代码(本人还在学习汇编语言)
Denial
要求是,在所有者在调用withdraw函数时拒绝其提取资金
分析:这题大概思路很好懂,就是如何去实现拒绝转账这步要思考一下,又是要用到汇编语言,等我学习后再来补题解
Shop
要求是,以低于要价的价格从商店购买物品
分析:就是使状态变量isSold等于true,而且还要使价格低于100,目标合约已经给了一个接口,就是就是在调用这个接口的时候满足要求,第一次调用的时候,要使价格高于100,满足if的条件,第二次调用的时候price就设置低于100的价格即可
方法:
contract Hack{ |