Reproduced Exploit

Remora — unrestricted `SignatureValidator::setAllowlist` enables free `buyTokenOCP`

1. SignatureValidator.setAllowlist is external with no restricted / onlyOwner guard. 2. TokenBank inherits it, so anyone can point the bank at a MaliciousAllowlist whose isSigner returns true for the attacker. 3. The attacker signs a BuyToken digest and calls buyTokenOCP — the off-chain-payment pat…

Oct 2025Otheraccess-control3 min read

Chain

Other

Category

access-control

Date

Oct 2025

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: 63776-signaturevalidatorsetallowlist-is-unrestricted-leading-to-fr. Standalone Foundry PoC and full write-up: 63776-signaturevalidatorsetallowlist-is-unrestricted-leading-to-fr_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/access-control/missing-modifier · vuln/auth/allowlist-spoof · vuln/logic/free-mint

Reproduction: a self-contained Foundry PoC that compiles & runs in an isolated project with only forge-std — no fork, no RPC, no anvil_state. Full trace: output.txt. PoC: test/63776-signaturevalidatorsetallowlist-is-unrestricted-leading-to-fr_exp.sol.

AuditVault taxonomy: lang/solidity · platform/cyfrin · has/github · has/poc · severity/high · sector/stable · sector/staking · genome: missing-modifier · direct-drain · reward-accounting


Key info#

ImpactHIGH — any address can call setAllowlist on TokenBank, install a malicious allowlist, self-sign buyTokenOCP, and drain central-token inventory for free
ProtocolRemora Dynamic Tokens
Vulnerable codeSignatureValidator::setAllowlist (inherited by TokenBank) — external, no access control
Bug classMissing access control on allowlist mutation → signature-authority spoof → free mint
FindingCyfrin — Remora Dynamic Tokens v2.1, 2025-10-22 · #63776 · reporter 0xStalin
ReportCyfrin Remora report
SourceAuditVault
StatusAudit finding — fixed at commit cc447da. Reproduced here as a standalone local PoC.
Compiler^0.8.24 (PoC)

TL;DR#

  1. SignatureValidator.setAllowlist is external with no restricted / onlyOwner guard.
  2. TokenBank inherits it, so anyone can point the bank at a MaliciousAllowlist whose isSigner returns true for the attacker.
  3. The attacker signs a BuyToken digest and calls buyTokenOCP — the off-chain-payment path skips stablecoin transfer.
  4. HARM: the entire central-token inventory is transferred to the attacker for free.

The vulnerable code#

SOLIDITY
function setAllowlist(address allowlist_) external {
    if (allowlist_ == address(0)) revert InvalidAddress();
    if (allowlist != allowlist_) {
        allowlist = allowlist_; // @> VULN: unrestricted
        emit AllowlistSet(allowlist_);
    }
}

Fix: make _setAllowlist internal and expose an external restricted wrapper on TokenBank.


Root cause#

Authorization for off-chain-payment buys is entirely delegated to allowlist.isSigner(signer). Making the allowlist pointer world-writable collapses that trust root.


Preconditions#

  • TokenBank holds central-token inventory.
  • Attacker can deploy a contract and call setAllowlist.

Attack walkthrough#

  1. Deploy MaliciousAllowlist(attackerSigner).
  2. Call tokenBank.setAllowlist(malicious).
  3. Sign BuyToken(investor, token, amount) with the attacker key.
  4. Call buyTokenOCP — inventory moves to the investor with no payment.

Diagrams#

sequenceDiagram participant A as Attacker participant TB as TokenBank participant MAL as MaliciousAllowlist participant INV as Inventory A->>MAL: deploy isSigner always true for A A->>TB: setAllowlist(MAL) Note over TB: unrestricted setter A->>TB: buyTokenOCP self-signed TB->>MAL: isSigner(A) true TB->>INV: transfer free tokens to A

Impact#

Complete drain of remaining central tokens via free buyTokenOCP. Same primitive can abuse ReferralManager bonuses.


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.