Reproduced Exploit

Vaultka — a 1-unit deposit resets the victim's unlock

deposit(amount, receiver) always sets userTimelock[receiver] to block.timestamp + lockTime. It does not matter that receiver already has a deposit whose lock has elapsed.

Aug 2023•Ethereum•dos•3 min read

Chain

Ethereum

Category

dos

Date

Aug 2023

Source

AuditVault

EVM Playground

Source-level debugger — step opcodes and Solidity in sync

evm-hack-analyzer

The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.

Loading fork state…

Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 44897-an-attacker-can-make-victims-deposit-be-locked-forever-zokyo. Standalone Foundry PoC and full write-up: 44897-an-attacker-can-make-victims-deposit-be-locked-forever-zokyo_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/dos/frozen-funds · vuln/governance/timelock-bypass

Key info#

ProtocolVaultka
FindingZokyo, August 2023, AuditVault 44897
Vulnerable codeWater.deposit writes userTimelock[receiver]
HarmThe victim's 1000 stays in the vault. Withdraw reverts for another 30 days.
Chainlocal synthetic (audited Ethereum contracts)

TL;DR#

deposit(amount, receiver) always sets userTimelock[receiver] to block.timestamp + lockTime. It does not matter that receiver already has a deposit whose lock has elapsed.

The victim has 1000 in the vault and the unlock time is the current timestamp, so withdraw would succeed. The attacker deposits 1 with receiver set to the victim. The unlock moves 30 days forward. The victim's withdraw reverts. The vault still holds 1001, and the victim's wallet is unchanged.

depositFixed leaves a non-zero timelock alone. After that deposit the victim withdraws 1000.

The vulnerable code#

SOLIDITY
token.transferFrom(msg.sender, address(this), amount);
assets[receiver] += amount;
// @> VULN: depositing for receiver resets their unlock to now + lockTime.
userTimelock[receiver] = block.timestamp + lockTime;

withdraw then requires block.timestamp >= userTimelock[msg.sender].

Root cause#

The timelock is per receiver, and every deposit overwrites it with a full lockTime measured from now. A deposit made for someone else is enough. Repeating a 1-unit deposit before the new unlock keeps the funds frozen. lockTime here is 30 days.

Preconditions#

The victim's earlier deposit has matured: assets are 1000 and userTimelock equals the current timestamp. The attacker has 1 unit and approval. One transaction. The maturity is the starting state, not a second warp inside run.

Attack walkthrough#

  1. The vault holds the victim's 1000. seedMatured records assets 1000 and a timelock of block.timestamp.
  2. That timelock is not in the future, and assets are 1000.
  3. The attacker approves 1 and calls deposit(1, victim).
  4. userTimelock becomes block.timestamp + 30 days.
  5. The victim's withdraw(1000) reverts. Assets are 1001. The victim received 0.

Control calls depositFixed(1, victim) on a mature timelock. The timelock stays at block.timestamp. withdraw(1000) pays the victim 1000.

Diagrams#

flowchart TD A["Victim has 1000 and the unlock is now"] --> B["Attacker calls deposit of 1 for the victim"] B --> C["VULN: timelock becomes now plus 30 days"] C --> D["Victim withdraw reverts"] D --> E["1001 stays in the vault"] E --> F["A deposit that keeps a mature timelock lets 1000 out"]

Impact#

The victim cannot withdraw 1000. Each further 1-unit deposit from anyone resets the same timer, so the freeze lasts as long as the attacker keeps paying the dust.

Remediation#

Stop taking a receiver other than the caller, or do not extend a timelock that is already set.

How to reproduce#

BASH
cd 44897-an-attacker-can-make-victims-deposit-be-locked-forever-zokyo_exp
forge test -vv

Sources#


Sources & further analysis#

Reproductions & code

Alerts & third-party analyses

  • Web3Sec X hacked database: search.
  • Rekt leaderboard: search.
  • Solodit incident search: search.

These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.