New Market Trading SquidRouterModule Caller-Supplied delegate Permission-Spoof Safe-Module Drain (2026-05-25)

  • Name: New Market Trading SquidRouterModule caller-supplied delegate permission-spoof Safe-module drain
  • Date: 2026-05-25
  • Protocol: New Market Trading / SquidRouterModule
  • Chain: Base + Ethereum + Arbitrum
  • Category: account confusion / delegate-identity spoof / confused-deputy authorization failure / Safe-module drain
  • Loss: about $3.78M-$3.98M; public reconstructions count 88 victim Safes, 308 successful drain transactions, and a larger untouched exposure set
  • Summary: New Market Trading’s third-party Safe module let anyone impersonate an authorized offchain delegate because its privileged execution path checked permissions against a caller-supplied delegate field rather than msg.sender, and its sourceAddress == squidRouter gate was only a hard-coded string comparison. The attacker enumerated real delegates from NMT’s onchain PermissionsManager, then submitted forged payloads that made victim Safes approve tokens, authorize Permit2, and swap full balances into attacker-owned Uniswap v3 pools against worthless token u. After the forced swaps, the attacker removed liquidity, harvested the real assets, and consolidated the proceeds into DAI.
  • Trigger / entry point:
    • NMT exposed privileged Safe actions through a custom module commonly identified in public reporting as SquidRouterModule, with public incident reconstructions citing vulnerable deployment 0x1f1d37a3Bf840e35c6a860c7C2dA71Fe555123ca.
    • The module sat on top of Axelar’s express-execution pathway and was meant to let NMT’s offchain delegate bots execute swaps and approvals on user Safes.
    • Public writeups say the exploited path used the module’s DelegateBundler flow, with Axelar Express Execution serving as the externally reachable delivery path rather than the root cause.
    • The decisive checks were: a caller-supplied sourceAddress string that only needed to equal the constant squidRouter, and a permission lookup that authenticated the payload’s delegate address instead of the real caller.
  • Root cause:
    • NMT authenticated the wrong principal. It proved that a named delegate had permission for a Safe, but not that the transaction sender was that delegate.
    • This was a confused-deputy / root-of-trust substitution bug, not merely missing slippage checks or bad pool validation. The module let the attacker choose the identity object against which authorization was evaluated.
    • The sourceAddress == squidRouter check added no real authenticity because the expected value was a fixed string any caller could copy into calldata.
    • Once those two gates collapsed, the Safe module executed attacker-authored approval and swap bundles with full module authority on victim wallets.
  • Exploit path:
    1. The attacker monitored NMT’s public permission registries and identified Safes that had the module enabled plus active delegate permissions.
    2. They deployed exploit contracts, attacker-owned Uniswap v3 pools, and fake settlement token u (0xe6Ff0FE017D09D690493deC0F0f55E8f9Cdc3512) across Base, Ethereum, and Arbitrum before the drain started.
    3. For each victim Safe, the attacker called the module’s privileged bundling path and supplied a forged payload containing a real delegate address copied from PermissionsManager, an APPROVE action, a Permit2 authorization, and a SWAP action.
    4. The module compared the caller-supplied sourceAddress against the constant squidRouter, checked hasPermission(safe, delegate, ...) against the payload’s delegate address, and never required msg.sender == delegate.
    5. Because the copied delegate really was authorized, the module accepted the bundle and executed it on the victim Safe.
    6. The forced bundle first approved the victim’s token balances, then authorized Permit2, then swapped each Safe’s full balance through an attacker-owned Uniswap v3 pool with amountOutMin = 0, so the Safe received worthless u while the attacker controlled the real-asset side of the trade.
    7. After the drain phase, the attacker removed liquidity from the fake pools, collected the real assets, converted proceeds into DAI, and consolidated them into holding wallet 0xa447f71782135ab96a71374271a749ff7aa54859.
  • What the attacker needed to be true:
    • Victim Safes had to keep the third-party module enabled and retain live delegate permissions in NMT’s permission manager.
    • The module had to authenticate authorization against a caller-supplied delegate identity rather than the real caller.
    • The sourceAddress == squidRouter check had to remain a plain string comparison instead of a cryptographic or canonical-identity proof.
    • Victim bundles had to be allowed to execute approvals, Permit2 setup, and swaps in one module-authorized sequence.
    • The attacker needed enough liquidity-control over the fake pools to guarantee that the Safe’s real assets exited while the Safe only received junk output.
  • Technical anatomy worth remembering:
    • This was not a Safe core exploit and not a Squid core exploit. It was a third-party Safe module that shared Squid branding in explorer metadata and used Squid / Axelar integrations inside its own execution path.
    • The preserved invariant is: this delegate is authorized and this caller is that delegate are different claims. NMT proved the first and skipped the second.
    • The bug also shows how little protection a static string check provides once it is treated as message authenticity. sourceAddress == squidRouter was effectively a public password hard-coded in source.
    • The swap stage mattered because it turned authorization failure into immediate cash-out without requiring direct transfer() calls from the Safe to the attacker. The module’s own strategy / swap machinery performed the extraction.
    • The incident is a clean EVM Safe-module example of account confusion: the authorization root object was not a fake pool or fake market, but a forged delegate identity copied from real registry state.
    • It also preserves a useful approval-funded-route lesson: once the attacker could make the Safe approve and route funds inside one trusted bundle, attacker-owned liquidity plus amountOutMin = 0 was enough to turn wallet authority into deterministic extraction.
  • Representative transactions and addresses:
    • Sample attack transaction: 0x59d17fd31e31959b2d562508bf91c4fc1271682ba7d61a6209865e1151b69aea
    • Vulnerable Safe module cited in public analyses: 0x1f1d37a3Bf840e35c6a860c7C2dA71Fe555123ca
    • Main operator wallet: 0x7c82cb4b2909c50c7c0f2b696eee7565e0a23bb8
    • Co-operator / gas-funding wallet: 0x9bdc730183821b6bb2b51be30b77c964fa645b91
    • Additional exploit wallet: 0x7e54c729148a95bca651f3214ac9ebefd3fb1271
    • Additional exploit wallet: 0xc8ef4003d9db3863b9af26afcf2275378bfa83e4
    • Consolidation wallet: 0xa447f71782135ab96a71374271a749ff7aa54859
    • Fake token u: 0xe6Ff0FE017D09D690493deC0F0f55E8f9Cdc3512
    • Example attacker contracts highlighted in incident reporting: Ethereum 0xfac7459683cdb9b6f367b42eedfebd745dc8760c, Ethereum 0xe1d5fcfbba4d46f4937de369de415dd7e2d3265a, Base 0x2d450322e3526f489afcc8c49923b35d355c70bc, Arbitrum 0x35b94f725bfdc1d00ef15df99dafadf6ecf3a0e9
  • Response / mitigation:
    • Blockaid detected the drain while it was in progress and publicly flagged the exploit, after which a substantial set of exposed Safes remained untouched.
    • Squid publicly clarified that the vulnerable contract was not part of Squid’s own protocol or deployment set, despite the SquidRouterModule explorer name.
    • Safe leadership likewise clarified that only Safes that had manually enabled this third-party module were affected.
    • NMT later published a bounty offer asking the attacker to return 90% of funds and keep 10%, but public reporting said no technical postmortem had been published by the time the incident writeups landed.

