September 2026 Web3 Hack Recap: $766M Lost in the Worst Month of 2026 ## TL;DR September 2026 was the worst month of the year for crypto security. The most widely cited tally counted 55 major incidents and $766.5 million in losses. That is up about 462 percent from August and above April's previous 2026 high. About $270 million came back, almost all of it Bitcoin returned after the Liquid Network exploit, which leaves roughly $495 million unrecovered. Two incidents made up 92 percent of the total: - **Bitget ($387.5 million):** Attackers spent more than three weeks inside third-party security products before they reached the exchange's wallet job server. They then fed spoofed withdrawal data into a signing pipeline that did exactly what it was told. The money left in under three hours, and no private key was ever touched. - **Liquid Network (about $320 million):** A patch for an eight-year-old cache bug joined variable-length fields together with nothing marking where one ended and the next began. The attacker used the resulting collision to create about 4,000 unbacked L-BTC and withdraw real Bitcoin 36 minutes later. The smaller incidents followed the same pattern: - **Neutron:** A governance proposal carried by about $20,000 of NTRN took admin control of eleven Astroport, Drop and Neutron contracts. - **Safe module:** A custom module trusted a router, and the router trusted any call that pointed back at itself. - **Limit Break:** Its immutable Payment Processor V2 trusted any forwarder contract, and anyone could deploy one. None of these attacks broke an authorization check. Each one fed false input to a step that was working as designed. September's lesson is that everything feeding a signer, a cache, a vote or a forwarder is part of the security boundary. [Get your protocol audited before you become next month's headline →](https://app.cecuro.ai/auth?mode=signup) --- ## The Numbers That Define September 2026 September broke the pattern of the summer. In August, many attackers each took a little. In September, two took nearly everything. **The totals:** - **Most widely cited tally:** 55 major incidents and $766.5 million lost, up from $136.3 million in August. - **Broader count, including phishing and smaller cases:** about $768 million across 97 incidents, with about $270 million returned or frozen. - **DeFi-only count:** $742 million, falling to $348 million once centralized exchange and gambling platform losses are removed. As usual, the gap between these figures comes from what each tracker counts, not from disagreement about the facts. We use $766.5 million as the headline. **How September compares:** - It is the worst month of 2026, above April's roughly $647 million on the same tally. - It takes the year-to-date total on that tally to about $2.14 billion. Broader counts put the year past $2.6 billion. **Concentration.** Bitget and Liquid together account for about $707 million. Without them, the other 53 incidents total roughly $59 million, a lighter month than most of 2026. That does not mean the long tail was harmless. It means September's damage came from two failures that each sat one layer below anything a typical smart contract audit would examine. **Category.** One tracker's breakdown puts about $13 million in the DeFi category. Centralized exchanges and chain-level failures account for more than $700 million. By attack type: - $387.5 million came through a compromised third-party service. - About $325 million came through invalid proofs or signatures that a chain accepted. - Classic code vulnerabilities in individual contracts were a rounding error by comparison. **Corrections to early reporting.** Several widely shared figures moved during the month: - **D'CENT:** First reported as about 2 million XRP from roughly 1,550 wallets. The drain kept going for ten days and reached about 12.4 million XRP from 7,393 wallets. - **Neutron:** The $9.4 million figure in early coverage is a notional value. The official post-mortem puts withdrawals at about $6.24 million and the net loss at about $2.23 million after recovery. The widely repeated claim that two attacker wallets cast 98 percent of the YES votes is also wrong: the second wallet was unrelated. - **Limit Break Payment Processor V2:** Estimates range from about $1.8 million to $6.6 million, depending on whether NFTs later rescued by a whitehat are counted as lost. --- ## The Big Story: Bitget and the Signer That Trusted Its Inputs **Loss:** $387.5 million (initially reported as $351.6 million) **Date:** September 24, 2026 **Attack type:** Third-party zero-day leading to compromise of the wallet job server, then spoofed withdrawal data sent into the legitimate signing pipeline Bitget's own description is the most important sentence of the month: "The attacker compromised a critical backend system within our wallet infrastructure, used it to spoof transaction data, and triggered our authorization process to move funds out." Private keys were never compromised. Bitget's signers signed every one of the theft transactions themselves. ### How the attackers got in Bitget's forensic investigators reconstructed the intrusion. It started on August 31, more than three weeks before any funds moved: 1. **August 31:** A zero-day in a third-party product, which Bitget has called only "Product A", let the attacker read environment variables and a database password, and then connect to the database. 2. **September 24, 16:07 UTC:** A second vendor system, "Product B", was accessed with an internal employee identity. The attacker injected system commands into its task parameters. 3. **From there:** The attacker planted a web shell on Product B, opened a command-and-control channel, and moved into the production wallet job server, where they deployed malicious packages. 4. **The final tool:** A custom tool, later recovered from deleted files, forged withdrawal parameters. Logs also show attempts to edit withdrawal records, plus two forged Bitcoin withdrawals that failed. Bitget has not named either vendor and no CVE has been published. Bitget CEO Gracy Chen declined to name the vendor, citing security risk. Reporting describes the products as third-party security products. In other words, the tools deployed to protect the network were the way in. ### The drain (all times UTC, September 24) | Time | Event | | --- | --- | | 18:31 | Small test transfers of TRX and ETH | | 18:58 | First large transfer: $34.75 million in USDT | | 19:01 | $87.6 million leaves across five chains in 15 seconds | | 19:05 | Bitget's reconciliation system flags the discrepancy and automatically blocks withdrawals | | 19:16 | $202.8 million leaves across five chains in nine seconds | | 20:40 | Remaining hot-wallet balances are moved to cold storage | | 21:23 | Last theft transfer | | 21:44 | Withdrawal service and signing machines shut down | The most important gap in that table is between 19:05 and 19:16. The reconciliation system detected the problem and blocked withdrawals. Eleven minutes later, the largest burst of the attack went out anyway. The block did not cover the path the attacker was using. Signing continued for about two hours and eighteen minutes after detection. ### What was taken, and where it went An independent trace counted 23 theft transfers across nine chains. The largest pieces: - 102.98 million XRP (about $157.8 million) - About $126.5 million on Ethereum mainnet - 18,917 ZEC (about $29.4 million) - Smaller amounts in USDT0 on Arbitrum, AVAX, ETH on Optimism and Base, BNB and TRX The theft transactions looked different from normal traffic. They used fixed, round gas limits of 100,000, 200,000 and 50 million, while ordinary customer withdrawals used about 63,000. The Zcash thefts used 1,074 and 956 inputs, against a historical maximum of 54 for Bitget's hot wallet. A policy engine checking each transaction against what normal withdrawals look like would have had obvious signals to stop on. Laundering was fast: - About $75.5 million of stablecoins and 3,000 XAUt were swapped into ETH or AVAX within 41 minutes. Swapping out of stablecoins first removes the chance of an issuer freeze. - About $269 million moved through THORChain across 7,804 transactions. All of the XRP had gone through THORChain by September 27. - Smaller amounts went through Chainflip, Circle's CCTP, USDT0, Across and Stargate. - Part of the ZEC moved into a shielded pool. **Frozen funds.** Tether and Circle froze about $318,000 on one address in the first hours. Including NEAR Intents, which froze about $503,000 mid-swap, total frozen funds reached roughly $1.1 million by October 1. **Attribution.** Elliptic said laundering patterns and on-chain overlaps are "suggestive" of the group commonly tracked as TraderTraitor. Chen called the attack "consistent with techniques used by DPRK-linked hacker groups." No government attribution had been published as of October 2. **Bitget's response:** - It covered the full loss from its User Protection Fund, which held $464 million before the attack. - It rebuilt the fund to $309 million with its own capital by September 30. - It published a 131 percent reserve ratio snapshot on September 29. - It reopened Bitcoin withdrawals on September 28, ETH on September 29 and USDT on September 30. - It offered a 5 percent bounty. Chen has said publicly that Bitget is "not expecting to recover a lot of funds." ### Why this is the same failure as Bybit The pattern is the one that cost Bybit about $1.5 billion in February 2025. There, a compromised wallet interface showed signers a benign transaction while they signed a malicious one. At Bitget there was no interface to fool, because the signing pipeline was automated. It received a request from a system it trusted, and it signed. Neither case required breaking cryptography. Both required only control over what the signer was told. ```go // ❌ VULNERABLE: the signer trusts whatever the wallet job server sends. // Compromise the job server and you control every signature. func (s *Signer) SignWithdrawal(req WithdrawalRequest) ([]byte, error) { if !s.jobServer.Authenticated(req) { return nil, ErrUnauthorized } return s.hsm.Sign(req.RawTx) } ``` ```go // ✅ HARDENED: the signer checks each request against an independent // record, enforces its own limits, and halts itself when something is wrong. func (s *Signer) SignWithdrawal(req WithdrawalRequest) ([]byte, error) { if s.halted.Load() { return nil, ErrHalted // a kill switch at the signer, not in front of it } // 1. Every outbound transfer must match a customer withdrawal in the // ledger, read over a channel the job server cannot write to. w, err := s.ledger.Withdrawal(req.WithdrawalID) if err != nil || w.Amount.Cmp(req.Amount) != 0 || w.Destination != req.Destination { s.halt("request does not match ledger") return nil, ErrMismatch } // 2. Velocity limits are enforced inside the signer, per asset and per window. if s.window.Add(req.Asset, req.USDValue) > s.limits[req.Asset] { s.halt("velocity limit exceeded") return nil, ErrVelocity } // 3. Shape checks: reject transactions that do not look like customer // withdrawals (unusual gas limits, abnormal input counts). if req.GasLimit != s.expectedGas[req.Chain] || len(req.Inputs) > s.maxInputs[req.Chain] { s.halt("transaction shape anomaly") return nil, ErrShape } return s.hsm.Sign(req.RawTx) } ``` **Key lesson:** A key held securely is not the same as funds held securely. Every system that can put a request in front of a signer, including every vendor product on the same network, is part of the signing system. The defenses that would have limited Bitget's loss all sit at the signer: - An independent record to check each request against - Velocity limits enforced by the signer itself - Checks on what a normal transaction looks like - A kill switch that stops signing, not only the customer-facing withdrawal flow Detection at 19:05 was fast. The kill switch was in the wrong place. --- ## Liquid Network: The Fix That Became the Bug **Loss:** About 3,996 BTC withdrawn (about $320 million). 3,400 BTC was returned; about 598 to 602 BTC (about $47 million) is still outstanding. **Date:** September 6, 2026 **Attack type:** Cache key collision in Elements rangeproof validation, allowing unbacked L-BTC to be created and withdrawn as real BTC ### Background Liquid is a federated Bitcoin sidechain run on the Elements codebase. Its token, L-BTC, is meant to be backed one to one by Bitcoin held by the federation. Liquid hides transaction amounts using *confidential transactions*. Each amount is replaced with a cryptographic commitment, and each output carries a *rangeproof*: a proof that the hidden amount is valid, so nobody can create value from nothing. Verifying rangeproofs is expensive, so Elements caches the result. A cache hit tells the node "this proof was already verified" and skips the real check. That cache is where the attack happened, and it involved two separate bugs. ### Bug A: a cache key missing fields (2018 to 2026) In 2018, Elements PR #335 shortened the rangeproof cache key to `SHA256(salt || rangeproof || value_commitment)`. The key left out the asset commitment and the scriptPubKey, even though both are part of what a rangeproof is checked against. As a result, a cached "valid" result could be reused in a different context. - **August 2, 2026:** Researcher stutxo reported Bug A through Blockstream's security mailbox. The report proposed two fixes: plain concatenation of the missing fields, or serialization with length prefixes. - **August 7:** An independent team, Bitcoin Red Team, confirmed the bug. - **August 3 to 11:** Blockstream chose plain concatenation and deployed it to bridge nodes on August 3. By August 11, 13 of 15 functionaries were running it. - **September 1:** The fix was merged into the public Elements repository. ### Bug B: the fix itself The fix wrote four fields into the hash one after another: 1. The proof, which has a variable length 2. The value commitment (33 bytes) 3. The asset commitment (33 bytes) 4. The scriptPubKey, which also has a variable length Nothing in the hash input recorded where the proof ended or where the script began. The proof does carry its own internal length field, but that field is only checked during full verification. A cache hit skips full verification. The attacker reused the asset commitment and the last byte of the destination script from a legitimate transaction whose result was already cached. They then built a longer proof, a modified value commitment and a shorter script that together produced exactly the same bytes as that cached entry. The node found a matching cache key, returned "valid", and never checked the forged rangeproof. In Blockstream's words, "the fabricated rangeproof never needed to actually verify." The per-process random salt did not help. The bytes being hashed were identical, so the key matched on every node that had cached the original entry. ```cpp // ❌ VULNERABLE (the August 2026 fix): fields joined back to back. // A longer proof plus a shorter script can produce the same bytes // as a legitimate entry, and a cache hit returns "valid" immediately. hasher.Write(proof.data(), proof.size()) .Write(commitment.data(), commitment.size()) .Write(asset_commitment.data(), asset_commitment.size()) .Write(scriptPubKey.data(), scriptPubKey.size()) .Finalize(entry.begin()); // later, in VerifyRangeProof: if (rangeProofCache.Get(entry, !store)) return true; // verification skipped ``` ```cpp // ✅ HARDENED (Elements v23.3.4, PR #1600): serializing through CHashWriter // adds a length prefix to every variable-length field, so two different // sets of inputs can no longer produce the same cache key. CHashWriter hasher = m_salted_hasher_range_proof; hasher << proof << commitment << asset_commitment << script_pub_key; entry = hasher.GetSHA256(); ``` The final fix also added fields that were missing entirely from a second cache, the one for surjection proofs. It added a startup flag to disable the rangeproof cache, and 164 lines of tests, including one that splits the same bytes two different ways to confirm they produce different keys. Blockstream's post-mortem notes a useful contrast. Bitcoin Core caches signature results using a similar concatenation, but it is not vulnerable, because the embedded length prefixes are checked before the cache is consulted. The same pattern is safe or unsafe depending on the order of the checks. ### From forged block to real Bitcoin in 36 minutes (all times UTC, September 6) | Time | Event | | --- | --- | | 13:53:10 | Forged transaction confirmed in Liquid block 4,050,336 | | 14:00 | 2.5 L-BTC test withdrawal through SideSwap succeeds | | 14:05 | Attacker deposits about 4,000 L-BTC to SideSwap's peg-out service | | 14:10 | Blockstream monitoring flags a stall in block production | | 14:28:56 | Federation releases 3,996.02 BTC; SideSwap forwards it to the attacker in the same Bitcoin block | | 18:26 | Bridge nodes halted | | 18:30 | Attacker posts an on-chain message: "we are whitehats. contact us on chain." | **How the BTC got out.** SideSwap is a federation member that holds a *PAK*, a peg-out authorization key that lets it withdraw L-BTC to Bitcoin. SideSwap's key was not compromised. The federation's nodes had already accepted the forged L-BTC as valid, so the peg-out request looked legitimate. SideSwap later acknowledged three operational choices that turned a consensus bug into an immediate $320 million withdrawal: - Its peg-out signing key was kept online, against the Federation Charter. - Payouts were forwarded to customers automatically, in the same Bitcoin block as the peg-out. - There was no size limit, velocity control or wallet-history check on peg-out orders. A request to withdraw roughly 95 percent of the reserve went through with no review. Blockstream's own description of Liquid's design is direct: "There is no separate, off-chain reserve check at peg-out time." ### The negotiation On September 7 at 16:09 UTC, the attacker returned 3,400 BTC. Two days later they demanded more: "You SHALL pay 10% using your own money as bug bounty or you will cause all your holders a 15% loss for your irresponsibility and stinginess." Blockstream refused on September 11: "Blockstream will not pay a ransom… It is not white-hat activity. It is theft." The attacker kept roughly 15 percent of what they took. ### Recovery Elements v23.3.4 was published on September 9. Block production resumed that night with a replacement block 4,050,336, and user transactions returned on September 10. Peg-outs were still suspended at month end. In the meantime: - An external audit of the new release has started. - PAK entries are being replaced, and all receiving keys must move to cold storage. - On September 10, reserves stood at about 3,601 BTC against 4,229 L-BTC, roughly 85 percent. - Blockstream CEO Adam Back has said the one-to-one peg will be covered. - USDt and other Liquid assets were not affected, but could not move while the chain was paused. **Key lesson:** Three lessons, each worth a line in your threat model. 1. **A cache inside validation code is part of the validation logic.** A cache hit is a verification result, so its key must include every input the verification depended on, encoded so that two different inputs can never produce the same key. 2. **A patch is new code.** Here the fix introduced the bug. It passed both manual and AI-assisted review, which were scoped to confirm that the right fields had been added, not whether the way they were joined could be forged. Blockstream now scans every change merged into a release branch, and reviews consensus-critical code on a schedule even when it has not changed. That is the job [Ozone by Cecuro](https://ozone.cecuro.ai/login) is built for: it learns how a codebase works, then reviews every pull request against that model and flags exploitable bugs before they merge. 3. **Every bridge needs a check that does not depend on the chain being right.** A peg-out against a reserve-sized amount, from a wallet with no history, forwarded in the same block, is exactly the event an independent reserve check and velocity limit should stop. This is the third month in a row that this bug class has caused a seven- to nine-figure loss: Wanchain's signed message in July, Injective's market identifier in August, and Liquid's cache key in September. In Solidity, the equivalent is hashing more than one variable-length field with `abi.encodePacked` instead of `abi.encode`. --- ## Neutron: Governance Bought for $20,000 **Loss:** About $6.24 million withdrawn, of which about $4.01 million has been secured, for a net loss of about $2.23 million (official post-mortem). Early tracker figures of $9.3 to $9.4 million are notional values. **Date:** September 22, 2026 **Attack type:** Governance takeover through an expedited proposal that changed contract admins ### Why governance could take over these contracts Neutron had moved from its earlier DAO-based governance to the standard Cosmos governance module (x/gov) in mid-2026, as it entered what its team called long-term maintenance mode. CosmWasm contracts on Neutron are managed by wasmd. In upstream wasmd, the governance module is authorized to change the admin of any contract, whatever admin or multisig the protocol itself configured. ```go // wasmd x/wasm/keeper/authz_policy.go (upstream, by design) // Governance can modify any contract, ignoring its configured admin. func (p GovAuthorizationPolicy) CanModifyContract(sdk.AccAddress, sdk.AccAddress) bool { return true } ``` That made governance the real admin key for every protocol on the chain, whatever multisig each protocol thought it had. ### The proposal Proposal #9 was titled "AIATO: AI Agent Takeover. Phase 1: Agent Admin Registration." - **How it was presented:** A "live testnet experiment" stress-testing an AI agent governance framework, citing a real academic paper and claiming outside funding. - **What it actually did:** It contained 11 `MsgUpdateAdmin` messages handing control of mainnet contracts to the attacker: eight Astroport contracts, two Drop contracts and a Neutron investor vesting contract. - **The rules it ran under:** It was marked expedited, which meant a 3-day voting period, a 67 percent threshold, 30 percent quorum and no timelock. Messages would execute the moment it passed. The attacker had tried twice before. Proposal #5 was rejected in August. Proposal #7 was withdrawn on September 19 at 02:10 UTC while losing 5.28 million votes to 49.01 million. Fourteen minutes later the attacker submitted Proposal #9. It drew only 8.09 million NO votes. Most of the voters who had defeated #7 did not come back for its replacement. ### The vote - **Buying the votes:** A fresh wallet used 20,199 USDC to buy 31.63 million NTRN on Astroport in five swaps. The USDC had been routed through Secret Network's private token and Noble. - **Timing:** At 02:13:10 UTC on September 22, eleven minutes before voting closed, it delegated 31.62 million NTRN to a validator. Under x/gov, voting power is counted when the vote ends, so stake added at the last minute counts in full. - **Result:** That stake was 84.7 percent of all YES votes. Final turnout was 33.3 percent against a 30 percent quorum. Without the purchased stake, the proposal would have failed quorum. At the post-mortem's NTRN price, the entire bonded stake securing Neutron governance, attacker included, was worth around $110,000. The contracts it could reassign held millions. ### The drain The attacker uploaded the draining contract code at 01:18 UTC, an hour before the vote closed, labelled as a "migration test vault". Between 02:38 and 03:04 UTC they migrated the contracts to that code and emptied them, 26 minutes in total. The drained positions included: - Astroport's staking contract (370.3 million ASTRO) - Astroport's USDC/dATOM, axlUSDC/USDC, USDC/DYDX, wstETH/axlWETH, ATOM/dATOM and USDC/NTRN pools - Drop's dATOM-to-ATOM converter (1.59 million ATOM and 735,337 dATOM) - Drop's withdrawal manager (115,878 ATOM) - The vesting contract (76.22 million NTRN) ### The response: two chains, two halts - **Neutron halted** at 07:43 UTC on September 22 and resumed on September 25. Its recovery release (v11.3.0): - Restored all 11 contracts and their admins - Removed the purchased stake and locked both attacker accounts - Clawed funds back to a validator multisig - Froze new delegations - Temporarily limited governance to software-upgrade and text proposals - **The Cosmos Hub halted** for about 24.5 hours. Its upgrade, Gaia v28.3.0, moved 1,227,121 ATOM from the attacker's Hub address to a 4-of-6 multisig held by six ecosystem operators. The Hub itself was not exploited. - **What escaped:** Neutron's IBC rate limiter kept 1.67 million USDC on the chain. About 1.18 million USDC left through Noble and CCTP. A collection wallet holding about 794 ETH had not moved at the time of the post-mortem. The validators who wrote the post-mortem put it plainly: "This was not a bug in Astroport, Drop, CosmWasm or Neutron's consensus." ```solidity // ❌ VULNERABLE: voting power is read when the vote is tallied, so stake // bought minutes before the deadline counts in full; admin-changing actions // can ride the expedited path; execution is immediate. function _votingPower(address voter, uint256) internal view returns (uint256) { return staking.bondedBalance(voter); // current balance, not historical } function execute(uint256 id) external { require(_passed(id), "not passed"); _executeAll(proposals[id].actions); // no timelock } ``` ```solidity // ✅ HARDENED: voting power is fixed when the proposal is created; privileged // actions cannot be expedited and must wait out a timelock; an absolute // YES floor stops a low-turnout vote from moving admin rights. function propose(Action[] calldata actions, bool expedited) external returns (uint256 id) { bool privileged = _touchesAdminOrFunds(actions); require(!(expedited && privileged), "privileged actions cannot be expedited"); id = _create(actions, expedited, privileged); proposals[id].snapshot = block.number - 1; // stake added after this does not count } function _votingPower(address voter, uint256 id) internal view returns (uint256) { return token.getPastVotes(voter, proposals[id].snapshot); } function execute(uint256 id) external { Proposal storage p = proposals[id]; require(_passed(id) && p.forVotes >= MIN_ABSOLUTE_YES, "not passed"); uint256 delay = p.privileged ? PRIVILEGED_TIMELOCK : STANDARD_TIMELOCK; require(block.timestamp >= p.votingEnds + delay, "timelock"); _executeAll(p.actions); } ``` **Key lesson:** If chain governance can override your multisig, governance is your admin key, and its price is whatever the governance token costs that week. Attacks on governance keep getting cheaper: - **July:** BonkDAO cost the attacker $4.4 million. - **August:** Term Finance cost about $1,000. - **September:** Neutron cost about $20,000, and the same month a dormant Yam Finance DAO was taken over through its quorum. A chain in maintenance mode is governance with nobody watching. Map every path that can change a contract's admin, including the chain's own governance module. Compare the cost of buying a vote with the value that vote controls, and make sure stake bought during a vote cannot decide it. [Audit your governance and admin paths with Cecuro →](https://app.cecuro.ai/auth?mode=signup) --- ## Contract-Level Failures: Callers That Should Not Have Been Trusted September's three notable smart contract exploits had the same root cause. In each one, a contract accepted an identity it never verified. ### Safe Module and rsETH: A Router That Trusted Itself **Loss:** 2,900 rsETH (about $7.8 million gross); the proceeds appear to have been returned **Date:** September 15, 2026, 04:38 UTC **Attack type:** A self-referencing multicall that passed authorization, chained into a Safe module that runs DELEGATECALL **The victim** was a private 2-of-3 Safe holding about 53,400 aEthrsETH, Aave's wrapped form of Kelp DAO's restaking token. The owners had enabled a custom strategy module on the Safe that executed DeFi "recipes", and the module trusted a router contract to call it. A Safe module can execute transactions without owner signatures, so the 2-of-3 threshold did not apply to anything the module did. **The router** had a function `multicall(address _contract, bytes[] _data)`. Its authorization check returned `true` whenever `_contract` was the router's own address. The attack used that rule twice: 1. The outer call targeted the router itself, so it passed the check automatically. 2. Inside it, the router called itself again. That inner call reached the module with the router as the caller, and the module trusted the router. The module then called `execTransactionFromModuleReturnData` with operation `1`, which is DELEGATECALL, into a recipe executor. Running inside the Safe's own storage context, the recipe withdrew 2,900 rsETH from Aave. It sent the rsETH through a Uniswap v4 pool the attacker had prepared in advance, with a custom hook that unwrapped it. **The front-run.** The attacker sent the exploit through the public mempool. An MEV bot named "Yoink" copied the transaction, landed it first in the same block, and paid the block builder 18.93 ETH. Yoink's address received 2,882 rsETH. Kelp DAO placed a 24-hour wallet-level pause on that address at 06:03 UTC. **Where the funds ended up.** On September 16, on-chain data shows the rsETH moving from Yoink's address to a Safe that is itself one of the victim Safe's owners. One tracker lists the funds as returned. No bounty has been publicly confirmed. Several later outlets reported no return, apparently because the funds went back to a related address rather than the original Safe. ```solidity // ❌ VULNERABLE (simplified reconstruction): authorization looks at the // call's target instead of its caller, and anything routed back through // the router is treated as already authorized. function multicall(address _contract, bytes[] calldata _data) external { require(_isAuthorized(_contract), "unauthorized"); for (uint256 i; i < _data.length; i++) { (bool ok, ) = _contract.call(_data[i]); require(ok, "call failed"); } } function _isAuthorized(address _contract) internal view returns (bool) { if (_contract == address(this) || msg.sender == address(this)) return true; return isOperator[msg.sender]; } // In the Safe module: any call from the router counts as owner intent. function executeRecipe(bytes calldata recipe) external { require(msg.sender == ROUTER, "only router"); safe.execTransactionFromModuleReturnData(RECIPE_EXECUTOR, 0, recipe, Enum.Operation.DelegateCall); } ``` ```solidity // ✅ HARDENED: authorize the caller, refuse self-calls, and pin what the // module is allowed to do inside the Safe. function multicall(address target, bytes[] calldata data) external nonReentrant { require(isOperator[msg.sender], "unauthorized caller"); require(target != address(this), "no self-calls"); require(isAllowedTarget[target], "target not allowlisted"); for (uint256 i; i < data.length; i++) { (bool ok, ) = target.call(data[i]); require(ok, "call failed"); } } // In the module: check the original operator, not the router, and validate // every recipe action against an allowlist before running DELEGATECALL. function executeRecipe(address operator, bytes calldata recipe, bytes calldata operatorSig) external { require(msg.sender == ROUTER, "only router"); require(isSafeOwner(operator) && _verify(operator, recipe, operatorSig), "operator did not sign"); require(_actionsAllowlisted(recipe), "action not allowlisted"); safe.execTransactionFromModuleReturnData(RECIPE_EXECUTOR, 0, recipe, Enum.Operation.DelegateCall); } ``` **Key lesson:** A Safe module goes around the owner threshold completely. Enabling a module gives full authority over the wallet to that module, to every contract the module trusts, and to every contract those contracts trust. A 2-of-3 multisig with a permissive module is a 0-of-3 multisig. Custom modules and their routers need the same depth of audit as the wallet they plug into. rsETH also featured in April's [$292 million Kelp bridge exploit](/blog/kelp-dao-292m-exploit), which came down to trusting a single verifier. Different code, same question: who does this contract trust, and who do they trust? ### Limit Break Payment Processor V2: Any Forwarder Was Trusted **Loss:** Estimates range from $1.8 million to $6.6 million. About 660 WETH (about $1.7 million) was drained through forced purchases, plus NFTs. A whitehat rescued 23,155 NFTs worth about $5.7 million. **Date:** September 24 to 26, 2026 **Attack type:** Sender spoofing through ERC-2771 meta-transactions, using a "trusted" forwarder anyone could create, against users' leftover approvals **Background.** Payment Processor V2 is an immutable, unpausable NFT settlement contract from Limit Break. Magic Eden routed its Ethereum trades through it from February to October 2024 and closed its EVM marketplace on March 9, 2026. Users' `setApprovalForAll` approvals to the contract outlived both. **What went wrong.** Payment Processor V2 supports *ERC-2771 meta-transactions*. If a call arrives from a "trusted forwarder", the contract reads the real sender from the last 20 bytes of the calldata. The flaw was in what counted as trusted: - Limit Break's public `TrustedForwarderFactory` let anyone create a forwarder, and marked every forwarder it created as trusted. - A forwarder created with its app signer set to `address(0)` would forward any call with no signature at all, appending whatever sender address its creator chose. So the attacker could make the contract believe any user was the caller. **What the attacker did with it:** - **Taking NFTs.** The attacker wrote offer orders in a victim's name and filled them at a price of zero. The victim's old approval did the transfer. The zero price was the visible symptom; the real bug was the spoofed sender. - **Taking WETH.** With the victim spoofed as the buyer, `buyListing` made victims pay WETH for worthless NFTs listed by the attacker. **Timeline:** 1. **September 24, 13:07 UTC:** The first wave of 305 zero-price sales took Meebits, Otherdeeds, World of Women and Desperate ApeWives. 2. **Within a day:** Once the technique was visible on-chain, dozens of new forwarders appeared and copycats joined in. On-chain data shows tens of thousands of zero-price sales on Ethereum alone. 3. **The whitehat rescue:** Yuga Labs' 0xQuit used the same technique to move exposed NFTs into a rescue wallet and set up a claim site for owners. **Statements:** - **Magic Eden** said it had stopped using the contract in October 2024 and that "no live Magic Eden listings were impacted." - **Limit Break** paused V3, which shared the flaw, everywhere except ApeChain. It was direct about V2: "Payment Processor V2 is an immutable contract that nobody can fix, so users should not wait for a remedy but revoke their V2 and V3 approvals now." This is not a new bug class. In December 2023, OpenZeppelin and thirdweb disclosed an arbitrary address spoofing flaw in contracts that combined ERC-2771 with Multicall, and attackers were already using it against live tokens that same week. ```solidity // ❌ VULNERABLE: any forwarder created by the factory counts as trusted, // and anyone can call the factory. The last 20 bytes of calldata become // the sender, with no signature from that sender. function _msgSender() internal view returns (address sender) { if (factory.isTrustedForwarder(msg.sender) && msg.data.length >= 20) { assembly { sender := shr(96, calldataload(sub(calldatasize(), 20))) } } else { sender = msg.sender; } } ``` ```solidity // ✅ HARDENED: a fixed forwarder set controlled by the protocol, and // forwarders that must verify the user's signature over the exact call // before passing on their identity. mapping(address => bool) private _trustedForwarders; // set by governance, never by a public factory function _msgSender() internal view returns (address sender) { if (_trustedForwarders[msg.sender] && msg.data.length >= 20) { assembly { sender := shr(96, calldataload(sub(calldatasize(), 20))) } } else { sender = msg.sender; } } // In the forwarder: function execute(ForwardRequest calldata req, bytes calldata sig) external { require(ECDSA.recover(_hashTypedData(req), sig) == req.from, "bad signature"); require(nonces[req.from]++ == req.nonce, "bad nonce"); (bool ok, ) = req.to.call{gas: req.gas}(abi.encodePacked(req.data, req.from)); require(ok, "forward failed"); } ``` **Key lesson:** A trusted forwarder can impersonate any user, so trusting a forwarder means trusting it with every user's identity. If anyone can create a trusted forwarder, every user can be impersonated. The second lesson is about deprecated contracts. An immutable contract with live user approvals stays exposed until every approval is revoked, so a plan to retire a contract has to include an approval revocation campaign. Revoking a listing or bumping a nonce does not remove an operator approval. ### Nostra Finance: An Oracle Median With Only Two Sources **Loss:** About $3.5 million **Date:** September 17, 2026 **Attack type:** Oracle manipulation of an illiquid collateral token on Starknet Nostra priced its own governance token, NSTR, through Pragma. Pragma's risk reporting classed NSTR as one of six critical-risk feeds. Its three nominal sources were not independent, and one of them priced NSTR from whichever pool was ranked highest on a market data aggregator. The attack was prepared months in advance: the borrowing wallet had interacted with NSTR contracts in March and August. On the day (all times UTC, September 17): | Time | Event | | --- | --- | | 05:23 | Attacker creates an NSTR/SolvBTC pool with about 1.5 SolvBTC of one-sided liquidity, which becomes the top-ranked pool | | 05:27 to 05:47 | Wash trading in the pool | | 05:47 to 05:48 | NSTR's price spikes from about $0.006 to $49.50, roughly 8,000x | | 05:48 to 05:50 | Attacker posts NSTR as collateral and borrows | | 05:51 to 07:08 | Proceeds dumped and bridged out | Only two sources returned a price. With an even number of inputs, the median calculation averaged the honest price and the manipulated one, so the manipulated price went straight through. Pragma's own assessment was: "An enforced three-source minimum would have rejected it." The attacker borrowed about $3.5 million in ETH, STRK, USDC, USDT, WBTC and DAI, roughly six times NSTR's entire market capitalization. About $1.92 million was bridged to Ethereum, and Nostra's TVL fell from about $4 million to about $710,000. Pragma said the attacker's address had been frozen and that recovery was its priority. Two weeks earlier, a Pragma USDT feed on Vesu briefly priced USDT at about $2.04 because too few sources remained after stale ones were filtered out. That caused about $3.08 million of liquidations. Both incidents came from the same oracle provider, with the same root cause. ```solidity // ❌ VULNERABLE: a "median" over whatever sources happened to respond. // With two sources, one honest and one manipulated, the result is their average. function aggregate(uint256[] memory prices) internal pure returns (uint256) { _sort(prices); uint256 n = prices.length; return n % 2 == 1 ? prices[n / 2] : (prices[n / 2 - 1] + prices[n / 2]) / 2; } ``` ```solidity // ✅ HARDENED: require a minimum number of independent sources, reject // prices when the sources disagree widely, and never let an oracle price // alone decide that an asset is safe collateral. uint256 constant MIN_SOURCES = 3; uint256 constant MAX_SPREAD_BPS = 500; // 5% function aggregate(uint256[] memory prices) internal pure returns (uint256) { require(prices.length >= MIN_SOURCES, "insufficient sources"); _sort(prices); uint256 lo = prices[0]; uint256 hi = prices[prices.length - 1]; require((hi - lo) * 10_000 / lo <= MAX_SPREAD_BPS, "sources disagree"); return prices[prices.length / 2]; } // And at the lending layer: a per-collateral debt ceiling sized to the // liquidity that could actually absorb a liquidation. ``` **Key lesson:** Pragma said it directly: "NSTR should not be treated as safe collateral simply because an oracle price is available." This is the fourth large illiquid-collateral exploit in five weeks, after Tectonic and Moonwell in August. Never price collateral from liquidity an attacker can create. Require at least three independent sources. Cap borrowing against each asset at what its real market depth could cover. --- ## Keys, Wallets and Signing Infrastructure Bitget aside, September's key and wallet losses split into two familiar groups: operational keys that leaked, and wallet software that generated keys badly. **D'CENT App Wallet (about 12.4 million XRP from 7,393 wallets, September 15 to 25)** The first wave emptied 1,682 XRPL wallets of about 3.62 million XRP in about 3 hours and 20 minutes. The drain then continued in five more waves over ten days. The attacker also deleted 6,095 accounts to reclaim their reserves ([XRPL.to](https://xrpl.to/insights/dcent-app-wallet-drain-timeline)). - **Value:** Trackers value the loss at about $6 million. Pricing the full XRP count puts it closer to $18 to $20 million. - **Who was affected:** Users of app versions older than 8.1.0, released November 5, 2025, who had entered their recovery phrase into the App Wallet and signed transactions with it. D'CENT's hardware wallets were not affected unless the same phrase had been entered into the app. - **Root cause:** D'CENT has not disclosed it. Researchers suspect weak randomness in key generation, and D'CENT warns that "reusing an existing recovery phrase may regenerate the same or a related key." - **A telling detail:** The attacker worked through wallets in order of creation date, not balance, which suggests a key list built in advance. - **This is the third wallet key-generation incident in three months,** after Coldcard in July and RRWallet in August. **Duelbits (about $6 million to $7 million, September 24)** Hot wallets on Ethereum, BNB Chain, Tron and Bitcoin were drained about nine hours before Bitget's first theft transfer. The haul included 836 ETH, about 593,000 USDT, 12.4 billion SHIB, 209 BNB and 8.1 BTC. Investigators suspect a private key compromise, which Duelbits has not confirmed. It is the platform's second key compromise, after a $4.6 million incident in February 2024. Duelbits covered user balances and reopened on September 27. **SingularityNET, Fetch.ai and NuNet (about $2.3 million realized, September 19 to 20)** A cloud breach at SingularityNET exposed a bridge signing key, and a separate NuNet minting key was also stolen. About $16.8 million was moved on paper, but only about $2.3 million was realized, including $1.53 million of FET and $463,000 from NuNet. The bridges were turned off. **Other key and wallet cases:** - **An unidentified victim ($4.3 million, September 22):** More than 145 Ethereum addresses plus Tron and Bitcoin wallets were drained in a single key compromise. - **XRPH Wallet (about $452,000 from 4,011 wallets, September 3):** A staking feature sent users' seed phrases to a remote server. - **Dominion ($238,000 realized, September 10 to 11):** Three of five treasury multisig keys were compromised. **Phishing** fell sharply. One tracker counted $6.2 million across 13 incidents, down from $41.5 million in August. **Key lesson:** Hot wallets should hold operating float and nothing more. Outflow alerts need to fire in seconds, not hours. Key-generation code needs a periodic audit of its randomness source, not a one-time sign-off. Wallet vendors also need a migration plan ready before they need it: D'CENT's users were still being drained ten days after the first wave. --- ## Bridges, ZK Verifiers and Chain-Level Mints - **Meter Passport (about $2.3 million in unbacked MTRG, September 23 to 24):** A block-validation flaw let the attacker mint MTRG with nothing behind it. MTRG fell 75 to 88 percent, and the chain was paused. - **Payy Network (about $1.9 million in USDC, September 24):** The bridge's ZK verifier accepted a forged burn proof. Payy ruled out a key compromise and halted the network. ZK circuits are code, and the public inputs a proof commits to need the same scrutiny as any authorization check. - **Symbiosis (about $336,000 realized, September 10 to 11):** The attacker minted billions of unbacked syBTC but cashed out only a small amount. About 15 BTC was recovered, and a 20 percent bounty offer expired unanswered on September 13. - **Chainflip ($736,000 in USDT on Tron, September 12):** A Tron memo was treated as a new swap, which paid out duplicates across eight rounds. Chainflip said users will be made whole. - **Nomic nBTC ($3.15 million, discovered September 9):** A flaw in custom forwarding logic let the attacker double-spend nBTC. That left Osmosis's alloyed BTC about 36 percent unbacked. Osmosis froze 22.65 BTC and plans to cover the rest from its community pool. The exploit itself dates to June 25, which is why some trackers include it in September and others do not. - **MultiversX (no reported loss, September 19 to 24):** A virtual machine atomicity bug pushed the chain into an invalid state. Validators halted the chain and froze the attacker's accounts. --- ## The Long Tail September's smaller contract exploits clustered around three themes: legacy code that was still live, arithmetic and accounting slips, and price sources that were easy to manipulate. | Protocol | Date | Loss | Exploit Type | Chain(s) | | --- | --- | --- | --- | --- | | Notional V2 | Sep 3 to 4 | ~$1.73M | fCash mint overflow from a signed-to-unsigned cast | Ethereum | | Flamincome | Sep 16 | ~$346K net | Anyone could call `stakeFor`, inflating strategy value | Ethereum | | Rocket | Sep 5 | ~$287K | Self-trading on a dormant perp market | Not disclosed | | Hemi Genesis Drop | Sep 7 | ~$255K | Reentrancy in a claim contract, with 63 recursive claims | Hemi | | SKYDAO | Sep 30 | ~$183K drained | Burn and sync on the sell path left near-zero reserves | BNB Chain | | Secured Finance | Sep 5 | $104K to $180K | Same-block self-trade to manipulate price | Ethereum, Arbitrum, Filecoin | | Cozy V2 | Sep 7 | ~$160K | Unverified oracle trigger allowed unbacked redemptions | Optimism | | Zentra | Sep 9 | ~$140K | `repayWithATokens` cleared debt without burning | Citrea | | Yam Finance | Sep 12 | ~$121K | Dormant DAO quorum takeover drained old farms | Ethereum | | BeatXswap | Sep 9 | $64K to $78K | `slot0` spot price used with no TWAP | BNB Chain | | Nimiq | Sep 16 | ~$50K | Gasless relay forwarder skipped signature check | Polygon | | ether.fi legacy queue | Sep 9 to 11 | 15.45 ETH | Deprecated withdrawal queue missing a solver check; users reimbursed | Ethereum | | GaslessReservoirEnabler | Sep 21 | ~$24K (466 victims) | Whitelisted module ran unvalidated `transferFrom` against approvals | EVM | Three of these point at September's main themes: - **Nimiq** is a small version of the Limit Break bug. A forwarder that skipped signature verification let the attacker act as someone else. - **GaslessReservoirEnabler and ether.fi** are the same story as Payment Processor V2: standing approvals plus an executor that does not check who is asking. - **Notional V2, Yam's old farms, ether.fi's legacy queue and Payment Processor V2** were all legacy or dormant code, and each one still had live funds or live approvals. Notional's bug class is worth a line of code, because it shows up again and again: ```solidity // ❌ VULNERABLE: a negative int256 becomes an enormous uint256. uint256 amount = uint256(fCashAmount); // ✅ HARDENED: revert on any value that cannot be represented. uint256 amount = SafeCast.toUint256(fCashAmount); ``` --- ## Follow-Ups From August Several of August's incidents changed materially in September, including one figure from our [August recap](/blog/august-2026-web3-hack-recap) that needs correcting. | Incident | September Update | | --- | --- | | Tectonic / Cronos | Cronos's post-mortem (September 8) gives the official figures: $120.4 million borrowed, $111.2 million reversed by the rollback, and $9.19 million not recovered, across 10,961 rolled-back blocks. This replaces the early estimates of about $74 million extracted and $6 million escaped. Every transaction in the 1 hour 54 minute window was reversed, whether or not it was related to the exploit. No compensation plan has been announced for the unrecovered amount. | | Harmony | Later analysis found far more forged ONE than the initial 4 billion, through cross-shard receipts that could be replayed. Exchanges reconciled a 6.58 billion ONE shortfall. On September 6, Harmony proposed shutting down its L1 and moving ONE to Ethereum: "The threats posed by state actors and AI agents are too great." That would make it the second L1 to propose shutting down after an exploit in less than a month, after BounceBit. | | Moonwell | Proposal MIP-X66 would use reserves the protocol owns to recapitalize the USDC market. The attack was funded with 800 ETH from Tornado Cash. Nothing has been recovered. | | Cosmos EVM | The advisory was published as Critical with no CVE. TAC's foundation is replacing 1.258 billion sold TAC from its treasury and moved its BNB Chain token to a new contract that excludes the attacker's holdings. | | Aquifer | The attacker ignored the signed on-chain whitehat offer, and the September 3 deadline passed. | | More Markets / Ankr | The Flow Foundation and Ankr will replace the drained WFLOW. Both products remain paused until Ankr ships a fix. | | Ontology | No loss was confirmed. Mainnet resumed on September 2. The root cause has not been disclosed. | | Coldcard | No new sweep wave was confirmed. Laundering began through THORChain and CoinJoin, and estimates of the total stolen were revised to about 1,806 BTC. | --- ## Attack Pattern Analysis: What September 2026 Tells Us | Attack Pattern | Notable Incidents | Approx. Losses | | --- | --- | --- | | Signing pipeline and infrastructure compromise | Bitget | ~$387.5M | | Chain-level validation, mint and verifier flaws | Liquid, Meter, Payy, Nomic, Symbiosis, MultiversX | ~$328M gross, ~$55M after the Liquid return | | Keys and wallet software | Duelbits, D'CENT, SingularityNET/Fetch.ai, unidentified $4.3M victim, XRPH, Dominion | ~$20M at tracker valuations, up to ~$34M counting D'CENT's full XRP total | | Trusted-caller and access control flaws | Safe module (rsETH), Payment Processor V2, Nimiq, ether.fi legacy queue, GaslessReservoirEnabler | ~$10M to $15M gross, most of the rsETH returned | | Governance capture | Neutron (Astroport, Drop), Yam Finance | ~$6.4M withdrawn, ~$2.4M net | | Oracle and thin-liquidity manipulation | Nostra, Rocket, Secured Finance, BeatXswap | ~$4M | ### Pattern 1: The Authorization Path Was Fed, Not Bypassed In almost every large September incident, the system that approved the theft worked exactly as designed: - **Bitget:** The signers signed. - **Liquid:** The federation honored a peg-out that consensus had accepted. - **Neutron:** Governance executed a proposal that met quorum. - **Safe module:** The module trusted the router. - **Payment Processor V2:** The contract trusted a forwarder. The attack surface was whatever fed each of those systems its inputs. Reviewing only whether the approval step is correct is no longer enough. Security review has to follow every input back to where it came from. ### Pattern 2: Ambiguous Encoding, for the Third Month Running Wanchain in July, Injective in August and Liquid in September all came from the same mistake: joining variable-length fields with nothing marking where each one ends, and treating the result as a unique identifier. Liquid adds a new lesson: the bug was introduced by a fix. A patch is new code, and it needs adversarial review of how its fields are combined, not just a check that the right fields are present. ### Pattern 3: Governance Keeps Getting Cheaper Neutron's attacker paid about $20,000 for 84.7 percent of the YES votes, on a chain whose whole bonded stake was worth about $110,000. In the same month, Yam Finance's dormant DAO was taken over through its quorum. Governance in maintenance mode is still governance, and anyone can buy it. ### Pattern 4: Deprecated Is Not Decommissioned Payment Processor V2, ether.fi's legacy queue, Notional V2 and Yam's old farms were all no longer central to their teams' products, and all still had live funds or live approvals. Shutting down the front end does not shut down the contract. Retiring a contract properly means draining its funds, revoking its approvals and, where possible, removing its permissions. ### Pattern 5: Validators Are Now the Incident Response The validators of a growing number of chains have stepped in to contain exploits: - **August:** Cronos rolled back its chain. - **September:** The Cosmos Hub moved stolen ATOM to a multisig in a chain upgrade. - **September:** Neutron clawed back funds and locked the attacker's accounts. - **September:** Liquid replaced a block. - **September:** MultiversX and Meter halted their chains. These interventions reduced losses. They also show that on many chains, finality is settled by validator agreement after the fact. Protocols cannot plan their security around the hope that validators will step in. --- ## What Would Have Prevented These Attacks **For signing pipeline compromise (Bitget):** - Have the signer check every request against an independent record before it signs. - Enforce velocity limits inside the signer. - Check each transaction against what a normal withdrawal looks like, including gas limits and input counts. - Put a kill switch at the signer, not only in the customer-facing flow. - Treat vendor products with network access to the wallet infrastructure as part of it. [Audit your smart contracts and signing assumptions with Cecuro →](https://app.cecuro.ai/auth?mode=signup) **For validation and encoding flaws (Liquid, and the chain-level mints):** - Build cache keys and identifiers from an encoding where two different inputs can never produce the same bytes, and fuzz them to prove it. - Review every patch adversarially, as new code. - Run a reserve check at the bridge that is independent of the chain's own state. - Apply velocity limits and manual review to peg-outs above a fixed share of reserves. **For governance capture (Neutron, Yam):** - Count voting power from a snapshot taken when the proposal is created. - Do not let proposals that change admins or move funds use the expedited path. - Require timelocks on privileged actions. - Set an absolute floor on YES votes. - Map every path, including the chain's own governance module, that can change a contract's admin. **For trusted-caller flaws (Safe module, Payment Processor V2, Nimiq):** - Authorize the caller, never the target. - Never treat a call back into the same contract as already authorized. - Use a fixed set of forwarders, each of which verifies the user's signature. - Allowlist what a Safe module can do through DELEGATECALL, and audit modules as part of the wallet. **For illiquid collateral and oracles (Nostra):** - Require at least three independent price sources, and reject prices when they disagree widely. - Set debt ceilings sized to real market depth. - Do not accept a protocol's own thinly traded token as collateral. **For keys and wallets (Duelbits, D'CENT, SingularityNET):** - Keep hot wallets to operating float. - Make outflow alerts fire in seconds. - Keep bridge and mint keys out of general cloud environments. - Audit the randomness used for key generation on a regular schedule. --- ## What This Means for Protocol Builders September's two largest incidents came in through vendor products and a cache key, not through anyone's smart contracts. The smart contract incidents it did produce all made one mistake: they trusted an identity they never verified. Both points lead to the same conclusion. The security boundary is not the contract. It is every system that can produce an input the contract, the signer or the chain will accept as authoritative. Modern protocol security works in three layers. **Layer 1: Pre-deployment auditing.** A full audit should cover: - Code correctness - Authorization logic that checks the caller and not the target - Meta-transaction and forwarder trust - Module and router permissions - Governance paths that can change admins - Encoding injectivity for every identifier, signed message and cache key - Oracle source requirements That is where the Safe module's self-call, Payment Processor's open forwarder factory, Neutron's expedited admin path, Nostra's two-source median and Liquid's concatenated cache key should have been caught. An audit is a snapshot, though, and September's largest on-chain loss came from a patch. Every change after the audit is new code. Ozone, built on the same engine as Cecuro's audits, reviews every commit and pull request to smart contracts and the backend services around them, and comments with severity, impact and a suggested fix before merge. **Layer 2: Continuous monitoring.** Detection has to happen in real time, and it has to trigger automated containment. Examples of what to watch for: - Signer output that does not match ledger intent - Withdrawal bursts with unusual gas limits or input counts - Peg-outs that are large relative to reserves - Governance proposals containing admin changes - Last-minute delegations that swing a vote - Collateral assets moving thousands of times in minutes Bitget detected its attack in 34 minutes and still lost most of the money afterward. Detection that does not stop the signer comes too late. **Layer 3: Operational security.** This layer covers: - Vendor products with network access to wallet infrastructure - Key storage, and keeping peg-out and bridge keys offline - Hot-wallet float limits - Randomness audits for any software that generates keys - Revoking approvals to retired contracts This layer produced September's largest single loss. Here is how Cecuro compares with the traditional model. | Dimension | Traditional Audit | Cecuro | | --- | --- | --- | | Turnaround | 2 to 6 weeks | Hours, typical | | Cost | $20K to $100K+ | From $799, about 90% less | | Coverage | Often single-chain focus | All chains and smart contract languages | | Technology | Manual, schedule-limited | AI-powered, multi-agent analysis | | Detection | Varies by reviewer | #1 on EVMBench | | Patch review | Usually a new engagement, weeks out | Every pull request, reviewed by Ozone on the same engine | Cecuro's AI-powered platform analyzes smart contracts across all chains and languages in hours, not weeks. Pricing starts at $799, roughly 90 percent less than traditional approaches, without trading away depth. Cecuro ranks first on EVMBench, the independent smart contract exploit benchmark. Our analysis checks for the patterns that defined September 2026: - Caller versus target authorization - Forwarder and module trust - Governance admin paths - Encoding injectivity - Oracle source requirements And because an audit is a snapshot, Ozone carries the same engine forward to every pull request, Web2 and Web3. Liquid's history shows why that matters. Ozone starts free with a $100 credit, valid for 30 days, no card required. [Start your audit today →](https://app.cecuro.ai/auth?mode=signup) [Get started free with Ozone →](https://ozone.cecuro.ai/login) --- ## Looking Ahead: October 2026 Four threads carry into October. **Bitget's funds are still moving.** About $342 million remained under attacker control as of September 29, including more than 3,000 BTC and a 10,000 ETH wallet that has not yet moved. Watch THORChain and the Zcash shielded pool. **Liquid's peg-outs are still closed.** The external audit of Elements v23.3.4 and the replacement of PAK entries will determine when L-BTC holders can withdraw again, and how the roughly 15 percent reserve gap is closed. **Neutron's recovery needs two more votes.** Returning the ATOM held in the Hub multisig needs a Cosmos Hub proposal followed by a Neutron proposal to send funds back to the contracts. **The trusted-caller pattern is still active.** On October 1, an attacker used a fake Safe to pass the access check of a custom Aave V3 leveraged-loop adapter, taking about $305,000 from two linked Safes. Aave's core contracts were not involved. That is the rsETH lesson again, two weeks later. The protocols that stay out of next month's recap will be the ones that treat every input to a trusted step as untrusted until it is verified, review every patch as new code, and know exactly who can change their admin keys. September 2026 cost the industry about $766 million. --- The Cecuro Security Team publishes monthly hack recaps to keep the community informed about emerging threats and prevention strategies. For real-time smart contract auditing and continuous protocol monitoring, visit [app.cecuro.ai](https://app.cecuro.ai).