-
Notifications
You must be signed in to change notification settings - Fork 71
/
SolidityHackingWorkshop.sol
352 lines (291 loc) · 10.6 KB
/
SolidityHackingWorkshop.sol
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
/* This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://www.wtfpl.net/ for more details. */
/* These contracts are examples of contracts with bugs and vulnerabilities in order to practice your hacking skills.
DO NOT USE THEM OR GET INSPIRATION FROM THEM TO MAKE CODE USED IN PRODUCTION
You are required to find vulnerabilities where an attacker harms someone else.
Being able to destroy your own stuff is not a vulnerability and should be dealt at the interface level.
*/
pragma solidity ^0.4.10;
//*** Exercise 1 ***//
// Simple token you can buy and send.
contract SimpleToken{
mapping(address => uint) public balances;
/// @dev Buy token at the price of 1ETH/token.
function buyToken() payable {
balances[msg.sender]+=msg.value / 1 ether;
}
/** @dev Send token.
* @param _recipient The recipient.
* @param _amount The amount to send.
*/
function sendToken(address _recipient, uint _amount) {
require(balances[msg.sender]!=0); // You must have some tokens.
balances[msg.sender]-=_amount;
balances[_recipient]+=_amount;
}
}
//*** Exercise 2 ***//
// You can buy voting rights by sending ether to the contract.
// You can vote for the value of your choice.
contract VoteTwoChoices{
mapping(address => uint) public votingRights;
mapping(address => uint) public votesCast;
mapping(bytes32 => uint) public votesReceived;
/// @dev Get 1 voting right per ETH sent.
function buyVotingRights() payable {
votingRights[msg.sender]+=msg.value/(1 ether);
}
/** @dev Vote with nbVotes for a proposition.
* @param _nbVotes The number of votes to cast.
* @param _proposition The proposition to vote for.
*/
function vote(uint _nbVotes, bytes32 _proposition) {
require(_nbVotes + votesCast[msg.sender]<=votingRights[msg.sender]); // Check you have enough voting rights.
votesCast[msg.sender]+=_nbVotes;
votesReceived[_proposition]+=_nbVotes;
}
}
//*** Exercise 3 ***//
// You can buy tokens.
// The owner can set the price.
contract BuyToken {
mapping(address => uint) public balances;
uint public price=1;
address public owner=msg.sender;
/** @dev Buy tokens.
* @param _amount The amount to buy.
* @param _price The price to buy those in ETH.
*/
function buyToken(uint _amount, uint _price) payable {
require(_price>=price); // The price is at least the current price.
require(_price * _amount * 1 ether <= msg.value); // You have paid at least the total price.
balances[msg.sender]+=_amount;
}
/** @dev Set the price, only the owner can do it.
* @param _price The new price.
*/
function setPrice(uint _price) {
require(msg.sender==owner);
price=_price;
}
}
//*** Exercise 4 ***//
// Contract to store and redeem money.
contract Store {
struct Safe {
address owner;
uint amount;
}
Safe[] public safes;
/// @dev Store some ETH.
function store() payable {
safes.push(Safe({owner: msg.sender, amount: msg.value}));
}
/// @dev Take back all the amount stored.
function take() {
for (uint i; i<safes.length; ++i) {
Safe safe = safes[i];
if (safe.owner==msg.sender && safe.amount!=0) {
msg.sender.transfer(safe.amount);
safe.amount=0;
}
}
}
}
//*** Exercise 5 ***//
// Count the total contribution of each user.
// Assume that the one creating the contract contributed 1ETH.
contract CountContribution{
mapping(address => uint) public contribution;
uint public totalContributions;
address owner=msg.sender;
/// @dev Constructor, count a contribution of 1 ETH to the creator.
function CountContribution() public {
recordContribution(owner, 1 ether);
}
/// @dev Contribute and record the contribution.
function contribute() public payable {
recordContribution(msg.sender, msg.value);
}
/** @dev Record a contribution. To be called by CountContribution and contribute.
* @param _user The user who contributed.
* @param _amount The amount of the contribution.
*/
function recordContribution(address _user, uint _amount) {
contribution[_user]+=_amount;
totalContributions+=_amount;
}
}
//*** Exercise 6 ***//
contract Token {
mapping(address => uint) public balances;
/// @dev Buy token at the price of 1ETH/token.
function buyToken() payable {
balances[msg.sender]+=msg.value / 1 ether;
}
/** @dev Send token.
* @param _recipient The recipient.
* @param _amount The amount to send.
*/
function sendToken(address _recipient, uint _amount) {
require(balances[msg.sender]>=_amount); // You must have some tokens.
balances[msg.sender]-=_amount;
balances[_recipient]+=_amount;
}
/** @dev Send all tokens.
* @param _recipient The recipient.
*/
function sendAllTokens(address _recipient) {
balances[_recipient]=+balances[msg.sender];
balances[msg.sender]=0;
}
}
//*** Exercise 7 ***//
// You can buy some object.
// Further purchases are discounted.
// You need to pay basePrice / (1 + objectBought), where objectBought is the number of object you previously bought.
contract DiscountedBuy {
uint public basePrice = 1 ether;
mapping (address => uint) public objectBought;
/// @dev Buy an object.
function buy() payable {
require(msg.value * (1 + objectBought[msg.sender]) == basePrice);
objectBought[msg.sender]+=1;
}
/** @dev Return the price you'll need to pay.
* @return price The amount you need to pay in wei.
*/
function price() constant returns(uint price) {
return basePrice/(1 + objectBought[msg.sender]);
}
}
//*** Exercise 8 ***//
// You choose Head or Tail and send 1 ETH.
// The next party send 1 ETH and try to guess what you chose.
// If it succeed it gets 2 ETH, else you get 2 ETH.
contract HeadOrTail {
bool public chosen; // True if head/tail has been chosen.
bool lastChoiceHead; // True if the choice is head.
address public lastParty; // The last party who chose.
/** @dev Must be sent 1 ETH.
* Choose head or tail to be guessed by the other player.
* @param _chooseHead True if head was chosen, false if tail was chosen.
*/
function choose(bool _chooseHead) payable {
require(!chosen);
require(msg.value == 1 ether);
chosen=true;
lastChoiceHead=_chooseHead;
lastParty=msg.sender;
}
function guess(bool _guessHead) payable {
require(chosen);
require(msg.value == 1 ether);
if (_guessHead == lastChoiceHead)
msg.sender.transfer(2 ether);
else
lastParty.transfer(2 ether);
chosen=false;
}
}
//*** Exercise 9 ***//
// You can store ETH in this contract and redeem them.
contract Vault {
mapping(address => uint) public balances;
/// @dev Store ETH in the contract.
function store() payable {
balances[msg.sender]+=msg.value;
}
/// @dev Redeem your ETH.
function redeem() {
msg.sender.call.value(balances[msg.sender])();
balances[msg.sender]=0;
}
}
//*** Exercise 10 ***//
// You choose Head or Tail and send 1 ETH.
// The next party send 1 ETH and try to guess what you chose.
// If it succeed it gets 2 ETH, else you get 2 ETH.
contract HeadTail {
address public partyA;
address public partyB;
bytes32 public commitmentA;
bool public chooseHeadB;
uint public timeB;
/** @dev Constructor, commit head or tail.
* @param _commitmentA is keccak256(chooseHead,randomNumber);
*/
function HeadTail(bytes32 _commitmentA) payable {
require(msg.value == 1 ether);
commitmentA=_commitmentA;
partyA=msg.sender;
}
/** @dev Guess the choice of party A.
* @param _chooseHead True if the guess is head, false otherwize.
*/
function guess(bool _chooseHead) payable {
require(msg.value == 1 ether);
require(partyB==address(0));
chooseHeadB=_chooseHead;
timeB=now;
partyB=msg.sender;
}
/** @dev Reveal the commited value and send ETH to the winner.
* @param _chooseHead True if head was chosen.
* @param _randomNumber The random number chosen to obfuscate the commitment.
*/
function resolve(bool _chooseHead, uint _randomNumber) {
require(msg.sender == partyA);
require(keccak256(_chooseHead, _randomNumber) == commitmentA);
require(this.balance >= 2 ether);
if (_chooseHead == chooseHeadB)
partyB.transfer(2 ether);
else
partyA.transfer(2 ether);
}
/** @dev Time out party A if it takes more than 1 day to reveal.
* Send ETH to party B.
* */
function timeOut() {
require(now > timeB + 1 days);
require(this.balance>=2 ether);
partyB.transfer(2 ether);
}
}
//*** Exercise 11 ***//
// You can create coffers put money into it and withdraw it.
contract Coffers {
struct Coffer {address owner; uint[] slots;}
Coffer[] public coffers;
/** @dev Create coffers.
* @param _slots The amount of slots the coffer will have.
* */
function createCoffer(uint _slots) external {
Coffer storage coffer = coffers[coffers.length++];
coffer.owner = msg.sender;
coffer.slots.length = _slots;
}
/** @dev Deposit money in one's coffer slot.
* @dev _coffer The coffer to deposit money on.
* @param _slot The slot to deposit money.
* */
function deposit(uint _coffer, uint _slot) payable external {
Coffer storage coffer = coffers[_coffer];
coffer.slots[_slot] += msg.value;
}
/** @dev withdraw all of the money of one's coffer slot.
* @param _slot The slot to withdraw money from.
* */
function withdraw(uint _coffer, uint _slot) external {
Coffer storage coffer = coffers[_coffer];
require(coffer.owner == msg.sender);
msg.sender.transfer(coffer.slots[_slot]);
coffer.slots[_slot] = 0;
}
}
//*** Exercise Bonus ***//
// One of the previous contracts has 2 vulnerabilities.
// Find which one and describe the second vulnerability.