Blockchain Security

Zodiac Treated Four Bytes of Revert Data as Authorization in the Gnosis Pay ERC-1271 Exploit

Legacy @gnosis.pm/zodiac 3.4.0 introduced the ERC-1271 call-status regression; the representative Delay pinned still-affected 3.4.1; v3.0.2 was the first fixed tag after the package rename; and current 4.3.0 is SOSEC's default evaluation target for new deployments and migrations, with old signing paths paused, old queues closed, and normal-return and revert-magic signers tested as a pair.

A technical investigation desk with translucent Zodiac, Safe, and ERC-1271 call layers connected by a red failure path to the queue and execution transaction timeline.
In this article

Research basisSOSEC blockchain security research · Zodiac, Delay, Safe, and Gnosis Pay Account Kit source pinned; Gnosis Chain transactions, historical state, call traces, and unverified runtime reviewed field by field

SourceGnosis post-mortem / pinned Zodiac, Safe, and Account Kit source / Gnosis Chain transactions, state, and runtime / SOSEC field-level review

1 The operational decision comes first: stop the old path, inspect old queues, and deploy verifiable runtime

Decision: an enabled Roles or Delay path enters remediation scope when it can reach the affected contract-signature checker or when its runtime provenance remains unknown. Immediately pause managed APIs, relayers, and background jobs that produce the old appended contract-signature form, then read every module implementation and unresolved queue item. Every candidate still needs local compatibility, reproducible-build, on-chain runtime, and positive/negative acceptance evidence.

Four literal objects operate in sequence: the signer/module Safe supplies a contract-signer identity → the Zodiac checker interprets the call result as authorization or rejection → the Delay queue persists that decision and starts a clock → the asset/card Safe holds assets and executes the matured operation. The signer Safe and asset Safe in the representative path are different accounts: one participates in authentication; the other performs custody and final execution.

A Solidity low-level call returns two values at once. status reports whether the child call completed normally, while data contains normal return data or revert data. The incident validator ended with REVERT(0,4) while making its error data equal the ERC-1271 success value 0x1626ba7e. A safe predicate requires both status=true and matching magic. The failed control-flow result remains authoritative alongside those bytes.

A signer Safe supplies identity, the Zodiac checker decides authorization, Delay stores and times the decision, and a separate asset Safe executes the queued payment through MultiSend.
Figure 1: signer Safe → Zodiac checker → Delay queue → asset Safe. Identity judgment, timed persistence, and asset execution belong to distinct objects.

Gnosis reported that the June 1, 2026 attack removed about $1.496 million and left another roughly $300,000 inaccessible to the attacker; 5,281 wallets with at least one dollar held about $1.8 million in total exposure. Monitoring observed the first large unauthorized transfer at 06:17 UTC and the team confirmed root cause at 08:06 UTC. Gnosis then paused relevant services, repaired modules, restored accounts in stages, absorbed the loss, and restored user balances. The public outcome establishes incident scale and the user result. Operators still need chain evidence for module replacement and queue disposition on every instance.

The lineage contains two numbering generations, so comparing “3.4.1” and “3.0.2” as bare numbers reverses chronology and the security conclusion. The v4 line contained an equivalent status check from its early public tags, and current 4.3.0 retains it. An inventory treats package namespace, tag, commit, module implementation, and runtime hash as one version identity. Operations read deployed code first and use the label to explain provenance.

The decisive state change occurred before assets moved. Zodiac interpreted a failed frame as an enabled module signer, and Delay persisted the operation. After cooldown, the execution function checked queue head, operation hash, and time without calling the original signer again. A source patch prevents new misclassification while authority already stored under the old runtime may remain executable. Patch deployment, old queues, account authority, and financial restoration are therefore four concurrent closure objects.

2 One failed frame became queue item 11 and moved assets after the clock opened

The following transaction pair is the report's single authoritative event chain. At 08:29:05 UTC on June 1, 2026, transaction 0x5f562057e1963964fc3a6d8b105057f3ab9f9dd7091ff5bdd8d51453f51bd456 reached Delay 0x7ECf01F102c00C51DF095e9f6C4ce81F4CA878f3 on Gnosis Chain. Selector 0x468721a7 identifies four-argument execTransactionFromModule(address,uint256,bytes,uint8). Safe signature bytes, a 32-byte salt, and outer r/s/v follow the canonical ABI call.

