On February 24, 2023, the Tranchess team was informed of a potential front-run vulnerability in the EthStakingStrategy contract via Tranchess’ Immunefi Bug Bounty program by an anonymous whitehat (later confirmed to be Jade Han from Kalos Security). The vulnerability can only be exploited by authorized node operators; The potential risk revolves around an authorized node potentially able to transfer out a limited amount of the strategy funds awaiting deposit. On the onset of discovery on Feb. 24, 2023, there were a total of 14 validator keys at risk, evoking a maximum of 14 * 32 = 448 ETH potential loss.
Post discovery, Tranchess had immediately changed the ETH fund cap value to 0, effectively preventing any illicit fund operations, whilst allowing the tech team to execute a mid-term mitigation plan to fix the vulnerability at the same time.
At the time of this recap, the vulnerability had been mitigated. All funds are safe. A bounty of 44.8 ETH (10% of the total potential loss, the maximum payment of our bug bounty program) has been paid via Tranchess treasury to the whitehat. We want to take this opportunity here to thank them again for discovering and reporting the exploit, as well as the ImmuneFi community for their support.
The attack vector is fundamentally made possible by the Ethereum Consensus Layer’s design choice of not enforcing the same withdrawal credential for subsequent deposits. A malicious node operator could associate the validator’s public key with the operator-controlled withdrawal credentials by front-running a deposit transaction that assigns the protocol-controlled withdrawal credentials, taking control of the withdrawal and disrupting the proper distribution of the user withdrawals.
For the mid-term solution, Tranchess rolled out the following new on-chain
SafeStaking modules working with off-chain safeguard nodes to secure all future deposits.
Here are the details of the fixing process:
- In the
EthStakingStrategymodule, we have restricted public access to the
depositfunction to only the
- In the
NodeOperatorRegistrymodule, we have restricted public access to the
updateVerifiedCountfunction to only the
SafeStakingmodule. And we have added a
registryVersionvariable to record validator-key-related editions and match them with the offline snapshot.
SafeStakingmodule is a new contract that contains two wrappers (
safeVerifyKeys) around the
- The module maintains a new Safeguard role.
- For the
safeDepositfunction, we now require off-chain collection of safeguard signatures attesting to a specific snapshot of valid validator states. Any one of the safeguards can trigger an emergency pause when noticing abnormal activities.
- For the
safeVerifyKeysfunction, we also require off-chain collection of safeguard signatures attesting to a specific set of verified keys. Any one of the safeguards can trigger an emergency pause when noticing abnormal activities.
This fix effectively restrains the attack surface of the aforementioned front-run by requiring a group of trusted off-chain parties to independently attest and collectively enforce the security of the next deposits. The fix places two separate checks on-chain and off-chain for verification before performing the actual deposit. The
SafeStaking module will only green-light the deposit and initiate the original deposit logic after all 7 of the criteria below are verified on-chain and true:
- Verify the Integrity of the Execution Layer:
- Observed deposit root matches with on-chain deposit root from the official DepositContract
- Observed registry version matches with on-chain registry version from
- The number of safeguard signatures is greater or equal to safeguard quorum
- All signatures are valid, and each signed by a different member of the Safeguard role
- Observed blockHash matches with the blockhash(blockNumber)
- Limit maximum possible amount of funds under risk by a malicious majority of safeguards
- The deposit amount does not exceed the ceiling
- The current timestamp should be at least
minDepositTimeIntervalaway from the last deposit timestamp
We provide further justification for using the deposit root as the certification for no front-run deposit to any pubkey since our off-chain check. Here’s a step-by-step overview to initiate a
- Get the finalized epoch on Beacon Chain, and its corresponding block number N_0 and block hash H_0
- Read Beacon Chain balances of all pubkeys at the end of the finalized epoch, making sure there’s no malicious deposit till block H_0
- Get the latest block number N_1 and its block hash H_1
- Get all Deposit events between block H_0 and H_1, making sure there’s no malicious deposit till block H_1
get_deposit_root()in block H_1
- Send a deposit transaction to
SafeStakingwith N_1, H_1 and this
Since the return value of
get_deposit_root() of the Deposit Contract changes every time a new deposit is made, if there’s any deposit after block N_1 but before any transaction (by either a malicious validator or anyone else),
get_deposit_root() returns a different hash, and the transaction reverts. Similarly, if there’s any deposit in a reorganized block N_1, its block hash does not match H_1 our transaction also reverts. As a result, we conclude that as long as the contract sees the same
get_deposit_root() as what we saw off-chain, it’s guaranteed that there’s no deposit (to any pubkey) since our off-chain check.
Smart Contract Deploy Plan
NodeOperatorRegistry(copy node operators and pubkeys from the old
SafeStakingand initialize it (set safeguards and quorum).
BeaconStakingOracleand initialize it (set members and quorum).
- Connect the smart contracts (set strategy’s reporter to
BeaconStakingOracle, set strategy’s safe staking to
- Propose strategy update:
- Wait for the internal 3-day timelock (hardcoded in
FundV4) and execute the following transactions in a
- Transfer ETH from the old strategy to the fund:
- Apply strategy update:
- Update all
- Initialize the new
EthStakingStrategy(copy the last beacon balance report from the old
- Re-open ETH staking:
Tranchess plans to establish a committee and upgrade the smart contracts for auto-checks and monitoring of the funds and deposits. It will require significant work, and we will update the solution in future releases and proposals with our community.