Reproduced Exploit
Curra — anyone can upgrade a proxy and drain it
ERC1967Factory stores an admin for the proxy, but upgrade and upgradeAndCall no longer read it. Any caller can point the proxy at a contract they deployed and then delegatecall it. The proxy holds 1000 tokens. drain transfers all of them to the caller. The recorded admin is 0xbeef and is never the…
Chain
Other
Category
access-control
Date
Jan 2024
Source
AuditVault
EVM Playground
Source-level debugger — step opcodes and Solidity in sync
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.
Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 37160-anyone-can-upgrade-an-implementation-zokyo-none-curra-markdo. Standalone Foundry PoC and full write-up: 37160-anyone-can-upgrade-an-implementation-zokyo-none-curra-markdo_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/access-control/missing-modifier · vuln/dependency/upgradeable-contract
Reproduction: a self-contained Foundry PoC that compiles and runs offline with only
forge-std— no fork and no RPC. Trace: output.txt. PoC: test/37160-anyone-can-upgrade-an-implementation-zokyo-none-curra-markdo_exp.sol.
Key info#
| Impact | HIGH — a non-admin replaces the implementation and takes 1000 tokens from the proxy |
| Protocol | Curra ERC1967Factory.upgradeAndCall |
| Vulnerable code | upgradeTo is called with no admin check |
| Bug class | Public upgrade after the admin check was removed |
| Finding | Zokyo — Curra · id 37160 |
| Report | Zokyo Curra, 4 Jan 2024 |
| Source | AuditVault finding |
| Status | The report says the issue was resolved by making the upgrade paths internal. Reproduced as a local synthetic. |
| Compiler | solc 0.8.27, cancun, via IR |
TL;DR#
ERC1967Factory stores an admin for the proxy, but upgrade and upgradeAndCall no longer read it. Any caller can point the proxy at a contract they deployed and then delegatecall it. The proxy holds 1000 tokens. drain transfers all of them to the caller. The recorded admin is 0xbeef and is never the caller.
A factory that requires msg.sender == admin reverts, and the 1000 tokens stay in the proxy. run() does not warp.
The vulnerable code#
function upgradeAndCall(address proxy, address implementation, bytes memory data) public payable {
// @> VULN: the proxy admin is not checked; anyone may replace the implementation
VaultProxy(payable(proxy)).upgradeTo(implementation);
if (data.length > 0) {
(bool ok,) = proxy.call(data);
require(ok, "call");
}
}
Root cause#
The report says the admin check was removed and both upgrade functions were left public. The factory is still the only address the proxy accepts as the upgrade caller, so going through the factory is enough.
Preconditions#
- The proxy holds 1000 tokens.
- The factory's stored admin is not the attacker.
- The attacker can deploy a contract with a
drainfunction.
Attack walkthrough#
- Call
upgradeAndCall(proxy, evil, drain(attacker)). upgradeTowrites the new implementation. No admin comparison runs.- The factory calls the proxy. The proxy
delegatecallsdrain. - 1000 tokens move to the attacker. The proxy balance is 0.
Diagrams#
Impact#
Every token held by the proxy can be taken by the first caller who supplies an implementation. Here that amount is 1000.
Remediation#
Require the caller to be the stored admin. Keep upgradeAndCall internal, as the report recommends.
How to reproduce#
cd evm-hack-registry/37160-anyone-can-upgrade-an-implementation-zokyo-none-curra-markdo_exp
forge test -vv
Sources#
- AuditVault finding 37160
- Zokyo, Curra, 4 January 2024,
src/ERC1967Factory.sol
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 37160-anyone-can-upgrade-an-implementation-zokyo-none-curra-markdo_exp (evm-hack-registry mirror).
- AuditVault finding: 37160-anyone-can-upgrade-an-implementation-zokyo-none-curra-markdo.
Alerts & third-party analyses
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.