0x92cdcc732eebf47200ea56123716e337f6ef7d5ad714a2295794fdc6031ebb2e0x534a3bb1ecb886ce9e7632e33d97bf22f838d085Ethereum0x5390724ca3b0880242c7b1ef08eb9b1abe698c0eEthereumOn Ethereum mainnet transaction 0x92cdcc732eebf47200ea56123716e337f6ef7d5ad714a2295794fdc6031ebb2e, an unprivileged attacker used a fresh harness contract to repay a Dough wallet's Aave USDC debt and then invoke Dough's public connector at 0x9f54e8eaa9658316bb8006e03fff1cb191aafbe6. That connector caused victim wallet 0x534a3bb1ecb886ce9e7632e33d97bf22f838d085 to execute privileged Aave actions, supply 5,000,000 USDC, withdraw 596.844648055377423623 WETH, and approve the connector to move the withdrawn WETH. The attacker harness then pulled the WETH, swapped it to USDC, repaid its flash liquidity, and left 830487417542 USDC of direct profit at attacker address 0x2913d90d94c9833b11a3e77f136da03075c04a0f.
The root cause is an access-control failure in Dough's connector model. The wallet trusted connector id 22 returned by DoughIndex, but the connector's privileged entrypoint was publicly callable and accepted attacker-controlled wallet and action payloads. That made connector authorization equivalent to universal caller authorization for any Dough wallet that had connector 22 configured.
The victim wallet 0x534a... is an EIP-1167 minimal proxy whose implementation at block 20288622 was 0x8a3f35e9eb756ad10242655bf5075178bcb7b59f. Storage slot 1 of the wallet points to DoughIndex proxy , which routes connector IDs and also exposes the AaveActions implementation address. Auditor RPC findings confirm that connector id resolved to both immediately before the exploit block and at the incident block.
0x5390724ca3b0880242c7b1ef08eb9b1abe698c0e220x9f54e8eaa9658316bb8006e03fff1cb191aafbe6The relevant protocol path is:
attacker harness -> public connector flashloanReq(...)
-> victim wallet executeAction(...)
-> AaveActions executeAaveAction(...)
-> Aave supply / withdraw on behalf of victim wallet
The pre-state evidence rules out a simpler approval-based theft path. At block 20288622, the victim wallet had zero aWETH allowance to both attacker harness 0x11a8dc866c5d03ff06bb74565b6575537b215978 and connector 0x9f54..., and zero USDC variable-debt delegation to those same addresses.
{
"aWETH.allowance(victim, 0x11a8...)": "0",
"aWETH.allowance(victim, 0x9f54...)": "0",
"variableDebtUSDC.borrowAllowance(victim, 0x11a8...)": "0",
"variableDebtUSDC.borrowAllowance(victim, 0x9f54...)": "0"
}
This incident is an ATTACK-class ACT opportunity caused by broken execution authorization around a shared public connector. The intended invariant is that only a wallet owner, or a wallet-owner-approved execution path, should be able to cause Aave supply, approval, repay, and withdraw operations for that wallet. Dough's wallet implementation instead treated the connector address returned by DoughIndex as a sufficient authorization condition for executeAction. Because the connector at 0x9f54... exposed a public flashloanReq entrypoint, any unprivileged caller could reach the wallet's privileged execution surface so long as the wallet had connector 22 configured. The exploit therefore did not depend on prior token approvals, debt delegation, privileged keys, or attacker-specific protocol roles. The code-level breakpoint is the combination of wallet executeAction(...) trusting connector 22 and connector flashloanReq(...) accepting attacker-supplied wallet and action payloads.
The exploit starts from pre-state sigma_B, defined as Ethereum mainnet immediately before block 20288623. At that point the victim wallet still used DoughIndex 0x5390..., connector 22 still resolved to 0x9f54..., and the wallet had no direct aWETH approval or USDC debt delegation to attacker-linked contracts. The attacker could therefore not pull collateral through a standard ERC-20 approval path and instead had to leverage the wallet's own privileged execution machinery.
The attacker EOA 0x67104175fc5fabbdb5a1876c3914e04b94c71741 first deployed harness contract 0x11a8dc866c5d03ff06bb74565b6575537b215978 in tx 0x903ab71f... and then approved it for USDC in tx 0x5efb524d.... In the seed exploit transaction, that harness borrowed USDC from Balancer, repaid the victim wallet's Aave USDC debt, and transferred 6,000,000 USDC into the public connector. The harness then called the connector's public flashloanReq(...) entrypoint with attacker-controlled payloads.
The incident trace shows the critical authorization path. The connector called the victim wallet's executeAction(22, USDC, 5000000, WETH, 596844648055377423623, 2). The wallet implementation then queried DoughIndex for connector 22, received 0x9f54..., and proceeded because msg.sender was that connector. The trace then shows the wallet reaching AaveActions and performing the privileged operations under victim-wallet context:
0x534a...::executeAction(22, USDC, 5000000, WETH, 596844648055377423623, 2)
-> DoughIndex::getDoughConnector(22) returns 0x9f54...
-> DoughIndex::aaveActionsAddress() returns 0x830926...
-> AaveActions::executeAaveAction(...)
-> AavePool::supply(USDC, 5000000, 0x534a..., 0)
-> AavePool::withdraw(WETH, 596844648055377423623, 0x534a...)
The same trace also shows why the attack is not based on a pre-existing allowance. After withdrawing WETH back to the victim wallet, the wallet approved the connector for exactly 596844648055377423623 WETH during exploit execution, and the connector immediately used WETH.transferFrom(victim, attacker_harness, drainAmount) to move the collateral out:
emit Approval(owner: 0x534a..., spender: 0x9f54..., value: 596844648055377423623)
0xC02a...::transferFrom(0x534a..., 0x11A8..., 596844648055377423623)
This establishes the decisive root cause: once the attacker can enter through the public connector, the wallet itself performs the approvals and Aave actions needed to exfiltrate collateral. Authorization is bound only to the connector contract address, not to the initiating caller or any wallet-owner consent.
The adversary cluster contains three directly evidenced addresses. EOA 0x6710... funded and controlled the exploit, harness 0x11a8... orchestrated the on-chain sequence, and EOA 0x2913... received the final direct USDC profit. The sender tx history shows the sequence clearly: harness deployment at block 20288611, USDC approval at block 20288615, a preparatory harness call at block 20288621, and the exploit tx at block 20288623.
The end-to-end exploit flow in tx 0x92cdcc73... is:
flashloanReq(...) with payloads that target the victim wallet.The trace evidence for the core pivot is direct:
emit Supply(... onBehalfOf: 0x534a..., amount: 5000000)
emit Withdraw(... user: 0x534a..., to: 0x534a..., amount: 596844648055377423623)
emit Transfer(from: 0x534a..., to: 0x11A8..., value: 596844648055377423623)
The measurable loss is the victim wallet's Aave WETH collateral. The wallet lost 596844648055377423623 wei-denominated WETH units, which is 596.844648055377423623 WETH. The attacker then swapped the drained WETH to 1769054244353 USDC through Uniswap V2. Balance-diff evidence shows a direct profit of 830487417542 USDC units credited to attacker address 0x2913..., with the harness also receiving 997500 aUSDC. Gas paid by the originating EOA was only 3167800072141389 wei and does not affect the exploit's profitability in USDC terms.
{
"loss_token": "WETH",
"loss_amount_raw": "596844648055377423623",
"attacker_direct_profit_usdc_raw": "830487417542"
}
0x92cdcc732eebf47200ea56123716e337f6ef7d5ad714a2295794fdc6031ebb2e0x903ab71fcf63fe3beb649317dfccd89845f6894d2c6f39c714564b88b2165dd6, 0x5efb524d7d290278b4d048e1f60eaabfc57d0ce84b55f786b570756406067091, 0x2083b43536ce93725fadaf9f4bac7a3a9b1700b050dbacc4aa7425208275196d0x534a3bb1ecb886ce9e7632e33d97bf22f838d0850x8a3f35e9eb756ad10242655bf5075178bcb7b59f0x5390724ca3b0880242c7b1ef08eb9b1abe698c0e0x9f54e8eaa9658316bb8006e03fff1cb191aafbe60x830926c67b09b78f854f0afa75892bd0c67902b10x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E20xBA12222222228d8Ba445958a75a0704d566BF2C80x7a250d5630B4cF539739dF2C5dAcb4c659F2488D