Dough Finance ConnectorDeleverageParaswap flashloanReq() Unvalidated ParaSwap Calldata / Approval Drain (2024-07-12)
-
Name: Dough Finance ConnectorDeleverageParaswap
flashloanReq()unvalidated ParaSwap calldata / approval drain -
Date: 2024-07-12
-
Protocol: Dough Finance
-
Chain: Ethereum
-
Category: router approval abuse / arbitrary external call / token-approval drain / leveraged-position unwind abuse
-
Loss: about
$2.1M-$2.55Mgross across the exploit wave, including roughly210,629 USDCand795.25 ETHin Dough’s postmortem, with about76.2 ETHlater recovered by whitehat frontrunners and the main attacker keeping roughly$1.87M -
Summary: Dough exposed a public flash-loan-assisted deleverage path in
ConnectorDeleverageParaswapthat trusted attacker-shaped ParaSwap calldata deep inside the unwind loop. The attacker first used a Balancer flash loan to repay a victim Dough DSA’s Aave debt, which made the victim’s collateral withdrawable through Dough’s owndeloopflow. In the same execution path, the connector then forwarded a second attacker-crafted ParaSwap payload through a raw external call, and that payload simply invokedWETH.transferFrom(...)to move the freshly withdrawn collateral out of the victim DSA and into attacker-controlled accounts. The protocol did not lose funds because ParaSwap itself failed; it lost funds because a public connector with spender authority over user DSAs accepted arbitrary downstream calldata as if it were a legitimate swap instruction. -
Trigger / entry point:
- Public analyses and Dough’s incident report identify
ConnectorDeleverageParaswapas the key exploited connector, with the vulnerable contract highlighted by CertiK at0x9f54e8eAa9658316bB8006e03fff1Cb191AaFBe6. - The public entry point was
flashloanReq(), which let callers kick off a Balancer-flash-loan-powered deleverage sequence. - Dough’s own postmortem later said the same
flashLoanReqpattern existed across four connectors and thatmsg.senderDSA checks were added afterward because the flash-loan context had been too public. - Inside the deleverage chain, attacker-controlled ParaSwap bytes eventually reached a low-level external call rather than a tightly validated swap adapter.
- Public analyses and Dough’s incident report identify
-
Root cause:
- Dough combined three dangerous assumptions in one path: public flash-loan orchestration, delegated asset-moving authority over user DSAs, and unvalidated downstream swap calldata.
- The intended invariant was something like
this public deleverage helper may only execute the exact unwind and swap steps needed for the targeted user's position; the deployed code instead let attacker-supplied ParaSwap payloads survive deep enough to become an arbitrary call surface. - Because the connector already had the authority it needed to unwind collateral from Dough DSAs, a malicious payload did not need to break Aave or Balancer. It only needed to redirect the last leg from
perform intended swaptocall token.transferFrom(). - This was therefore not a classic protocol-TVL drain and not a direct EOA-approval phishing event. It was an approval-drain variant aimed at user-owned Dough proxy accounts whose collateral-management permissions had already been delegated to the connector.
-
Exploit path:
- The attacker funded EOA
0x67104175fc5fabbdb5A1876c3914e04B94c71741and deployed attack contract0x11A8DC866C5d03ff06bb74565b6575537B215978. - In the first exploit transaction
0x92cdcc732eebf47200ea56123716e337f6ef7d5ad714a2295794fdc6031ebb2e, the attacker took a Balancer flash loan and used it to repay about938,566 USDCof Aave debt for victim Dough DSA0x534a3bb1ecb886ce9e7632e33d97bf22f838d085. - With that debt temporarily cleared, the attacker invoked Dough’s
flashloanReq()/ deleverage flow so the victim DSA’s WETH collateral became withdrawable through Dough’s own unwind logic. - CertiK’s trace describes the attacker-crafted
paraswapCallDataas two segments: the first segment nudged the deloop path far enough to withdraw collateral, while the second segment targeted the WETH contract directly and encodedtransferFrom(...). - Because Dough eventually forwarded that payload through a raw external call inside the deloop chain, the connector executed the malicious token call rather than a constrained swap. Roughly
596.844648055377423623 WETHfrom the first victim DSA moved to the attacker in that first transaction. - The attacker swapped proceeds back into USDC, repaid the Balancer flash loan, and kept the spread.
- Dough and CertiK say the attacker repeated the same pattern across seven additional Dough DSA accounts using follow-on exploit transactions
0x28a934816ed0371a3fbb2f89986eecf87a22937c40309ff2f58697792215b4da,0xf356d1b05d26de824baddf1a575a33dcf63e2ecec2a0eaa37b15653b94845b63,0x1f76a6a208b481b107b96e3867f8cf346dbc7b5fc9bdb2d617cd55865a2492c8,0x4d52f0dad3ddb7ac5523c51a75a9acd47b244e536b240d692784305b69ca6ab5,0x96d311cec2f5a935b3252c551fca6b5f2a5764c646e02fe3551d43f4e854021d,0xb77d3960d3c841c6a5d5919e54f5aae64f43588cc8c7c94556aee42d2f88ad4a, and0x8e6630799608d868db8cba400853bf9bfee1e2acbb34d30ab0ca6f1bcefbb80e. - Stolen assets were consolidated through addresses including
0x2913d90d94c9833b11a3e77f136da03075c04a0fand0x34611F6BBB0A5F6F8EB48146F4474BFF842AE893, then portions were sent to Tornado Cash in100 ETHbatches. - Whitehat / copytrader responders also hit the vulnerable path; Dough later recovered about
76.2 ETHvia transactions0xB44F3098EE7A8A58505017078CF80516E684963146B031C9E211A72869E1CA0Band0x8e7f7241a1a8d162dabacc0ddfeba5424ec4b75b20cdd60022778477173a7b6f.
- The attacker funded EOA
-
What the attacker needed to be true:
flashloanReq()had to remain callable without proving that only the rightful Dough DSA owner or a tightly scoped protocol dispatcher was initiating the unwind.- The connector had to retain enough standing authority over user DSAs and token balances that a malicious downstream call could move collateral once it had been withdrawn.
- ParaSwap-related calldata needed to survive Dough’s parsing and control flow without target / selector validation strong enough to prevent direct token calls.
- Balancer flash liquidity had to be large enough to temporarily clear each victim’s Aave debt so Dough’s own deleverage logic would release collateral before the loan was repaid.
-
Technical anatomy worth remembering:
- The clever step was not the flash loan by itself. The flash loan only made the victim position look deleverage-ready for a moment. The real theft primitive was Dough’s willingness to treat attacker-shaped swap calldata as authenticated unwind intent.
- This incident sits between an approval drain and a leverage-helper bug. The attacker did not drain ordinary EOAs with stale router approvals; they drained user-controlled Dough proxy accounts after Dough’s connector temporarily liberated collateral from Aave.
- The exploit is a good reminder that
public deleverage helperis already a privileged surface. Once it can both repay debt and move collateral, every downstream swap byte becomes security-critical. - Dough’s postmortem is also useful because it shows the bug was not isolated to one connector file. The team later acknowledged the same
flashLoanReqtrust model in multiple connectors, which is exactly how generic adapter architectures magnify one validation mistake.
-
Representative transactions and addresses:
- Attacker EOA:
0x67104175fc5fabbdb5A1876c3914e04B94c71741 - First attack contract:
0x11A8DC866C5d03ff06bb74565b6575537B215978 - Vulnerable connector highlighted by CertiK:
0x9f54e8eAa9658316bB8006e03fff1Cb191AaFBe6 - First victim Dough DSA:
0x534a3bb1ecb886ce9e7632e33d97bf22f838d085 - Other victim DSAs highlighted by CertiK:
0x53d2D9D2b36d2784D80297E6532e3BD965435021,0x0d2b0c59D6a51eAE239A6C6eE29cFE73b79cC35b,0x8f690502964348acbab0E3E3E81192A582715d89,0xC581d6Ef24146f745d5Bc014cc114C8F0CA74783,0x460fDebe3D0B26e9DC194De80B3111b369B29272,0x3e9F59f371dB249F2D95cf1E6F5224Ff1D7328Ab,0x52F0b1B251FCa6f2b1191803a0aA4d9f4dB6F924 - First exploit transaction:
0x92cdcc732eebf47200ea56123716e337f6ef7d5ad714a2295794fdc6031ebb2e - Consolidation / stolen-funds addresses from Dough’s postmortem:
0x2913d90d94c9833b11a3e77f136da03075c04a0f,0x34611F6BBB0A5F6F8EB48146F4474BFF842AE893,0x932e71261ba289c5bcf197c0a7eabfe69d145488,0xBA25624eD80eb244f941281abb37d9589cFFdBF0 - Whitehat recovery transactions:
0xB44F3098EE7A8A58505017078CF80516E684963146B031C9E211A72869E1CA0B,0x8e7f7241a1a8d162dabacc0ddfeba5424ec4b75b20cdd60022778477173a7b6f
- Attacker EOA:
-
Response / mitigation:
- Dough disabled the affected flash-loan-based connectors, including
ConnectFlashloanUniV3,ConnectFlashloanParaswap,ConnectorDeleverageUniV3, andConnectorDeleverageParaswap. - Dough’s own postmortem says the hardening pass added a connector / action kill switch, stricter ECDSA-backed DSA creation,
msg.senderDSA checks to restrict publicflashloanReq()usage, a validated Augustus V5 registry lookup instead of trusting offchaintokenTransferProxydata, and a dedicated flash-loan storage contract to carry state fromdelegateDoughCallintoflashloanReq(). - The same postmortem also leaves useful unfinished work in the open: verify ParaSwap and UniswapV3 bytes onchain, add balance-expectation and max-slippage checks for loop / deloop / swap actions, and consider a more private flash-loan context instead of the public escrow-style path.
- Dough worked with SEAL 911 and cooperating whitehats, recovered a small portion of funds, and offered the main attacker a
10%bounty. - The durable fix for this family is not just
validate ParaSwap bytes better; it istreat public leverage/unwind helpers as privileged approval-funded route surfaces and bind payer scope, canonical DSA identity, downstream target / selector family, and spend meaning before collateral movement begins.
- Dough disabled the affected flash-loan-based connectors, including
Pattern tags
- arbitrary-call-token-approval-drain
- Public leverage helper acted as a privileged wallet-spend surface:
flashloanReq()was not a harmless convenience wrapper; it was the path that could repay debt, release collateral, and then speak arbitrary downstream calldata from a connector already trusted by user DSAs - Unwind sequencing and downstream call meaning drifted apart: the same deloop chain that legitimately reached
AaveActionto free collateral later accepted attacker-shaped ParaSwap bytes, sodeleverage this positionquietly becamecall this token contract with delegated authority - Delegated proxy-account custody broadened the approval-drain family: Dough was not draining plain EOAs with stale router approvals, but user-controlled DSAs whose collateral-management permissions had already been handed to protocol connectors
- Generic connector architecture multiplied one validation miss: Dough’s own postmortem said the same
flashLoanReqtrust model existed across four connectors, which is exactly how adapter fleets turn one under-validated execution primitive into a wider class of wallet-funded risk
Why the exploit worked
- The exploit paid because Dough fused three powers into one public path without re-binding them tightly enough at runtime: anyone could trigger the flash-loan deleverage helper, the helper could temporarily reshape a victim DSA’s solvency state, and the same execution chain could forward attacker-controlled ParaSwap calldata through a raw external call.
- The flash loan was only the setup. Its job was to make the victim position look momentarily deleverage-ready by repaying Aave debt. The actual theft happened later, when Dough treated the second-stage ParaSwap payload as if it still represented canonical unwind intent.
- Dough’s connector already sat inside the victim DSA’s trust boundary. Once collateral had been withdrawn from Aave into the DSA / connector-controlled flow, the attacker did not need to break ParaSwap, Aave, or Balancer; they only needed Dough to keep believing that attacker-shaped bytes were still part of a legitimate deleverage route.
- CertiK’s reconstructed call chain is the useful anatomy to preserve:
flashloanReq()→executeOperation()→deloopInOneOrMultipleTransactions()→deloopAllCollaterals()→flashloanVars.paraSwapContract.call(). The vulnerability was not merelybad calldata somewhere, but a privileged unwind pipeline whose final external-call boundary never proved that the downstream target and selector still matched the unwind it had just authorized. - Dough’s own postmortem sharpens the broader lesson: once the team discovered the same trust model in four flash-loan connectors, the real failure stopped looking like one ParaSwap integration mistake and started looking like a reusable approval-funded route-authority problem across the connector layer.
Detection and audit pivots
- Treat every public debt-repay / loop / deloop / flash-loan helper as a wallet-drain surface when it can act through user proxy accounts. The first question is not
can it deleverage?butwho is allowed to start this state transition for which DSA, and what exact downstream call family remains authorized after collateral is freed? - Reconstruct the full authority chain, not only the final malicious call. For Dough-style connectors, auditors should walk
entrypoint -> flash-loan callback -> debt repayment -> collateral release -> swap / settlement calland ask at which hop attacker-controlled bytes can stop meaningcanonical unwindand start meaningarbitrary token call. - Search connector fleets for repeated public entrypoints like
flashloanReq()that share the same trust model across multiple files. Dough’s own mitigation list is the clue: if one connector is vulnerable and three siblings expose the same public orchestration surface, review them as one family before production does it for you. - Watch for downstream calls from deleverage connectors to token contracts or arbitrary ParaSwap / aggregator targets that are inconsistent with expected unwind counterparties. In this incident, the key anomaly was not
flash loan happened; it wasconnector that should be swapping now directly called WETH.transferFrom(...). - Test victim-owned proxy accounts, not just EOAs. Systems that route through DSAs, smart wallets, or user vault proxies often hide approval-drain risk inside delegated account-management permissions rather than inside classic ERC-20 approvals granted by a plain wallet.
Failure-prevention checklist
- Restrict flash-loan deleverage entrypoints to the canonical actor for the DSA being unwound: the DSA owner, a signed route intent, or a tightly scoped protocol dispatcher.
public flashloanReq()plus delegated DSA authority is already too much ambient power. - Bind one flash-loan context object before external control leaves: DSA identity, connector branch, debt asset, collateral set, canonical downstream swap target family, recipient semantics, and one-shot consumption rules should all be committed in trusted local storage before
executeOperation()resumes. - Validate aggregator bytes semantically, not cosmetically. Onchain checks need to prove that ParaSwap / Uniswap payloads still mean
sell this asset through this approved Augustus path for this recipientrather than merelysome calldata blob that balances could survive. - Derive privileged downstream helpers from validated registries or code-pinned contracts, not from attacker-shaped or offchain-supplied
tokenTransferProxy-style fields. Dough’s own postmortem is explicit that this had to change. - Separate
repay debt,withdraw collateral, andswap / settle proceedsinto stage-scoped permissions. Freeing collateral for a deleverage should not automatically authorize arbitrary downstream token calls from the same connector frame. - Add per-connector kill switches and keep a live inventory of sibling connectors sharing the same flash-loan or swap trust model. Dough’s four-connector disable list is the operational clue that connector fleets must be reviewed as one risk surface.
- Pair semantic validation with economic bounds: onchain balance expectations, max slippage, and per-asset amount caps should constrain the canonical unwind after the route has already been proven authentic, not substitute for target / selector validation.
- Fuzz the whole deleverage chain using victim DSAs, not just happy-path swaps: adversarial tests should try fake ParaSwap targets, direct token
transferFrom()calls, cross-connector context reuse, and arbitrary callers invoking someone else’s unwind.
Audit prompts
- Who is actually authorized to call
flashloanReq()for a given DSA, and where is that authorization re-checked after the flash-loan callback returns control? - What exact state object binds
this flash loan belongs to this DSA and this connector branchacrossdelegateDoughCall,flashloanReq(),executeOperation(), and the final swap / settlement step? - Can
paraswapCallData,tokenTransferProxy, Augustus address selection, or any equivalent downstream routing field still be attacker-defined at the moment the connector makes its raw external call? - After Aave debt is repaid and collateral becomes withdrawable, what prevents the next downstream call from targeting an ERC-20 token contract or arbitrary recipient instead of a canonical unwind counterparty?
- Which sibling connectors share the same public flash-loan entrypoint or delegated-call trust model, and do they all inherit the same calldata validation, DSA binding, and kill-switch controls?
- Are balance checks and slippage limits proving route authenticity, or are they only checking that value moved somehow after a semantically unconstrained external call already happened?
- If the protocol uses registries such as Augustus for swap routing, is the connector deriving the executable helper from a validated onchain registry / code identity, or can route bytes still smuggle in a different spender or token-pull helper?
- In logs and traces, what would distinguish a healthy deleverage from a Dough-style exploit wave early enough to pause connectors before copycats spread: repeated public callers, victim DSAs that never initiated the unwind, or connector-to-token
transferFrom()calls during the swap stage?
Lessons that should already be standard
- Public deleverage helpers are privileged wallet-spend surfaces. If a connector can repay debt, free collateral, and forward dynamic calldata, it belongs in the same risk class as an approval-funded router, not in the same class as an ordinary convenience wrapper.
- Flash loans were not the root cause here; they were the temporary solvency shim. The security failure was letting
position now looks unwindablecollapse intoattacker may choose the next external call. - Delegated smart-account custody does not remove approval-drain risk; it relocates it. Dough’s DSAs were not EOAs with stale allowances, but they still exposed the same core problem once a trusted connector could speak arbitrary token calldata on their behalf.
- Offchain routing hints and proxy-helper fields are custody-critical. Dough’s own move to derive
tokenTransferProxyfrom a validated Augustus address is the concrete reminder thatintegration metadataoften sits directly on the spend-authority boundary. - Connector fleets amplify trust-boundary mistakes. Once Dough found the same
flashLoanReqmethod family across four connectors, the real lesson became architectural: review shared entrypoint and callback patterns as one control surface, not as isolated files. - Kill switches and route inventory are part of the design, not just incident-response garnish. A system that can move user collateral through public helper flows needs a fast way to answer
which connectors can spend with this authority right now? - Balance checks and slippage bounds are downstream controls. They help only after the protocol has already proven that the route, caller, DSA, and external counterparty are the canonical ones.
Why this incident matters
- It is a strong arbitrary-call approval-drain exemplar where the stolen balances lived in user proxy accounts rather than plain EOAs.
- It preserves a useful architecture lesson for account-abstraction-style DeFi products: if connectors can both manipulate debt and forward dynamic integration calldata, then route validation is as security-critical as collateral accounting.
- The preserved healthy boundary was not just
ParaSwap calldata should be cleaner, butone route-local authority object should have fused which Dough DSA was allowed to fund this unwind, which connector branch was active, which downstream swap targets / selectors were canonical, and what recipient semantics collateral release was supposed to have. - It also broadens the approval-drain corpus beyond pure aggregators like Dexible, Socket, and LI.FI into
position-management helper with delegated collateral authorityterritory.
Internal linkages
- Core recurring pattern: arbitrary-call-token-approval-drain.
- Positive-design companions: approval-funded-route-authority-truth-security-object and spender-authority-truth-security-object — the sharper question is what narrower approval-exercise object should have kept DSA identity, public deleverage entry, downstream ParaSwap target / selector family, and collateral-recipient meaning fused before the connector could exercise delegated wallet authority.
- Adjacent pattern: privileged-message-dispatcher-abuse — Dough’s connector was not an admin message bus, but the preserved lesson is similar: a generic external-call surface inherited more authority than its input validation deserved.
- Compare against router-centric wallet drains: dexible-selfswap-router-validation-approval-drain-2023-02-17, socket-wrappedtokenswapperimpl-performaction-arbitrary-call-approval-drain-2024-01-16, li-fi-gaszipfacet-arbitrary-call-infinite-approval-drain-2024-07-16.
Control surface
-
The decisive leverage sat in Dough’s public
flashloanReq()-driven deleverage workflow, the connector’s standing authority over user DSAs, and the raw downstream ParaSwap call that never provedthis payload is the intended swap for this unwind. -
More specifically, Dough never kept one route-local approval object for
which DSA may fund,which deleverage branch is active,which downstream target / selector family counts as canonical swap execution, andwhat collateral-release recipient meaning is allowed once debt is repaid. -
Once those pieces lined up, the attacker did not need to counterfeit balances or break Aave. Dough’s own helper truthfully unwound the victims’ positions and then faithfully executed the wrong downstream call.
-
Sources:
- https://medium.com/@dsa_56488/dough-finance-post-mortem-security-incident-report-3cf7a12f5ed4
- https://www.certik.com/blog/dough-finance-incident-analysis
- https://etherscan.io/tx/0x92cdcc732eebf47200ea56123716e337f6ef7d5ad714a2295794fdc6031ebb2e
- https://etherscan.io/tx/0x28a934816ed0371a3fbb2f89986eecf87a22937c40309ff2f58697792215b4da
- https://etherscan.io/tx/0xf356d1b05d26de824baddf1a575a33dcf63e2ecec2a0eaa37b15653b94845b63
- https://etherscan.io/tx/0x1f76a6a208b481b107b96e3867f8cf346dbc7b5fc9bdb2d617cd55865a2492c8
- https://etherscan.io/tx/0x4d52f0dad3ddb7ac5523c51a75a9acd47b244e536b240d692784305b69ca6ab5
- https://etherscan.io/tx/0x96d311cec2f5a935b3252c551fca6b5f2a5764c646e02fe3551d43f4e854021d
- https://etherscan.io/tx/0xb77d3960d3c841c6a5d5919e54f5aae64f43588cc8c7c94556aee42d2f88ad4a
- https://etherscan.io/tx/0x8e6630799608d868db8cba400853bf9bfee1e2acbb34d30ab0ca6f1bcefbb80e
-
Last reviewed: 2026-07-21 UTC