Reproduced Exploit

Sablier / PRBProxy — Plugins can be maliciously overridden by colliding signatures

1. Plugins are installed by mapping each selector from methodList() → plugin address. 2. There is no check that the selector is free; a later install overwrites the earlier plugin. 3. A malicious "fee" plugin lists the same selector as Sablier's onStreamCanceled. 4. On stream cancel, the proxy DELE…

Jul 2023Otheruntagged2 min read

Chain

Other

Category

untagged

Date

Jul 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: 54666-plugins-can-be-maliciously-overridden-by-colliding-signature. Standalone Foundry PoC and full write-up: 54666-plugins-can-be-maliciously-overridden-by-colliding-signature_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/wrong-condition · admin-takeover · selector-collision

Reproduction: self-contained Foundry PoC with only forge-std — no fork. output.txt · test/54666-…_exp.sol.

AuditVault taxonomy: lang/solidity · platform/cantina · severity/high · sector/streaming · genome: wrong-condition · admin-takeover


Key info#

ImpactHIGH — a colliding plugin silently replaces onStreamCanceled and steals stream refunds from the proxy owner
ProtocolSablier (PRBProxy integration) — PRBProxyAnnex.installPlugin
Vulnerable codeplugins[methodList[i]] = plugin with no collision check
Bug class4-byte selector collision / missing install guard
FindingCantina — Sablier, July 2023 · #54666 · reporter Zach Obront
Reportcantina_sablier_jul2023.pdf
SourceAuditVault
FixSablier PR 121 — revert on selector collision
Compiler^0.8.24 (PoC)

TL;DR#

  1. Plugins are installed by mapping each selector from methodList() → plugin address.
  2. There is no check that the selector is free; a later install overwrites the earlier plugin.
  3. A malicious "fee" plugin lists the same selector as Sablier's onStreamCanceled.
  4. On stream cancel, the proxy DELEGATECALLs the malicious plugin, which sends the refund to the attacker instead of the owner.

Diagrams#

flowchart TD A["installPlugin good SablierPlugin"] --> B["plugins onStreamCanceled = good"] C["installPlugin malicious plugin"] --> D["plugins onStreamCanceled = bad"] B --> C D --> E["stream.cancel refunds proxy"] E --> F["proxy fallback DELEGATECALL bad plugin"] F --> G["refund sent to attacker treasury"]

Impact#

Theft of cancel refunds (and potentially all refundable stream value once the attacker gains control flow mid-hook).

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.