Ethernaut

本篇将从这个平台开始,并且尝试用合约的方式进行交互完成,因为ctf中大部分都是要合约交互的。

0x00

第一关主要是讲一些abi的调用方式。

源码

pragma solidity ^0.4.18;

contract Instance {

  string public password;
  uint8 public infoNum = 42;
  string public theMethodName = 'The method name is method7123949.';
  bool private cleared = false;

  // constructor
  function Instance(string _password) public {
    password = _password;
  }

  function info() public pure returns (string) {
    return 'You will find what you need in info1().';
  }

  function info1() public pure returns (string) {
    return 'Try info2(), but with "hello" as a parameter.';
  }

  function info2(string param) public pure returns (string) {
    if(keccak256(param) == keccak256('hello')) {
      return 'The property infoNum holds the number of the next info method to call.';
    }
    return 'Wrong parameter.';
  }

  function info42() public pure returns (string) {
    return 'theMethodName is the name of the next method.';
  }

  function method7123949() public pure returns (string) {
    return 'If you know the password, submit it to authenticate().';
  }

  function authenticate(string passkey) public {
    if(keccak256(passkey) == keccak256(password)) {
      cleared = true;
    }
  }

  function getCleared() public view returns (bool) {
    return cleared;
  }
}
>> contract.address
>> "0x077ba30f5810b179f9ac0b0de5d2968023809d80"

函数修饰词

view function

  • Modifying state variables.

  • Emitting events.

  • Creating other contracts.

  • Using selfdestruct.

  • Sending Ether via calls.

  • Calling any function which is not marked view or pure.

  • Using low-level calls.

  • Using inline assembly containing certain opcodes.

  • pragma solidity ^0.4.21;
    
    contract constantViewPure{
        string name;
        uint public age;
    
        function constantViewPure() public{
            name = "liushiming";
            age = 29;
        }
    
        function getAgeByConstant() public constant returns(uint){
            age += 1;  //声明为constant,在函数体中又试图去改变状态变量的值,编译会报warning, 但是可以通过
            return age;  // return 30, 但是!状态变量age的值不会改变,仍然为29!
        } 
    
        function getAgeByView() public view returns(uint){
            age += 1; //view和constant效果一致,编译会报warning,但是可以通过
            return age; // return 30,但是!状态变量age的值不会改变,仍然为29!
        }
    
        function getAgeByPure() public pure returns(uint){
            return age; //编译报错!pure比constant和view都要严格,pure完全禁止读写状态变量!
            return 1;
        }
    }
    
    
    
    
    ![](/img/block/1.png)
    
    需要注意的是,静态方法不需要gas,应为不需要合约,图中为蓝色的就是静态方法可以秒get状态变量和返回值,而黄色的是需要交互的,并且可以改变合约的状态变量,需要矿工来验证的,那么就需要支付gas并确定。
    
    
    
    第一关主要是如何和合约交互
    
    ![](/img/block/2.png)
    
    也可以用控制台内进行交互,可以看到contract对象下的所有东西,应为这个是和我们的插件交互过的,所以也可以敲player拿到自己的地址,也可以看到这个对象下的方法,在控制台下调用(这个是这个网站写的js吧?ctf中经常需要我们自己写合约交互,所以我就不依赖这个了。)也可以看到这个abi。
    
    abi大概写了:
    
    - 是否是个constant类的方法
    - 参数怎么样?有几个?
    - 函数名?
    - return
    - 是否可支付?接收ether
    
    ## 0x01
    
    第一关开始讲一些已知的实际漏洞 
    pragma solidity ^0.4.18;

import ‘zeppelin-solidity/contracts/ownership/Ownable.sol’;
import ‘openzeppelin-solidity/contracts/math/SafeMath.sol’;

contract Fallback is Ownable {

using SafeMath for uint256;
mapping(address => uint) public contributions;

function Fallback() public {
contributions[msg.sender] = 1000 * (1 ether);
}

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];
}

function withdraw() public onlyOwner {
owner.transfer(this.balance);
}

function() payable public {
require(msg.value > 0 && contributions[msg.sender] > 0);
owner = msg.sender;
}
}


放在remix里一大堆报错,把函数内容全删了只留函数声明。

=> Instance address 0xb1544a094bcb84692709a08553c7e670a1ba2ed4


过关条件

1. you claim ownership of the contract
2. you reduce its balance to 0



ok,思路清晰了,要成为合约owner,并且balance为0

要想成为owner:

1. 贡献1000eth????给爷爬
2. 调用fallback函数。

> ### Fallback
>
> //
>
> fallback函数,回退函数,是合约里的特殊无名函数,有且仅有一个[1](https://me.tryblockchain.org/blockchain-solidity-fallback.html#fn1)。它在合约调用没有匹配到函数签名,或者调用没有带任何数据时被自动调用。
>
> 回退函数是合约里的特殊函数,没有名字,不能有参数,没有返回值。下面来看一个简单的回退函数例子。
>
> ```text
> pragma solidity ^0.4.0;
> 
> contract SimpleFallback{
>   function(){
>     //fallback function
>   }
> }
>

注意:

​ 在调用send函数时因为没有函数调用会默认调用fallback函数,所以要注意fallback函数要加payable,且在fallback里面不能有写入操作,因为会调用gas而导致无限消耗,DAO就是这样被黑的。

事件

区块链是打包一系列交易的区块组成的链条,每一个交易“收据”会包含0到多个日志记录,日志代表着智能合约所触发的事件。

回到第一关,

要调用fallback我们需要给他转账,可以在console里contract.contribute({value:1})

来转账也可以直接用metamask转账,注意金额要小于0.001,可以注意初始函数中显示的owner的eth是1000

那么我们要1000/0.001一次转账显然是不可能的现在我们随便调用一个不存在的函数试试?

叼你妈的,不是可以调用fallback吗???操

走另一条,send函数调用的路子,contract.sendTransaction({value:1})来调用

我透??啥时候合约变成我的了?透再来一次。

=> Instance address 0xcf71f5f466d0da1ae24721a295695322e2b72a96

现在合约不是我的也没贡献,先打钱。

脑壳痛,打钱他不收,你妈嗨!

这新合约那么傲娇吗??????

哦我知了。。。。。。这尼玛就是前面说的那种,fallback函数里面直接写了状态量,所以会不断消耗gas,刚才我能调用是因为合约owner是我的了,没有发生改变不消耗gas。但为什么console里调用console.contribute()就可以了???应该是这种方式没有像send函数那样默认调用fallback

没解决得到,先留着吧。

好了现在有了贡献之后就可以用插件给他打钱,并且这个也把owner改为了我们。刚才是因为没贡献就进不到fallback里机会一直卡着

0x02

  • 怎么在fallback这题里用contribute合约交互把value传进去

    1.console里用contract.contribute({value:1})

    那么用abi交互的方式呢?

  • 这个withdraw函数也不能调用。。。。(没那么多钱。。。合约内就要少很多)


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

vulhub Previous
xss_train Next