Pattern tags

  • account-confusion-root-of-trust-substitution
  • The module authenticated a real delegate copied from registry state while never proving the caller was that delegate, so the trusted root object was counterfeit even though the permission lookup itself returned true
  • sourceAddress == squidRouter acted as a public shared secret, not as message provenance or route authenticity
  • The attacker did not need to steal Safe owner keys or compromise Safe core contracts; they only needed the module to confuse authorized delegate exists with this caller is that delegate
  • Approval, Permit2, and swap execution were bundled tightly enough that one principal-confusion bug could immediately cash out through attacker-owned liquidity rather than waiting for a separate withdrawal path

Why the exploit worked

  • NMT’s module began from the wrong trust anchor. It treated a caller-supplied delegate value as the principal whose authorization mattered, then asked the permission registry whether that named address was trusted. That is a real lookup over real state, but it is still attacker-authored reality if the caller gets to choose the identity being checked.
  • The supposed source-authentication gate was equally non-binding. A constant string such as squidRouter can label an expected route, but it cannot prove that a real Squid-controlled process authored or relayed the message. Once the module promoted that string into security-critical provenance, the authentication boundary had already collapsed.
  • The bundle shape amplified the damage. A forged delegate identity did not only authorize one swap; it unlocked an APPROVE -> Permit2 -> SWAP pipeline that could reconfigure spend authority and settle the drain in one module-executed sequence.
  • amountOutMin = 0 and attacker-owned Uniswap v3 pools finished the extraction deterministically. After the module accepted the counterfeit delegate, the Safe no longer needed to transfer funds directly to the attacker; the trusted route machinery itself sold real assets into worthless output and let the attacker harvest value by removing liquidity afterward.
  • The branding confusion around SquidRouterModule added an operational lesson too: explorer names and integration labels are weak provenance. They helped route attention to Squid even though the code, deployment, and operational control belonged elsewhere.

