Reproduced Exploit

OctoDeFi — anyone sets the deposit fee to 100% and takes the deposit

setFunctionFeeConfig is external and has no onlyOwner. The percentage cap is 10000 bps, so 100% is a legal value. The caller writes that fee for the deposit selector, then deposits the user's 100e18. The vault pays the whole amount to the executor. The control reverts the setter for a non-owner, an…

Jul 2025•Other•access-control•2 min read

Chain

Other

Category

access-control

Date

Jul 2025

Source

AuditVault

EVM Playground

Source-level debugger — step opcodes and Solidity in sync

evm-hack-analyzer

The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.

Loading fork state…

Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 61597-missing-access-control-on-critical-feecontroller-setters-cyf. Standalone Foundry PoC and full write-up: 61597-missing-access-control-on-critical-feecontroller-setters-cyf_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/access-control/missing-modifier

Key info#

ProtocolOctoDeFi
FindingCyfrin, AuditVault 61597
Vulnerable codeFeeController.setFunctionFeeConfig
HarmThe executor sets the fee to 100% and takes 100e18 of the next deposit.
Chainlocal synthetic

TL;DR#

setFunctionFeeConfig is external and has no onlyOwner. The percentage cap is 10000 bps, so 100% is a legal value. The caller writes that fee for the deposit selector, then deposits the user's 100e18. The vault pays the whole amount to the executor. The control reverts the setter for a non-owner, and the same deposit leaves 100e18 in the vault.

The vulnerable code#

SOLIDITY
function setFunctionFeeConfig(bytes4 _selector, FeeType _feeType, uint256 _feePercentage) external {
    if (_feePercentage > maxFeeLimits[_feeType]) {
        revert FeePercentageExceedLimit();
    }
    // @> VULN: no onlyOwner. The caller sets the deposit fee to 100%.
    functionFeeConfigs[_selector] = FeeConfig(_feeType, _feePercentage);
}

The fix is onlyOwner on the setter.

Root cause#

The admin setter checks the percentage against maxFeeLimits and does not check the caller. 10000 is inside the cap.

Preconditions#

maxFeeLimits[Percent] is 10000. The depositor holds 100e18 and has approved the vault. The caller is not the owner. One block.

Attack walkthrough#

  1. The depositor is minted 100e18 and approves the vault.
  2. The caller writes deposit.selector to fee type Percent and 10000 bps.
  3. The stored fee reads back as 10000.
  4. deposit pulls 100e18 and computes fee = amount * 10000 / 10000.
  5. The vault transfers 100e18 to the executor. The vault and the depositor both end at 0.

Diagrams#

flowchart TD A["Depositor holds 100e18 and approved the vault"] --> B["Caller writes deposit fee to 10000 bps"] B --> C["VULN: setFunctionFeeConfig has no onlyOwner"] C --> D["deposit computes a fee of 100e18"] D --> E["Executor receives 100e18 and the vault keeps 0"]

Impact#

The executor takes 100e18 of the deposit. The depositor is left with 0. With onlyOwner, the setter reverts and the deposit stays in the vault.

Remediation#

Add onlyOwner to setFunctionFeeConfig, setTokenGetter, and setGlobalTokenGetter.

How to reproduce#

BASH
cd 61597-missing-access-control-on-critical-feecontroller-setters-cyf_exp
forge test -vv

Sources#


Sources & further analysis#

Reproductions & code

Alerts & third-party analyses

  • Web3Sec X hacked database: search.
  • Rekt leaderboard: search.
  • Solodit incident search: search.

These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.