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; } }
pragma solidity ^0.4.18; 需要注意的是,静态方法不需要gas,应为不需要合约,图中为蓝色的就是静态方法可以秒get状态变量和返回值,而黄色的是需要交互的,并且可以改变合约的状态变量,需要矿工来验证的,那么就需要支付gas并确定。 第一关主要是如何和合约交互  也可以用控制台内进行交互,可以看到contract对象下的所有东西,应为这个是和我们的插件交互过的,所以也可以敲player拿到自己的地址,也可以看到这个对象下的方法,在控制台下调用(这个是这个网站写的js吧?ctf中经常需要我们自己写合约交互,所以我就不依赖这个了。)也可以看到这个abi。 abi大概写了: - 是否是个constant类的方法 - 参数怎么样?有几个? - 函数名? - return - 是否可支付?接收ether ## 0x01 第一关开始讲一些已知的实际漏洞
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 协议 ,转载请注明出处!