Detection and audit pivots

  • Start by asking which runtime object actually proves this caller is the authorized delegate for this Safe right now. In this incident, reviewers could have found the bug by tracing the privileged module path until they saw authorization evaluated against a payload field rather than msg.sender, a recovered signature, or a protocol-authenticated relayer identity.
  • Inventory every caller-supplied identity field that can cross a trust boundary inside wallet modules: delegate, owner, relayer, sourceAddress, route tags, integration labels, and similar metadata. Any one of those becomes a live confused-deputy surface the moment the module treats it as provenance rather than description.
  • Reconstruct the full authority ladder, not only the first bad permission check. For NMT-style modules the meaningful chain is copied delegate identity → module authorizes bundle → Safe approves token / Permit2 → swap route spends through attacker-controlled liquidity. The exploit only became a cash drain because each later stage inherited authority from the counterfeit principal without re-authenticating it.
  • Audit bundled wallet actions as one spend surface when they can both create approval state and consume it immediately. A module that can approve tokens, install Permit2 rights, and execute swaps inside one trusted frame should be reviewed like a settlement engine, not like a harmless convenience adapter.
  • Watch for provenance theater in integrations. Static strings, explorer names, and brand-shaped route labels often survive code review because they look like integration checks, but they should trigger the opposite question: what cryptographic or code-identity proof is actually binding this action to the claimed upstream actor?
  • During incident response and pre-incident readiness, keep a live inventory of which third-party modules are enabled across user Safes, who deployed them, who can upgrade them, and which permissions managers or settlement integrations they depend on. This case spread partly because the affected surface was a custom module riding on top of a familiar brand stack, which delayed clean attribution.

Failure-prevention checklist

  • Bind delegated authority to a caller-bound proof. A module that checks whether delegate is authorized must also prove that msg.sender, a recovered signer, or a trusted relayer attestation corresponds to that delegate for this exact bundle.
  • Treat route labels, chain names, sourceAddress strings, and explorer metadata as descriptive metadata only. If provenance matters, require a cryptographic or protocol-owned identity proof rather than a public constant any caller can copy.
  • Keep approval creation and approval consumption on a short leash. If a single privileged path can both install Permit2 / token approvals and immediately spend them, require one tightly scoped reviewed bundle with explicit asset, spender, route, and slippage bounds.
  • Bind swap authority to an exact settlement envelope. Once a delegated module is authorized to trade, it should still prove the permitted tokens, exact router / pool family, recipient, minimum output, and replay scope instead of accepting an open-ended cash-out path.
  • Inventory third-party Safe modules by blast radius rather than by branding. A module that is not part of Safe core can still be a protocol-scale custody surface if many wallets enable it.
  • Pre-stage response controls for module incidents: module disablement guidance, approval / Permit2 revocation campaigns, per-safe or per-module spend ceilings, and fast communication templates that clarify ownership of the vulnerable code path.

