DeRace vesting proxy ownership takeover and emergency exit
Exploit Transactions
Victim Addresses
0x2fd602ed1f8cb6deaba9bedd560ffe772eb85940EthereumLoss Breakdown
Similar Incidents
BUILD Governance Takeover and Unlimited Mint ACT Exploit
34%LiteV3 Bridge Aggregator Proxy Initialization Race Enabled Unauthorized UUPS Takeover
34%Access-control bug draining 5 ETH from token contract
33%Beanstalk flash-loan governance takeover drains treasury assets
32%FlippazOne ungated ownerWithdrawAllTo lets attacker drain 1.15 ETH
32%Audius Governance Reinitialization and Treasury AUDIO Drain
32%Root Cause Analysis
DeRace vesting proxy ownership takeover and emergency exit
1. Incident Overview TL;DR
An unprivileged Ethereum EOA 0x2708cace7b42302af26f1ab896111d87faeff92f seized ownership of a funded DeRace vesting proxy 0x2fd602ed1f8cb6deaba9bedd560ffe772eb85940 and drained its entire DERC balance. The attacker first called an unprotected init(...) function on the shared vesting implementation 0xf17ca0e0f24a5fa27944275fa0cedec24fbf8ee2 via the proxy in tx 0xd5e2edd6089dcf5dca78c0ccbdf659acedab173a8ab3cb65720e35b640c0af7c (block 13155321), reinitializing an already-configured proxy and overwriting the owner and vesting schedule. With ownership reassigned, the attacker then called emergencyExit(address) via the same proxy in tx 0x96bf6bd14a81cf19939c0b966389daed778c3a9528a6c5dd7a4d980dec966388 (block 13155350), transferring exactly 5,760,000 DERC from the proxy to the attacker EOA in a single ERC20 transfer.
This is an Anyone-Can-Take (ACT) opportunity: any unprivileged EOA with sufficient ETH for gas, using only public ABI/bytecode information and canonical on-chain data, can reproduce the same two-transaction strategy against any similarly configured proxy that delegates to implementation 0xf17c.... The profit predicate is a strictly positive net gain of 5,760,000 DERC with zero DERC-denominated fees; gas fees are paid only in ETH and sum to 0.0399448 ETH across the two attacker-crafted transactions.
2. Key Background
The DeRace (DERC) token 0x9fa69536d1cda4a04cfb50688294de75b505a9ae is a standard Ownable, Pausable ERC20 with 18 decimals and a fixed total supply of 120,000,000 * 1e18 minted to the deployer. The verified source at artifacts/root_cause/seed/1/0x9fa6.../src/Contract.sol shows that balances are stored in a single mapping slot and that transfers follow the usual ERC20 semantics:
// DERC ERC20 excerpt (verified source)
contract ERC20 is Pausable {
mapping(address => uint256) private _balances;
uint256 private _totalSupply = 120e6 ether;
event Transfer(address indexed from, address indexed to, uint256 value);
constructor() {
_balances[_msgSender()] = _totalSupply;
}
function balanceOf(address account) public view returns (uint256) {
return _balances[account];
}
}
On top of DERC, the victim system deploys vesting contracts as EIP‑1167 minimal proxies that delegate all logic to a shared implementation at 0xf17ca0e0f24a5fa27944275fa0cedec24fbf8ee2. The proxy involved in this incident is 0x2fd602ed1f8cb6deaba9bedd560ffe772eb85940; its runtime code in the trace prestate is a standard minimal proxy that forwards to 0xf17c..., and its storage holds the vesting parameters and owner field.
The vesting implementation is not verified on Etherscan, but Heimdall decompilation and traces show that it exposes:
- an
init(uint256 start, uint256[] releasePeriods, uint256[] releasePercents, address token)function that writes owner and vesting configuration into storage, and - an
emergencyExit(address token)function that transfers the proxy’s entire token balance to a caller-specified recipient, gated only by ownership.
Proxy 0x2fd6... was first initialized and funded as intended:
- Deployer / factory
0x95626473b6782292d20f1b07a85a8b7f6ab63677created and initialized the proxy, becoming the initial owner. - Funder
0x45166749c271f0688f624f6c1e897ad14b8bf6d7transferred5,760,000DERC to the proxy in tx0x5f8351ee555d6111161fd4a8d8232ebc665e8d05d8e24fe54c9afaac4df54b44(block12951637), as shown by the Etherscan token transfer API:
// Etherscan tokentx for DERC vs proxy 0x2fd6... (excerpt)
{
"hash": "0x5f8351ee555d6111161fd4a8d8232ebc665e8d05d8e24fe54c9afaac4df54b44",
"from": "0x45166749c271f0688f624f6c1e897ad14b8bf6d7",
"to": "0x2fd602ed1f8cb6deaba9bedd560ffe772eb85940",
"value": "5760000000000000000000000",
"tokenSymbol": "DERC",
"tokenDecimal": "18"
}
At this point, the proxy correctly held 5,760,000 DERC on behalf of vesting beneficiaries, with ownership and release schedule under the control of 0x9562.... The system’s safety relies on two implicit invariants:
- only the intended owner or controlled governance can change vesting parameters or trigger full withdrawals, and
- once a proxy is initialized and funded, arbitrary EOAs cannot reinitialize it or seize ownership.
3. Vulnerability Analysis & Root Cause Summary
The core vulnerability is a reinitialization and access‑control bug in the vesting implementation 0xf17c.... The init(...) function:
- can be called via delegatecall from any minimal proxy pointing to
0xf17c..., - has no access control (no
onlyOwneror equivalent), and - does not record or check an “initialized” flag or a constraint such as
require(owner == address(0)).
As a result, any EOA can call init(...) through a proxy at any time, even after the proxy has already been initialized and funded. When called, init(...) overwrites the owner address and vesting schedule in the proxy’s storage. Once ownership is reassigned, the attacker can immediately invoke emergencyExit(address) to transfer the proxy’s entire DERC balance to an arbitrary address they control.
In the incident at hand, this bug allowed 0x2708... to:
- Reinitialize proxy
0x2fd6...in tx0xd5e2e..., replacing owner0x9562...with0x2708...and simplifying the vesting schedule. - Drain the full
5,760,000DERC balance to0x2708...in tx0x96bf6b...viaemergencyExit(address).
Because both functions are callable using only standard ABI information and Ethereum transaction semantics, and because no privileged off-chain coordination is required, this forms an ACT opportunity for any unprivileged EOA.
4. Detailed Root Cause Analysis
4.1 Pre‑state and funding
We define the pre‑state σ_B as Ethereum mainnet state immediately before tx 0xd5e2e... in block 13155321, reconstructed from QuickNode debug_traceTransaction (prestateTracer) and Etherscan metadata and source/decompilation for the relevant contracts.
At this point:
- Proxy
0x2fd6...has already been initialized once by0x9562...with a multi‑period vesting schedule, reflected in several storage slots encoding release timestamps and percentages. - Token transfer logs show that
0x2fd6...holds exactly5,760,000DERC, funded by0x4516...in tx0x5f83...as shown above. - Ownership logs (
OwnershipTransferredevents) for0x2fd6...show an earlier transfer from the deployer to0x9562..., but no involvement of0x2708...until tx0xd5e2e....
4.2 Ownership takeover via unguarded init(...)
Tx 0xd5e2edd6089dcf5dca78c0ccbdf659acedab173a8ab3cb65720e35b640c0af7c is sent by attacker EOA 0x2708... to proxy 0x2fd6... with calldata encoding init(uint256,uint256[],uint256[],address). The QuickNode prestateTracer diff for this tx shows storage for 0x2fd6... changing as follows:
// debug_traceTransaction prestateTracer for tx 0xd5e2e... (excerpt)
{
"post": {
"0x2fd602ed1f8cb6deaba9bedd560ffe772eb85940": {
"storage": {
"0x...0000": "0x00000000000000000000002708cace7b42302af26f1ab896111d87faeff92f00",
"0x...0001": "0x0000000000000000000000000000000000000000000000000000000061cf6f51",
"0x...0002": "0x0000000000000000000000000000000000000000000000000000000062267251",
"0x...0003": "0x...0001",
"0x...0004": "0x...0001",
"0x8a35acfb...9b": "0x...2710"
}
}
},
"pre": {
"0x2fd602ed1f8cb6deaba9bedd560ffe772eb85940": {
"storage": {
"0x...0000": "0x000000000000000000000095626473b6782292d20f1b07a85a8b7f6ab6367700",
"0x...0001": "0x...61093038",
"0x...0002": "0x...62e3cc38",
"0x...0003": "0x...0004",
"0x...0004": "0x...0004",
"0x8a35acfb...9b": "0x...09c4",
"0x8a35acfb...9c": "0x...09c4",
"0x8a35acfb...9d": "0x...09c4",
"0x8a35acfb...9e": "0x...09c4"
}
}
}
}
Interpretation:
- Storage slot
0x...0000encodes the owner address. Its value changes from a word embedding0x95626473b6782292d20f1b07a85a8b7f6ab63677(the deployer/initial owner) to one embedding0x2708cace7b42302af26f1ab896111d87faeff92f. - Slots
0x...0001and0x...0002change from earlier vesting start/end timestamps to new ones. - Slots keyed by
0x8a35acfb...change from multiple entries with value0x09c4(decimal 2500, representing 25% each over four periods) to a single0x2710entry (decimal 10000, representing 100% in a single period).
Ownership transfer logs for 0x2fd6... in ownership_transferred_logs.json show a matching OwnershipTransferred event in block 13155321 with:
previousOwner = 0x95626473b6782292d20f1b07a85a8b7f6ab63677newOwner = 0x2708cace7b42302af26f1ab896111d87faeff92f
Combined with the Heimdall decompilation of implementation 0xf17c..., which shows init(...) writing owner and vesting parameters without checking an initialized flag or restricting the caller, this establishes that:
init(...)is callable by arbitrary EOAs through the proxy,- it can be invoked more than once, and
- in this tx it reassigns ownership and vesting configuration of an already funded proxy to the attacker.
4.3 Full token drain via emergencyExit(address)
After seizing ownership, 0x2708... sends tx 0x96bf6bd14a81cf19939c0b966389daed778c3a9528a6c5dd7a4d980dec966388 to proxy 0x2fd6... at block 13155350. Etherscan’s transaction metadata (stored under seed artifacts) shows:
from = 0x2708cace7b42302af26f1ab896111d87faeff92fto = 0x2fd602ed1f8cb6deaba9bedd560ffe772eb85940input = 0xa441d0670000000000000000000000002708cace7b42302af26f1ab896111d87faeff92f
Selector 0xa441d067 corresponds to emergencyExit(address), and the argument is the attacker EOA. The QuickNode receipt and decoded logs for this tx confirm the token transfer:
// erc20_transfers_decoded.json for tx 0x96bf6b... (excerpt)
[
{
"token": "0x9fa69536d1cda4a04cfb50688294de75b505a9ae",
"from": "0x2fd602ed1f8cb6deaba9bedd560ffe772eb85940",
"to": "0x2708cace7b42302af26f1ab896111d87faeff92f",
"amount": "5760000000000000000000000"
}
]
The balance-diff tracer for the same tx quantifies the ERC20 deltas:
// balance_diff_prestate.json for tx 0x96bf6b... (excerpt)
{
"erc20_balance_deltas": [
{
"token": "0x9fa69536d1cda4a04cfb50688294de75b505a9ae",
"holder": "0x2fd602ed1f8cb6deaba9bedd560ffe772eb85940",
"before": "5760000000000000000000000",
"after": "0",
"delta": "-5760000000000000000000000"
},
{
"token": "0x9fa69536d1cda4a04cfb50688294de75b505a9ae",
"holder": "0x2708cace7b42302af26f1ab896111d87faeff92f",
"before": "0",
"after": "5760000000000000000000000",
"delta": "5760000000000000000000000"
}
]
}
An eth_call against the DERC contract at block 13155350, saved in derc_balance_at_13155350.json, returns a zero balance for 0x2fd6..., consistent with the tracer output. Together, these artifacts show that:
- immediately before tx
0x96bf6b..., the proxy holds exactly5,760,000DERC, and - after the tx, the proxy’s DERC balance is zero and the attacker’s DERC balance has increased by the same amount.
4.4 Invariant and breakpoint
The relevant safety invariant for any funded vesting proxy that has already been initialized is:
- No unprivileged EOA other than the intended owner may seize ownership or unilaterally redirect, accelerate, or fully withdraw vested tokens; in particular,
init(...)must not be callable again by arbitrary callers after the initial setup, and ownership must remain under controlled governance or the designated owner.
The concrete breakpoint that violates this invariant is the design of init(...) in implementation 0xf17c...:
init(...)is callable at any time via delegatecall from proxies.- It does not enforce
onlyOwneror an equivalent modifier. - It does not check or record an initialized state (for example,
require(owner == address(0))).
Transaction 0xd5e2e... is the first time 0x2708... calls init(...) through proxy 0x2fd6.... Because the function is completely unguarded, the call succeeds under EVM rules and overwrites owner and vesting schedule in a single step, breaking the invariant. The subsequent call to emergencyExit(address) in 0x96bf6b... realizes the impact of this broken invariant by withdrawing all funds.
5. Adversary Flow Analysis
The adversary’s strategy is a two‑step ACT flow on Ethereum mainnet that any unprivileged EOA can reproduce using only public on-chain data and standard tooling.
5.1 Transaction 1 (ownership takeover)
- Tx hash:
0xd5e2edd6089dcf5dca78c0ccbdf659acedab173a8ab3cb65720e35b640c0af7c - Block:
13155321 - From:
0x2708cace7b42302af26f1ab896111d87faeff92f(attacker EOA) - To:
0x2fd602ed1f8cb6deaba9bedd560ffe772eb85940(vesting proxy) - Calldata:
init(uint256,uint256[],uint256[],address)with chosen schedule and token address. - On-chain effect: Proxy
0x2fd6...delegatecalls into implementation0xf17c...;init(...)overwrites owner from0x9562...to0x2708...and replaces the original multi‑period vesting schedule with a single 100% release period. Token balances remain unchanged. - Gas cost: Receipt data (
gasUsed = 0x12e5e,effectiveGasPrice = 0x2e90edd000) imply a deterministic ETH fee of0.0154812ETH.
This transaction is realizable by any unprivileged EOA who:
- knows the proxy address
0x2fd6...and target implementation ABI (recoverable from decompile and public metadata), and - can construct the
init(...)calldata and pay the gas fee.
5.2 Transaction 2 (emergency exit drain)
- Tx hash:
0x96bf6bd14a81cf19939c0b966389daed778c3a9528a6c5dd7a4d980dec966388 - Block:
13155350 - From:
0x2708cace7b42302af26f1ab896111d87faeff92f - To:
0x2fd6... - Calldata:
emergencyExit(address)with recipient set to0x2708.... - On-chain effect: Proxy
0x2fd6...delegatecalls into0xf17c...;emergencyExit(address)reads the proxy’s entire DERC balance and transfers5,760,000DERC from0x2fd6...to0x2708.... No other ERC20 movements occur. - Gas cost: Receipt data (
gasUsed = 0xeee7,effectiveGasPrice = 0x5d21dba000) imply a deterministic ETH fee of0.0244636ETH.
The attacker’s net portfolio change in the chosen reference asset (DERC) is:
- Fees paid in DERC:
0 - DERC gained:
+5,760,000DERC - Net DERC change:
+5,760,000DERC
The ETH gas outlay (0.0154812 + 0.0244636 = 0.0399448 ETH) is fully determined by the receipts but is not used as the reference asset; the ACT profit predicate is satisfied purely in DERC units.
5.3 Reusability of the strategy
Analysis of 0x2708...’s broader transaction history shows the same pattern applied to another minimal proxy 0xdd571023d95ff6ce5716bf112ccb752e86212167 that also delegates to implementation 0xf17c.... Decompilation of 0xdd57... confirms that it follows the same minimal proxy pattern.
Given:
- the public nature of proxy and implementation code (via bytecode and decompile),
- the absence of access control and initialization guards in
init(...), and - the deterministic behavior of
emergencyExit(address),
any unprivileged EOA that learns of a funded proxy pointing to 0xf17c... can reproduce this two‑transaction strategy to seize ownership and drain the proxy’s tokens.
6. Impact & Losses
6.1 Quantified loss for this incident
For proxy 0x2fd6...:
- Token: DERC (
0x9fa6...) - Amount lost:
5,760,000DERC (5,760,000 * 1e18base units) - Victim address:
0x2fd602ed1f8cb6deaba9bedd560ffe772eb85940 - Adversary address:
0x2708cace7b42302af26f1ab896111d87faeff92f
The loss is confirmed by:
- Etherscan token transfer logs (
derc_transfers_tokentx.json) showing the funding tx0x5f83...(from0x4516...to0x2fd6...) and the drain tx0x96bf6b...(from0x2fd6...to0x2708...). - Balance-diff tracer output for tx
0x96bf6b...showing the proxy’s DERC balance decreasing from5,760,000 * 1e18to0and the attacker’s balance increasing by the same amount. - A direct
eth_callat block13155350returning zero DERC balance for proxy0x2fd6....
6.2 Systemic impact
The DERC ERC20 contract behaves according to its specification; the vulnerability lies entirely in the vesting implementation and proxy configuration:
init(...)is callable indefinitely and overwrites owner and schedule without restrictions.emergencyExit(address)allows the owner to withdraw all tokens from a proxy.
Any other vesting proxy that:
- delegates to implementation
0xf17c..., - is initialized and funded, and
- does not have additional protective controls (such as pausing transfers or revoking the vulnerable implementation),
is vulnerable to the same ACT strategy. The data collection attempt proxies_pointing_to_0xf17c_from_0x9562.json did not identify additional proxies from the same deployer within the scanned block range, but this does not change the mechanism: the root cause is implementation‑level and affects all proxies that share it.
7. References
-
Victim protocol and token
- DeRace Token (DERC) ERC20 contract:
0x9fa69536d1cda4a04cfb50688294de75b505a9ae - Verified source and build artifacts:
artifacts/root_cause/seed/1/0x9fa69536d1cda4a04cfb50688294de75b505a9ae/
- DeRace Token (DERC) ERC20 contract:
-
Vesting proxies and implementation
- Vulnerable vesting implementation:
0xf17ca0e0f24a5fa27944275fa0cedec24fbf8ee2- Etherscan metadata and Heimdall decompile:
artifacts/root_cause/data_collector/iter_2/contract/1/0xf17ca0e0f24a5fa27944275fa0cedec24fbf8ee2/
- Etherscan metadata and Heimdall decompile:
- Main victim proxy:
0x2fd602ed1f8cb6deaba9bedd560ffe772eb85940- Etherscan metadata, decompile, ownership logs, tx history, and DERC balance:
artifacts/root_cause/data_collector/iter_2/contract/1/0x2fd602ed1f8cb6deaba9bedd560ffe772eb85940/andartifacts/root_cause/data_collector/iter_3/address/1/0x2fd602ed1f8cb6deaba9bedd560ffe772eb85940/
- Etherscan metadata, decompile, ownership logs, tx history, and DERC balance:
- Additional proxy using the same implementation:
0xdd571023d95ff6ce5716bf112ccb752e86212167- Etherscan metadata and decompile:
artifacts/root_cause/data_collector/iter_3/contract/1/0xdd571023d95ff6ce5716bf112ccb752e86212167/
- Etherscan metadata and decompile:
- Vulnerable vesting implementation:
-
Key transactions
- Funding tx (DERC → proxy):
0x5f8351ee555d6111161fd4a8d8232ebc665e8d05d8e24fe54c9afaac4df54b44(block12951637)- Etherscan tokentx API result:
artifacts/root_cause/data_collector/iter_3/address/1/0x2fd602ed1f8cb6deaba9bedd560ffe772eb85940/derc_transfers_tokentx.json
- Ownership takeover via
init(...):0xd5e2edd6089dcf5dca78c0ccbdf659acedab173a8ab3cb65720e35b640c0af7c(block13155321)- QuickNode debug_traceTransaction prestateTracer and receipt:
artifacts/root_cause/data_collector/iter_2/tx/1/0xd5e2edd6089dcf5dca78c0ccbdf659acedab173a8ab3cb65720e35b640c0af7c/
- Emergency exit drain via
emergencyExit(address):0x96bf6bd14a81cf19939c0b966389daed778c3a9528a6c5dd7a4d980dec966388(block13155350)- Receipt, decoded ERC20 transfers, debug traces, and balance diffs:
artifacts/root_cause/data_collector/iter_2/tx/1/0x96bf6bd14a81cf19939c0b966389daed778c3a9528a6c5dd7a4d980dec966388/and
artifacts/root_cause/data_collector/iter_3/tx/1/0x96bf6bd14a81cf19939c0b966389daed778c3a9528a6c5dd7a4d980dec966388/
- Funding tx (DERC → proxy):
-
Adversary account and behavior
- Adversary EOA:
0x2708cace7b42302af26f1ab896111d87faeff92f- Normal and internal tx history:
artifacts/root_cause/data_collector/iter_2/address/1/0x2708cace7b42302af26f1ab896111d87faeff92f/
- Normal and internal tx history:
- Adversary EOA:
These references together provide a complete, reproducible on-chain evidence trail for the ownership takeover and emergency exit drain, and for the underlying implementation-level vulnerability that makes this an ACT opportunity.