In historical Account Kit commit 51f60789df3252c06aab963654a183f25abc6afd, encodeDelayModTransaction(), lines 43–52, builds the complete selector-bearing call, and encodeERC1271Signature(), lines 72–92, appends the contract-signature tail. Outer r identifies signer/module Safe 0xce7d2f8e54e1104ed7a4c1f88dac63aa4a825561. It was an enabled Delay module and the identity from which Zodiac requested an ERC-1271 verdict.

That Safe used SafeL2 1.4.1 implementation 0x29fcB43b46531BcA003ddC8FCB67FFE91900C762 and Safe4337Module v0.2.0 fallback handler 0xa581c4A4DB7175302464fF3C06380BC3270b4037. The handler translated the updated selector into the legacy overload and built a SafeMessage. Safe's first contract-signature slot identified candidate 0x5A77953CAa27eD4638F4DfdC665b8064D0e97A35. The fixed-block owner set at block 46470631 excluded that candidate. Safe called it at line 315; the candidate reverted, so the later line 331 GS026 membership and ordering check was never reached.

queue transaction:
  attacker EOA
    → Delay / inherited Zodiac checker
      → signer Safe 0xce7d…5561
        → Safe4337Module handler → Safe.checkSignatures(threshold=1)
          → candidate 0x5a779…97a35
            ↳ REVERT, data = 0x1626ba7e
      ↳ low-level result: success=false, returnData=0x1626ba7e
    → vulnerable checker accepts signer Safe
    → consumed[signer][moduleTxHash] = true
    → Delay stores queue item 11
A low-level call emits both control status and returned bytes; its failed branch can still carry four bytes, so authorization must inspect both results.
Figure 2: The EVM supplied status and data on separate result paths. The affected predicate consumed only data, allowing the failed path to reach authorization.

Zodiac recovered signer Safe 0xce7d…5561, and moduleOnly marked its ModuleTx digest consumed. The Delay body stored keccak256(abi.encodePacked(to,value,data,operation)) and creation time at index 11, then advanced the queue tail. The batch bytes already described GNO and EURe calls, while the receipt contained no matching token Transfer. The right to move assets now existed as queue state with a time condition.

At 08:53:45 UTC, transaction 0x4d3888cf3c68bffb2013001aabaf7b3596fc5d3f5d4cc5f139487d8a4140ae14 called executeNextTx() on the same Delay, supplying operation fields that reproduced item 11's queue hash. Delay's owner, avatar, and target resolved to asset/card Safe 0x0cae88383f2d1f308292e58cd71984bb6120e03d. That Safe used DELEGATECALL into MultiSend 0x38869bf66a61cF6bDB996A6aE40D5853Fd43B526, moving 17.855442968223287575 GNO and 364.071530831603544366 EURe from the asset Safe.

One transaction writes false authorization into item eleven, the queue opens after 180 seconds, and a second transaction executes from the asset Safe 1,480 seconds later.
Figure 3: Queueing and execution were 1,480 seconds apart. Historical cooldown was 180 seconds and expiration 1,800 seconds, producing an executable interval from second 180 through second 1,980.

The sample pair was twenty-four minutes and forty seconds apart; configured cooldown was only 180 seconds. Execution at second 1,480 was past the earliest execution point and 500 seconds before expiration. Queue creation, time admission, and asset effect are established by different state and receipts. In a response plan, those three times govern detection, cancellation, and final reconciliation respectively.

2.1 Three digests bind different state; a deployed patch applies to new signer decisions

moduleTxHash is an EIP-712 digest under the Delay domain over the selector-bearing canonical call and timestamp-derived salt; it keys consumed[signer][moduleTxHash]. The fallback handler wraps that digest in a SafeMessage under the signer Safe's domain. Delay's queue hash covers only to/value/data/operation, with the numeric nonce locating its mapping entry. Investigation tooling retains all three identities and keeps replay, signature, and queue questions separate.

This state split determines repair order. A new checker rejects a new failed-magic request. For an existing item, executeNextTx() uses the stored operation hash and clock. Operations records therefore need two closure blocks: the block where new vulnerable authorization stopped and the block where the final authorization created by the old predicate became non-actionable. The account closes this technical path only after the second point.

3 Runtime, signer reachability, and queue state together define exposure