Audit prompts

  • Which field defines the acting principal in this module path, who writes that field, and what exact proof binds it to the real caller?
  • Can an attacker copy a genuinely authorized delegate, relayer, or signer identity out of registry state and pass authorization checks without controlling that identity?
  • Does any sourceAddress, route tag, integration label, or string constant get promoted into a provenance check even though it is caller-supplied?
  • Once a delegated bundle is admitted, can that same path create approvals or Permit2 authority and then immediately consume them before any human or protocol boundary re-checks the action?
  • If the privileged path performs swaps, what exact facts constrain counterparty selection, slippage, output asset, and recipient, and are those facts bound to the reviewed authorization envelope?
  • What is the blast radius of one compromised or mis-authenticated module: one Safe, one strategy, one asset subset, or every wallet that enabled the module?
  • During incident triage, can responders quickly answer who deployed this module, who controls upgrades, which wallets enabled it, and which external integrations it touches, or would branding confusion slow containment again?

Lessons that should already be standard

  • Authorization checks over attacker-chosen identities are security theater. This address is authorized somewhere is not the same claim as this caller is that address.
  • Public metadata is not provenance. A hard-coded string such as squidRouter, an explorer label, or an integration brand name cannot authenticate who really authored a wallet-moving action.
  • Safe modules are custody infrastructure. If a module can approve, route, or swap user balances, it deserves the same threat model as a wallet core contract.
  • Bundling approval setup and settlement in one privileged lane turns small principal-confusion bugs into immediate cash-out incidents.
  • Third-party integration surfaces amplify attribution risk during response. Teams need clean ownership maps before an incident, not while social media is arguing about whose module it was.

Why this incident matters

  • It preserves a high-signal authorization checks read real registry state but still authenticate the wrong actor example for Safe-module and delegated-wallet designs.
  • It shows how a non-custodial wealth layer can still become a protocol-scale single point of failure once one privileged module is enabled across many user wallets.
  • It highlights the difference between transport and trust: Axelar express execution provided delivery reachability, but the exploit only existed because NMT failed to bind delivery to a real delegate principal.
  • It also highlights a recurring product-risk pattern: a V1 audit that predates a new privileged module says little about the risk of the V2 architecture that actually holds production authority.

Internal linkages

  • Core recurring pattern: account-confusion-root-of-trust-substitution — the module accepted a counterfeit principal as the root object that defined authorization truth for the rest of the bundle.
  • Root-authenticity companion: root-object-authenticity-and-trust-anchor-recomputation-discipline — the sharper design question is what canonical principal or message-authorship object the module should have recomputed or proven before any caller-supplied delegate field was allowed to define reality.
  • Approval / route-design companion: approval-funded-route-authority-truth-security-object — once the module could authenticate the wrong delegate, the swap bundle also lacked any healthy route-local object binding payer authority, approved spend, and acceptable settlement counterparty into one reviewed action.
  • Adjacent pattern: arbitrary-call-token-approval-drain — the cash-out path used approvals, Permit2, and attacker-controlled swap routes, but the sharper failure sat one layer earlier in delegate-identity authentication.
  • Adjacent execution-integrity companion: execution-envelope-identity-pinning — useful when the follow-up question is how a delegated module should have kept who authored this action, what exact bundle is authorized, and which route may execute it fused into one machine-checkable envelope.
  • Adjacent operator-topology companion: operator-topology-map-and-effective-threshold-discipline — useful when the sharper question is how many third-party operators, bots, and module installers effectively sat above user wallets even though custody was marketed as non-custodial.

Control surface

  • The leverage sat in NMT’s custom Safe module, its caller-supplied delegate field, the meaningless sourceAddress == squidRouter authenticity check, and the bundled APPROVE -> Permit2 -> SWAP path that could move a Safe’s full balance once the module accepted the forged principal.
  • The attacker did not need to compromise a Safe owner key, phish a signature, or break Squid / Safe core contracts. They only needed the module to confuse known authorized delegate with actual caller.
  • The real trust boundary was not does some delegate have permission for this Safe? but what exact principal and route-authorship object is allowed to exercise this wallet authority right now?

Sources

Last reviewed

  • 2026-07-30 UTC