# Guru.fund $96K Vault Approval Exploit

> Guru.fund, a delegated fund-management protocol on Ethereum, lost about $96K across seven transactions when a whitelisted deposit adapter let attackers make each vault approve and drain itself.

Source: https://defimon.xyz/blog/guru-fund-hack-july-2026 · Published: 2026-07-24 · Network: Ethereum · Impact: $96K

---

## TLDR

On July 24, 2026, Guru.fund, a delegated fund-management protocol on Ethereum, was drained across seven transactions for roughly $96,000 in fund value, of which the attackers realized about $61,500. Defimon flagged the first drain in real time and the team was alerted while about $83,000 of the protocol's TVL was still sitting in the vaults, so almost the entire loss was recoverable at the moment of the alert. Guru.fund lets anyone create or join a fund whose assets sit in a per-fund vault, and its shared deposit path let a depositor make a vault approve an arbitrary spender for its entire balance. The attackers deposited dust, forced each targeted vault to grant their own contract an unlimited token allowance through a whitelisted adapter, then transferred the vault's holdings out and repeated the process fund by fund. Eight of the protocol's sixteen funds, every one holding meaningful value, were emptied in about 85 minutes. The pause landed 67 minutes after the alert, by which point only dust remained.

![Telegram message from Defimon in Guru's channel asking to share a detected suspicious Guru transaction with the developers, with two emoji reactions and no reply.](/guru.png "Defimon's message in Guru's Telegram: two reactions, no reply from the team.")

## Technical Analysis

Guru.fund, built on an engine its contracts call Lotus, is a PAMM-inspired protocol where each fund is a pair of minimal-proxy clones: a FundVault that custodies a basket of tokens and a FundLedger that tracks shares. Every fund routes operations through one shared FundController, so a flaw in that controller applies to all funds at once.

The vulnerability sits in the permissionless deposit path. `deposit()` accepts a caller-supplied array of external calls and executes them against the vault through whitelisted adapters, then mints shares from raw on-chain balance ratios with no independent price oracle.

```solidity
function _processDepositAssets(DepositContext memory ctx) private {
    for (uint256 i; i < ctx.deposit.extCalls.length; ++i) {
        ctx.vault.execute(
            _verifiedAdapter(ctx.deposit.extCalls[i].adapter),
            ctx.deposit.extCalls[i].callData
        );
    }
}

// shares = totalSupply * min_i((balanceAfter_i - balanceBefore_i) / balanceBefore_i)
function _registerDeposit(DepositContext memory ctx) private {
    ctx.shares = ctx.totalSupply * ctx.lowestInputRatio / UNIT;
    ctx.deposit.ledger.registerDeposit(
        msg.sender, ctx.deposit.coin, ctx.shares, principalAmountIn, ctx.fee
    );
}
```

One of the enabled adapters, an unverified contract at [0x5e0234](https://etherscan.io/address/0x5e0234C98fDCC1c5e69241F6a08C624dF6fAd91E), makes the vault call `approve` with a caller-chosen spender. Passing their own exploit contract as that spender, the attacker made each vault grant an unlimited allowance over its own tokens. The vault emitted the approval on-chain during the deposit, and once `deposit()` returned the attacker pulled the balance.

```solidity
// during deposit(), through the whitelisted adapter:
GURU.approve(attackerContract, type(uint256).max);   // vault approves the attacker

// immediately after deposit() returns:
GURU.transferFrom(vault, attackerContract, vaultGuruBalance);
WETH.transferFrom(vault, attackerContract, vaultWethBalance);
```

A second whitelisted adapter, a Uniswap V2 wrapper at [0x733977](https://etherscan.io/address/0x733977b9CA2db53F3d796c88e36df3383c72434a), swapped the small deposited amount into a dust quantity of each fund asset so the per-asset minimum checks in `_validateDepositAmounts` passed and the deposit did not revert. A Balancer flash loan funded the deposits and liquidated the drained baskets inside the same transaction. Because the deposit prices shares from spot balances rather than an oracle, the flash-loaned swaps that satisfied the checks and sold the loot were cheap.

The pattern repeated across funds. Two externally owned accounts ran the campaign: [0x7752](https://etherscan.io/address/0x7752F477958A55D26f062e75dA9FF3B142706cc5) executed the first drain with a standalone exploit contract, and [0x1e7B](https://etherscan.io/address/0x1e7Bd709fb53ef70F6B6f9C04b1bd2e77d0dE29A), deploying a fresh contract per transaction, ran the remaining six. This is a flash-loan-assisted access control failure rather than a pricing bug in the usual sense. [Flash loans](/blog/flash-loan-attacks-explained) supplied working capital and cover, but the decisive flaw was an adapter that let anyone make a vault approve them.

## Impact Assessment

The seven transactions drained eight of Guru.fund's sixteen funds, specifically every fund holding more than dust. Total fund value removed was about $96,000, close to the entire protocol TVL, while the attackers realized roughly $61,500, about 26 ETH plus $13,300 in USDC. The difference went to slippage as illiquid basket tokens were sold into thin pools. The largest single drain took about $30,800 from one fund.

Because the flaw lived in shared controller code, every fund, and every future deposit into any fund, was drainable by anyone in a single transaction until the protocol was paused. At the moment of the pause, the funds that survived held only about $300 combined, so the exposure was not limited to the value present at attack time but extended to any capital the protocol might later hold.

## Response and Recovery

Detection was not the bottleneck, and neither was raising the alarm. Defimon's monitoring flagged the exploit in real time, catching the drainer logic the attacker ran inside their exploit contract's creation code. At 10:33 UTC, nineteen minutes into the incident and while roughly $83,000 of TVL was still in the vaults, the Defimon team posted in Guru's own Telegram channel and asked a developer there to escalate to whoever held the pause key. No one on the team responded. With no reaction on the channel, Defimon had to track down a direct contact while the vaults kept emptying, and that search is where the time went. The protocol was not paused until 11:40 UTC, and nearly all of the damage occurred inside that 67-minute window. Had the team acted on the message when it landed, the funds would have been saved.

```timeline
{
  "timezone": "UTC",
  "alertTime": "10:33",
  "pauseTime": "11:40",
  "gapLabel": "67 min",
  "avoidable": "$83,054",
  "savedPct": "86%",
  "events": [
    {"time": "10:14", "kind": "attack-pre", "title": "Attack #1 · fund 0xc91d…", "sub": "18 minutes before the alert", "amount": "−$13,197", "meta": "already settled", "links": {"0xc91d…": "https://etherscan.io/address/0xC91d4660B8429B141c3D555286DC47054D555A2C"}},
    {"time": "10:33", "kind": "alert", "title": "Defimon messages Guru's Telegram", "sub": "flagged the exploit, asked a dev to escalate · no one answered · ~$83K still in the vaults"},
    {"time": "10:51", "kind": "attack", "gap": true, "title": "Attack #2 · fund 0x4bcc…", "amount": "−$894", "meta": "avoidable Σ $894", "links": {"0x4bcc…": "https://etherscan.io/address/0x4BccF3044D49022B511B1bAae3Bedf798E51FaAf"}},
    {"time": "11:16", "kind": "attack", "gap": true, "title": "Attack #3 · funds 0x0099… + 0xd7ad…", "amount": "−$123", "meta": "avoidable Σ $1,017", "links": {"0x0099…": "https://etherscan.io/address/0x0099B6712a8ecccaA9395dF23edC88Cd5D52F750", "0xd7ad…": "https://etherscan.io/address/0xD7AD05608459329DA44750d0B6a0FeDe18443676"}},
    {"time": "11:24", "kind": "attack", "gap": true, "title": "Attack #4 · fund 0x8688… (largest)", "amount": "−$30,767", "meta": "avoidable Σ $31,784", "links": {"0x8688…": "https://etherscan.io/address/0x868847E1a5Ca7489371184eDC19594e2C5F2D8EE"}},
    {"time": "11:29", "kind": "attack", "gap": true, "title": "Attack #5 · fund 0xde54…", "amount": "−$1,419", "meta": "avoidable Σ $33,203", "links": {"0xde54…": "https://etherscan.io/address/0xDe54e9801c6B28B73de945b103f30Dd3D8850298"}},
    {"time": "11:34", "kind": "attack", "gap": true, "title": "Attack #6 · fund 0xf3e2…", "amount": "−$27,621", "meta": "avoidable Σ $60,824", "links": {"0xf3e2…": "https://etherscan.io/address/0xf3E2ce89ADFa9732f2cb4E821fd462499058a908"}},
    {"time": "11:39", "kind": "attack", "gap": true, "title": "Attack #7 · fund 0x8b87…", "amount": "−$22,230", "meta": "avoidable Σ $83,054", "links": {"0x8b87…": "https://etherscan.io/address/0x8B8762052914912d2661810b361b60bCa53c0Bbc"}},
    {"time": "11:40", "kind": "pause", "title": "Protocol paused", "sub": "67 minutes after the alert · dust remaining"}
  ]
}
```

Only the first attack, at 10:14 UTC, settled before the alert, and it took about $13,200. Every one of the six attacks after 10:33 was still preventable, roughly $83,000 in total, and the three largest drains, about $80,600 combined, all landed 51 to 66 minutes after the alert. Acting on the alert promptly would have cut the loss from about $96,000 to about $13,200 and preserved essentially all of the recoverable TVL.

With the vaults empty, recovery came down to negotiation. About two hours after the pause, Guru.fund's developer [messaged the main attacker on-chain](https://etherscan.io/tx/0x446dfdfda71bc6d6c8476bc07458c8f78e35115825998fe8f2c8a71e7267bdb3) from the protocol's admin address ([0x0290…08f6](https://etherscan.io/address/0x0290334f079b3A04C428c4f5813759c681b208f6)), offering a bounty for the return of funds:

> This is Numa, Guru.Fund dev. We acknowledge the exploit and I am reaching out dev-to-dev: if you can consider returning some or all of the assets in exchange for a bounty, please reply onchain from this address with a secure contact method encoded in the tx data.
>
> Behind those vaults there are real users, and we do not have the resources to refund them. If there's a path to an outcome that inflicts the least damage to them, I'm ready to discuss quickly and in good faith.

The message was also [cross-posted on X](https://x.com/DefimonAlerts/status/2080647569852535154).

That gap between a prompt alert and a slow pause is the recurring failure mode in on-chain incidents, and it is a response problem rather than a detection one. A [real-time exploit feed](/docs/websocket_attack_message) wired into a pre-authorized pause lets a team act in the minutes a drain takes rather than reading about it afterward. The same point runs through our note on [automating incident response](/blog/automating-defi-incident-response): an alert only prevents loss if something acts on it quickly.

## Related Addresses

- Attack transaction (first drain): [0xa5afe80e4de236dbf493b8b082f1066bc427f15d7fa5df8570d005bf2daf7659](https://etherscan.io/tx/0xa5afe80e4de236dbf493b8b082f1066bc427f15d7fa5df8570d005bf2daf7659)
- Attacker, first drain: [0x7752F477958A55D26f062e75dA9FF3B142706cc5](https://etherscan.io/address/0x7752F477958A55D26f062e75dA9FF3B142706cc5)
- Attacker, six subsequent drains: [0x1e7Bd709fb53ef70F6B6f9C04b1bd2e77d0dE29A](https://etherscan.io/address/0x1e7Bd709fb53ef70F6B6f9C04b1bd2e77d0dE29A)
- Vault approval adapter (unverified): [0x5e0234C98fDCC1c5e69241F6a08C624dF6fAd91E](https://etherscan.io/address/0x5e0234C98fDCC1c5e69241F6a08C624dF6fAd91E)
- FundController: [0xf9357a85e79c388c13fb83b237ff759675cc5977](https://etherscan.io/address/0xf9357a85e79c388c13fb83b237ff759675cc5977)
- Pause transaction: [0x78a3afa1b9e1a6d4f39804be34e86994ea48869a7581a1499b1bd76ff2269e1f](https://etherscan.io/tx/0x78a3afa1b9e1a6d4f39804be34e86994ea48869a7581a1499b1bd76ff2269e1f)
- On-chain bounty message (Guru.fund dev to attacker): [0x446dfdfda71bc6d6c8476bc07458c8f78e35115825998fe8f2c8a71e7267bdb3](https://etherscan.io/tx/0x446dfdfda71bc6d6c8476bc07458c8f78e35115825998fe8f2c8a71e7267bdb3)
- Guru.fund dev / admin (pauser and message sender): [0x0290334f079b3A04C428c4f5813759c681b208f6](https://etherscan.io/address/0x0290334f079b3A04C428c4f5813759c681b208f6)