Potential scope begins with actual on-chain instances. On every supported chain, list each Safe, singleton, owners, threshold, guard, fallback handler, enabled modules, module proxy, implementation, runtime hash, cooldown, expiration, and queue head and tail. Follow minimal proxies, implementation slots, factory parameters, or custom master-copy relationships to the executing code. Explorer names, ABI, package lockfiles, and bytecode length are candidate-discovery aids; final classification is bound to the incident-block execution path.

Runtime classification uses vulnerable, fixed, and unknown states. Fixed source or a reproducible build can expose _isValidContractSignature() directly. For an unknown implementation, a fixed-block fork presents two signers with identical four bytes delivered through RETURN and REVERT. Authorization of both identifies this regression; authorization of the normal return alone closes this defect. A real production module call may write a queue item, so dynamic classification runs on a fork or dedicated low-value account.

Reachability then narrows candidates through public moduleOnly entries, appended signature tails, contract signer types, module enablement, and caller capability. Queue state raises urgency. Every account records separate evidence for potentially exposed, attempted, queued, executed, contained, and restored states. Contained means new vulnerable authorization has stopped and every old item has a terminal state. Restored additionally requires trusted authority and reconciled user value.

3.1 Exact asset details belong in one ledger; exposure, transfer, control, and restoration stay separate

Gnosis's asset table reports a total of approximately $1,496,151: $641,159 GNO, $453,175 EURe, $399,121 USDC.e, $2,202 SAFE, $323 WETH, $135 xDAI, $28 USDC, and $7 USDT. The components are displayed as whole dollars, so their visible subtotal differs from the approximate total by one dollar through rounding. The case keeps both the official total and every line item, with raw token quantity and log index as the time-stable ledger.

Four account ledgers separately record potential exposure, actual transfer, attacker-controlled value, and user restoration.
Figure 4: An incident needs four ledgers. Potential exposure, on-chain transfer, attacker-controlled value, and user restoration may all differ at the same moment.

Roughly $1.8 million of total exposure across 5,281 wallets with at least one dollar exceeded the transferred-asset table, and the project described about $300,000 as inaccessible to the attacker. The public post-mortem assigns no single chain reason to every dollar. Account ledgers use available evidence to record freezing, failed execution, cancellation, unrealized value, or unknown disposition. Gnosis restored user balances from its own resources, so financial restoration may also exceed asset recovery.

EURe migration, wrapping, and intermediate contracts can emit several Transfer events for one economic asset. Reconciliation builds a flow graph from token contract, uint256 amount, decimals, log index, and pre/post balance, then calculates net inflow to attacker-controlled addresses. Dollar valuation has a separate price source and timestamp, stored beside the incident-day project account.

The representative 17.855442968223287575 GNO and 364.071530831603544366 EURe amounts come from the second execution receipt and balance check and establish mechanism only. The official table supplies aggregate incident scope; the two evidence sets answer different questions. Scope covers the Gnosis Pay card-Safe compositions using the relevant Delay/Roles modules and signature path. The claim does not extend to Gnosis Chain consensus or ordinary Safe core.

4 Detection and containment must beat the queue clock

A Delay queue event is a security state change. The live pipeline joins it to complete input, historical implementation, and trace, then emits caller, Delay, signer Safe, fallback handler, first-slot candidate, membership-reach state, asset Safe, queue index, executable and expiry time, operation, MultiSend children, token, recipient, and registered service intent. High-value delegatecall, authority changes, first-seen signer paths, and items without registered intent enter human review at the creation block.

Failed magic is a high-specificity signal. Archive traces or fixed-block replay find an ERC-1271 call with failed status and a magic prefix, then confirm that an ancestor reached the Zodiac checker. A top-level receipt lacks the internal status/data pair, so the evidence pipeline retains traces or reproducible historical replay material.

trace condition:
  call.type        = STATICCALL
  call.selector    ∈ {0x1626ba7e, 0x20c13b0b}
  call.success     = false
  call.output[0:4] = 0x1626ba7e

enrichment:
  signer Safe / handler / Zodiac ancestor
  membership reached or not reached
  queue mutation and registered intent

Execution clustering groups by caller, beneficiary, inner target, runtime hash, and normalized batch hash, then counts Safes and asset types within ten minutes. Managed services retain request ID, session, device, signer method, relayer, Safe, salt, expected digest, and transaction hash. Behavioral trace detects changed addresses and recompiled validators; intent records distinguish legitimate direct calls from unauthorized queueing.

4.1 Queue flooding requires role separation, rate limits, and auditable batch cancellation

