Share on XShare on LinkedInShare on Telegram
Hack Analysis

New Market Trading $3.78M SquidRouterModule Exploit (Explained)

New Market Trading lost $3.78M after a critical access control flaw let attackers drain 88 Safes across three chains using a preventable bug.

Author
QuillAudits Team
June 1, 2026
New Market Trading $3.78M SquidRouterModule Exploit (Explained)
Share on XShare on LinkedInShare on Telegram

On May 25, 2026, New Market Trading lost $3.78M when an attacker exploited a broken access control check in the protocol's custom Gnosis Safe module, SquidRouterModule. 88 user Safes were drained across Ethereum, Base, and Arbitrum in under 15 minutes.

No flash loan. No phishing. No bridge exploit. The attacker read a public contract, copied a string, encoded a real delegate address from an open on-chain registry, and called a function that had been left unguarded for three months. The vulnerability class, trusting payload data over msg.sender  is documented, well-understood, and entirely preventable.

Hack Analysis

The PermissionsManager contract storing each Safe's registered delegate address was fully public on-chain. The attacker read it directly to enumerate the real delegate per target Safe. No special access required, the keys to the check were sitting in plain sight.

For each target Safe, the attacker crafted a call payload encoding the real delegate address read from PermissionsManager, along with three chained actions: an APPROVE, a PERMIT2 authorization, and a SWAP targeting the relevant attacker-controlled pool. amountOutMin was set to 0 on every swap, no slippage protection needed when you own the pool.

Screenshot 2026-05-30 at 11.43.04 AM.png

The module verified the caller by checking:

1require(sourceAddress == squidRouter);
2

sourceAddress is a parameter the caller supplies themselves. It has no binding to msg.sender or any on-chain identity. The attacker passed the string "squidRouter". Check passed.

Screenshot 2026-05-30 at 11.51.29 AM.png

Screenshot 2026-05-30 at 11.49.55 AM.png

Screenshot 2026-05-30 at 11.49.40 AM.png

The module then ran a permission lookup:

1require(hasPermission(safe, delegate, APPROVE));
2

The delegate address fed into this check was decoded from the caller's own payload, not derived from msg.sender. The attacker encoded the real registered delegate. The registry returned a valid result. The check passed. The actual caller was never verified.

Screenshot 2026-05-30 at 11.52.40 AM.png

Screenshot 2026-05-30 at 11.53.10 AM.png

Screenshot 2026-05-30 at 11.53.43 AM.png

The single missing line that stops the entire attack:

1require(msg.sender == delegate);
2

With both checks cleared, the module executed all three actions on the victim Safe in sequence: approved the token balance, authorized Permit2, then forced a full-balance swap through the attacker's Uniswap V3 pool. Victims received the worthless "u" token. The attacker held the LP and collected the real assets on the other side of every trade.

Screenshot 2026-05-30 at 11.56.54 AM.png

After the drain phase, complete, the operator wallet removed liquidity from all pools and collected the real assets. Relay was used to consolidate proceeds cross-chain. Everything was swapped to DAI and swept into a single holding wallet. No direct transfer trail between victim and attacker. The protocol's own swap logic did the moving.

Screenshot 2026-05-30 at 11.55.36 AM.png

Screenshot 2026-05-30 at 11.30.06 AM.png

Screenshot 2026-05-30 at 11.54.45 AM.png

Root Cause

The root cause is a confused deputy vulnerability in SquidRouterModule. The module inherited expressExecuteWithToken() from Axelar's gateway interface, built for relayers, not for vault execution, and placed no additional access control behind it.

Screenshot 2026-05-30 at 11.35.38 AM.png

Two checks guarded the execution path. The first, sourceAddress == squidRouter, was a caller-supplied string with no cryptographic binding. The second, hasPermission(safe, delegate, APPROVE), ran a real registry lookup but sourced the delegate address from the caller's own payload rather than from msg.sender. An attacker who knew the real delegate, public data, readable by anyone, passed both checks trivially.

Screenshot 2026-05-30 at 11.35.06 AM.png

Screenshot 2026-05-30 at 11.36.08 AM.png

The contract verified that a valid delegate exists. It never verified that the caller is that delegate. The vulnerability is not a novel class. It is a documented confused deputy pattern. An audit would have caught it on the first pass.

Screenshot 2026-05-30 at 11.37.40 AM.png

How QuillAudits Could Have Prevented This

A security review of SquidRouterModule before V2 deployment would have caught the confused deputy flaw immediately. Any function executing privileged Safe actions must bind caller identity to msg.sender not to a value the caller supplies. The absence of require(msg.sender == delegate) is a critical access control failure that sits at the top of any structured audit checklist.

A review would also have flagged the misuse of expressExecuteWithToken(). The function skips gateway validation by design that is appropriate for bridge relayers, not for executing arbitrary actions on user wallets. Using it as a vault execution entry point without additional gatekeeping is a category error that a threat model surfaces before a line of code hits mainnet.

Funds Flow After Attack

The attacker did not transfer tokens directly. Each victim Safe was forced to swap its full balance through an attacker-controlled Uniswap V3 pool into the worthless "u" token. Victims received junk. The attacker, as sole LP, collected the real assets from every trade.

After the drain, the operator removed liquidity from all pools, used Relay for cross-chain consolidation, and swept everything to the holding wallet as DAI.

Screenshot 2026-05-30 at 11.30.06 AM.png

Post-Attack Mitigation

NMT reached out to the attacker via two on-chain messages offering a white-hat bounty, return 80% of drained funds.

Screenshot 2026-05-30 at 11.28.12 AM.png

Screenshot 2026-05-30 at 11.27.54 AM.png

Relevant Address and Transactions

Vulnerable Contract (Ethereum / Base / Arbitrum): 0x1f1d37a3Bf840e35c6a860c7C2dA71Fe555123ca

Attacker EOAs:

Attack Contracts:

Fake Token "u": 0xe6Ff0FE017D09D690493deC0F0f55E8f9Cdc3512

Sample Attack Transaction: 0x59d17fd31e31959b2d562508bf91c4fc1271682ba7d61a6209865e1151b69aea

Bounty Offer Transaction: 0xa6a2f41076ff55d090a197e6d27f5188eea7ac7511b96b66387e9589256df0b3

PermissionManager: 0x03B8B1bA6B02b8A566cB757DFa627f7198c44cB7

Conclusion

One missing require check made 88 user Safes drainable by anyone who read the source code. NMT's V2 module inherited a relayer function never designed for vault execution and sat exposed for three months. The confused deputy pattern is not novel, it has drained protocols before this one. An audit catches it. A threat model anticipates it. Neither was applied to the component that failed.

Contents

Tell Us About Your Project
Subscribe to Newsletter
hashing bits image
Loading...
cta-bg

WE SECURE EVERYTHING YOU BUILD.

From day-zero risk mapping to exchange-ready audits — QuillAudits helps projects grow with confidence. Smart contracts, dApps, infrastructure, compliance — secured end-to-end.

QuillAudits Logo


ISO 27001
DeFi Security AllianceplumeUniswap FoundationAethiropt-collectivePolygon SPNBNB Chain Kickstart

All Rights Reserved. © 2026. QuillAudits - LLC