An actor can exhaust human review with many low-value entries and hide a high-impact action among them. Production design separates queues by role or risk and applies verifiable queue-rate limits by caller, account, and window. Owner, module, guard, upgrade, unlimited allowance, arbitrary delegatecall, and high-value transfer operations receive longer cooldown. Observable rejection records let flood control preserve the evidence stream.

Batch cancellation follows the deployed version. Sample v1.1.0 can only use setTxNonce() to skip a contiguous index range. Before that action, the operator lists every hash, user intent, maximum consequence, and resulting head; an independent authority approves the operation and state is reread after finality. A future per-hash or per-role batch facility should constrain guardians to revoke, pause, extend cooldown, or disable modules, with no authority to transfer assets, add owners, or lower threshold.

Response order follows dependency:

  1. Freeze new authorization. Stop every old signing API, relayer, and job; preserve request, configuration, trace, and queue snapshots.
  2. Sort by time remaining. Decode all unresolved items and prioritize authority changes, delegatecall, approvals, and high-value asset actions.
  3. Neutralize through independent authority. Cancel, batch-skip, disable the module, or protect assets, recording a terminal state for each item.
  4. Replace vulnerable runtime. Bind source, build, and on-chain code and close every alternate route that delegates into the old checker.
  5. Restore account and value. Reconcile authority and balances from a trusted block, then complete financial accounting and user notice.
  6. Admit cohorts after paired tests. The normal signer passes; the revert-magic signer fails before any queue side effect.

SOSEC starting recommendation: high-value cooldown covers the local ninety-day P99 for detection, intent confirmation, emergency signing, and chain finality, plus a congestion buffer established by stress testing. Wallet operations and contract engineering jointly own it; measurement window, threshold, and rollback trigger are calibrated per chain. The sample's historical 180 seconds describes the incident condition; every production baseline needs local calibration.

5 Repair ends when runtime, old queues, account authority, and user assets are all trusted

The upgrade manifest uses on-chain relationships as keys: chain ID, Safe, module proxy, implementation, eth_getCode, runtime hash, source commit, compiler, optimizer, libraries, immutables, initialization data, replacement transaction, and enable/disable blocks. Fixed source describes expected behavior for a build. On-chain hash and upgrade transaction establish when a user account obtained it.

A new or migratable system evaluates current 4.3.0 first; v3.0.2 marks only the first-fixed boundary in the v3 lineage. Compatibility tests cover EOA, approved-hash, standard ERC-1271, nested Safe, Roles, Delay, and existing business encoding. When a module is immutable, deploy a reviewed replacement and use an atomic or explicitly ordered Safe transaction to disable the old module and enable the new one. Old address, initialization values, and replacement transaction enter inventory.

Queue disposition surrounds code replacement. First enumerate items authenticated by the old runtime and pin creation block, executable time, expiry, inner action, and intent. Read them again after upgrade to confirm cancellation, skip, expiry, or re-authorization. An isolated checker upgrade leaves stored execution rights; isolated queue clearing lets the vulnerable entry generate new ones. The same change ticket closes both.

Account restoration starts at a trusted block before malicious queueing and compares owners, threshold, guard, fallback handler, modules, allowances, delegated authority, sessions, and balances. Current state may already contain authority changes caused by queued actions, so copying it into a replacement can preserve risk. Technical restoration and financial restoration have separate timestamps and evidence and meet at the user closure point.

A response workbench stops new authorization, clears queues, replaces runtime, reviews account relationships, and restores user assets.
Figure 5: After entry is paused, recovery continues through queues, implementation, authority, balances, and user notice.

5.1 Paired canaries, rollback conditions, and cohort reopening form production admission

The positive canary uses a standards-compliant contract signer that normally RETURNs magic, creates one harmless queue record through the production-shaped path, and is cancelled or executed under supervision. The negative canary uses REVERT(0,4) with the same magic and must fail at signer recovery, leaving consumed, queue nonce, hash, timestamp, and events unchanged. Both carry registered intent and remain visible to the behavioral alert.

Rollback triggers include runtime-hash mismatch, wrong implementation resolution, legitimate-signer incompatibility, any side effect from revert magic, an omitted old item, unreconciled account authority, or missing monitoring visibility. Rollback keeps managed entry paused, restores an accepted implementation and module relationship, and preserves completed safety cancellations. The selected artifact must retain the correction before restoration.

Reopening proceeds by cohort. Each batch records Safe count, asset ceiling, entry point, implementation hash, paired acceptance, queue terminal states, and observation window. An anomaly stops the next cohort and rolls back the current one. After full reopening, historical hunting continues from the first vulnerable deployment block through the block where the last old authorization became non-actionable; every failed-magic, suspicious-queue, or clustered-execution hit receives account review.

The interface labels a Delay action as queued and shows executable time, expiry, action summary, and available cancellation route. Final status changes after execution. Emergency notice uses at least one channel independent of the primary session. User restoration evidence links to the specific transaction, raw token amount, and account state; the global dollar total remains summary context.

6 Technical appendix keeps the minimum receipt for each question: source for decisions, bytes for input, trace for path

This appendix carries reproduction details that the main narrative no longer needs to derive repeatedly. Each evidence class answers one question. Source describes how a pinned version handles call results. ABI and Account Kit explain transaction construction. Incident-block runtime explains the machine behavior of the unverified validator. Trace shows the order and status in which the objects met. Storage and logs show how authorization became state and produced asset effects.

6.1 Source receipts pin the regression, representative deployment, fix, and current stable line

The parent of 9a9e380 retained success && magic; that commit replaced the first tuple element with an empty position and shipped in legacy 3.4.0. The 3.4.1 snapshot still uses a data-only predicate in SignatureChecker.sol, lines 127–135. Fix 4cf1530 restored status, v3.0.2 resolves to commit 4a24501572db953f355634e20701dbf50732f625, and current v4.3.0 resolves to b5fad243… with the invariant still present.

Representative Delay proxy 0x7ECf…78f3 resolves to implementation 0x4A97E65188A950Dd4b0f21F9b5434dAeE0BBF9f5, whose verified source matches Delay v1.1.0 commit 515737f67bb704d0ce14d45cd6f7cc8e6b4655d1; package.json, lines 1–32, locks legacy 3.4.1. execTransactionFromModule(), lines 128–140, writes the queue; executeNextTx(), lines 176–200, consumes the head; and lines 214–220 define the four-field hash.

Safe and its handler form the sample propagation path. The pinned CompatibilityFallbackHandler.sol, lines 28–80, translates the selector and constructs SafeMessage; Safe.sol, lines 274–331, establishes that the candidate call precedes membership. Later Safe commit 77901a5a1ad835b74ad3b72f73a8412cfe491c57 added successful-status and exact-length checks as independent defense in depth. Zodiac's success && magic remains the root-cause repair for this incident.

6.2 Byte receipts pin two offset origins, the four-byte revert block, and the chain reproduction entry

The representative input is 871 bytes long. Zodiac reads outer r=0xce7d…5561, absolute s=0x244, and v=0 from complete msg.data, then hands the 0xc2-byte region between 0x244 and salt to the signer Safe. Safe starts a new origin inside that slice: slot one identifies candidate 0x5a779…97a35 with relative s=0x82 and v=0. Slot two contains the attacker EOA, but the threshold-one process ended when slot one reverted.

complete calldata origin:
  outer r = signer Safe 0xce7d…5561
  outer s = 0x244 → 0xc2-byte Safe signature slice
  salt    = ABI-encoded millisecond timestamp

Safe-slice origin:
  slot 0 r = candidate 0x5a779…97a35
  slot 0 s = 0x82 → length 0x20 || 32 zero bytes
  slot 1   = present, unprocessed at threshold 1

Fixed-block reads show threshold one and owners 0x25d21d6a390b41ae032121761f3f62b8f675713e, 0xe92ed76e5b853f246fd6dc8bef58339ed1fa05bd, 0xc75e0137aeb875b76f326481bdbf19877ce01c4d, and 0x1f778edc79aec4e17ea4daf8e2f438cec26a7d13. The candidate is absent from that set and the trace ends before GS026. The mechanism section uses that fact to correct the role; the final case boundary records the candidate role and the absence of acquired owner authority.

The malicious contract has no verified source. Its incident-block runtime recognizes ERC-1271 selectors 0x1626ba7e and legacy 0x20c13b0b. The terminal block forms 0x1626ba7e through PUSH4 0x0b135d3f → SHL 0xe1 → MSTORE → REVERT(0,4). Fixed-block eth_call and the attack trace both show failed status with the same error data. These machine facts classify the validator. Public evidence leaves authorship, source language, and reconnaissance method unknown.

Independent reproduction preserves raw input, block hash, implementation storage, and runtime; establishes the four-argument Delay ABI boundary; parses the Zodiac tail and Safe-relative offsets; recomputes ModuleTx and queue hashes under their actual formulas; joins item 11 creation to execution input; checks token logs and balances in the second receipt; and finally compares old checker, fixed checker, and legitimate signer on a fixed-block fork. Full raw input, runtime, and RPC JSON remain controlled case material. The public report supplies selectors, offsets, addresses, source lines, and transaction hashes for review.

Public evidence establishes the predicate, representative deployment, nested signature, queue/execute pairing, asset effect, and project restoration. The post-mortem does not enumerate every module replacement, cancellation, and authority-restoration transaction for every account, and the actor's account-selection method remains undisclosed. Operators complete those objects with their historical account graph, request logs, and chain receipts. A missing object remains unknown or pending instead of being inferred from exploit success.

7 Closing assessment: four bytes describe data; authorization must retain call status

The root cause is one verifiable Boolean expression. The affected Zodiac checker accepted magic in returnData while discarding success=false from the same call. The representative path persisted that result as Delay item 11, and a separate asset Safe completed MultiSend inside the executable window. Repair has equally explicit scope: deploy verifiable success && magic runtime, disable old modules, disposition old queues, and restore account state and user value.

The reusable engineering rule is compact. Whenever a low-level call supplies evidence for authorization, price, ownership, or execution, status and data form one indivisible invariant. Whenever a time system persists authorization, monitoring, independent cancellation authority, and enough response time jointly create its security value. Paired canaries, runtime inventory, and queue terminal states turn that rule from code-review advice into an accepted production control.

Gnosis restored user balances, supplying the financial endpoint for this event. Every other deployment remains governed by its own chain runtime, old queues, and account authority. A reader should close this report able to state four things precisely: which four objects connect, which predicate failed, why stored execution rights require separate disposition after a patch, and which evidence admits an account back to service.

Research record

8Evidence, objects, and sources

The material below preserves the identifiers and references used in this report.

8.1Research objects

Products, actors, techniques, affected objects, and control points discussed in the report.

Affected componentDelay v1.1.0 / @gnosis.pm/zodiac 3.4.1; affected checker lineage introduced in 3.4.0

The representative Delay inherited an ERC-1271 checker that discarded low-level call status

Current evaluation target@gnosis-guild/zodiac-core 4.3.0

Current stable npm version on July 27, 2026; production still requires local compatibility and runtime acceptance

Exploit contract0x5A77953CAa27eD4638F4DfdC665b8064D0e97A35

Gnosis Chain contract that reverts with the ERC-1271 magic value

Attacker EOA0x81BA8A2b895D30280bca199C2Ff75f3F058d4C6c

Deployed the malicious contract and called multiple Delays

Magic value0x1626ba7e

ERC-1271 isValidSignature(bytes32,bytes) success return value

Fix commit4cf15302228a578985b0af2a3e7000dcded2da6b

Restores the staticcall success condition

Regression commit9a9e38025ad7518f6e5350aeac673435a7db70f1

Legacy 3.4.0 change that ignored call status

Sample queue transaction0x5f562057e1963964fc3a6d8b105057f3ab9f9dd7091ff5bdd8d51453f51bd456

Created Delay queue item 11 at 08:29:05 UTC

Sample execution transaction0x4d3888cf3c68bffb2013001aabaf7b3596fc5d3f5d4cc5f139487d8a4140ae14

Executed the same MultiSend batch at 08:53:45 UTC

8.2Event chronology

  1. Legacy Zodiac 3.4.0 released

    The signature refactor entered a release with low-level call status ignored.

  2. Malicious ERC-1271 contract deployed

    The attacker EOA created the revert-magic contract on Gnosis Chain.

  3. First large transfer

    Gnosis monitoring observed the first large unauthorized transfer.

  4. Root cause confirmed

    The team located Zodiac ERC-1271 result handling and paused relevant services.

  5. Fix and regression committed

    The status check returned and a revert-magic signer test entered the repository.

  6. Public post-mortem current revision

    Gnosis recorded its current impact, restoration, and root-cause account.

  7. Current stable line reviewed

    The current stable npm version is @gnosis-guild/zodiac-core 4.3.0.

8.3Sources and material

  1. Gnosis incident post-mortemhttps://www.gnosis.io/blog/post-mortem-gnosis-pay-vulnerability-exploit
  2. EIP-1271 contract-signature standardhttps://eips.ethereum.org/EIPS/eip-1271
  3. Zodiac Core regression commit 9a9e380https://github.com/gnosisguild/zodiac-core/commit/9a9e38025ad7518f6e5350aeac673435a7db70f1
  4. Zodiac Core fix commit 4cf1530https://github.com/gnosisguild/zodiac-core/commit/4cf15302228a578985b0af2a3e7000dcded2da6b
  5. Revert-magic regression test b87fe79https://github.com/gnosisguild/zodiac-core/commit/b87fe79e66c9c5bc3c46f3018ea53ebddbed516b
  6. Representative Zodiac Delay v1.1.0 pinned sourcehttps://github.com/gnosisguild/zodiac-modifier-delay/blob/515737f67bb704d0ce14d45cd6f7cc8e6b4655d1/contracts/Delay.sol#L108-L220
  7. @gnosis-guild/zodiac-core 4.3.0 current stable packagehttps://www.npmjs.com/package/%40gnosis-guild%2Fzodiac-core/v/4.3.0
  8. Safe 1.4.1 core sourcehttps://github.com/safe-fndn/safe-smart-account/blob/bf943f80fec5ac647159d26161446ac5d716a294/contracts/Safe.sol
  9. Safe 1.4.1 signature decoder sourcehttps://github.com/safe-fndn/safe-smart-account/blob/bf943f80fec5ac647159d26161446ac5d716a294/contracts/common/SignatureDecoder.sol
  10. Historical Gnosis Pay Account Kit transaction and ERC-1271 encodinghttps://github.com/gnosispay/account-kit/blob/51f60789df3252c06aab963654a183f25abc6afd/src/entrypoints/accounts-actions/execute.ts#L43-L318
  11. Safe 1.4.1 compatibility fallback-handler pathhttps://github.com/safe-fndn/safe-smart-account/blob/bf943f80fec5ac647159d26161446ac5d716a294/contracts/handler/CompatibilityFallbackHandler.sol#L28-L80
  12. Safe4337Module v0.2.0 handler inheritancehttps://github.com/safe-global/safe-modules/blob/d5de32548c968533ace53f514fd8ce2d276c1a27/4337/contracts/Safe4337Module.sol#L1-L22
  13. Safe4337Module v0.2.0 deployment manifesthttps://github.com/safe-global/safe-modules-deployments/blob/e6b1e025b281d295224408ea8be54a89ea3dd060/src/assets/safe-4337-module/v0.2.0/safe-4337-module.json
  14. Safe post-incident defense-in-depth commit 77901a5https://github.com/safe-global/safe-smart-account/commit/77901a5a1ad835b74ad3b72f73a8412cfe491c57
  15. Safe non-owner reverting-contract regression testhttps://github.com/safe-global/safe-smart-account/blob/77901a5a1ad835b74ad3b72f73a8412cfe491c57/test/core/Safe.Signatures.spec.ts#L1123-L1145
  16. Malicious ERC-1271 contract and bytecodehttps://gnosis.blockscout.com/address/0x5A77953CAa27eD4638F4DfdC665b8064D0e97A35
  17. Malicious contract creation transactionhttps://gnosis.blockscout.com/tx/0x771a8040f3ae89fd28eecb96bbe1bfe452a868a09febe0368eb00a1105603ea2
  18. Representative queue transactionhttps://gnosis.blockscout.com/tx/0x5f562057e1963964fc3a6d8b105057f3ab9f9dd7091ff5bdd8d51453f51bd456
  19. Representative execution transactionhttps://gnosis.blockscout.com/tx/0x4d3888cf3c68bffb2013001aabaf7b3596fc5d3f5d4cc5f139487d8a4140ae14
  20. Representative signer/module Safehttps://gnosis.blockscout.com/address/0xce7d2f8e54e1104ed7a4c1f88dac63aa4a825561
  21. Representative asset/card Safehttps://gnosis.blockscout.com/address/0x0cae88383f2d1f308292e58cd71984bb6120e03d
  22. Safe smart-account signature documentationhttps://docs.safe.global/advanced/smart-account-signatures
  23. Solidity revert and error-data semanticshttps://docs.soliditylang.org/en/latest/control-structures.html#revert-statement