Beefy CAKE Vault Share Inflation
Exploit Transactions
0x03d363462519029cf9a544d44046cad0c7e64c5fb1f2adf5dd5438a9a0d2ec8eVictim Addresses
0x489afbaed0ea796712c9a6d366c16ca3876d8184BSC0xc2562dd7e4caee53df0f9cd7d4dddaa53bcd3d9bBSCLoss Breakdown
Similar Incidents
Goose Vault Share Inflation
49%Belt beltBNB Share Inflation
43%Channels Dust-Share Drain
36%AISPACE Pair Spoofing and Vault Drain
33%sDAO Reward Inflation Attack
33%Venus vTHE Donation Inflation
32%Root Cause Analysis
Beefy CAKE Vault Share Inflation
1. Incident Overview TL;DR
On BSC block 22832428, transaction 0x03d363462519029cf9a544d44046cad0c7e64c5fb1f2adf5dd5438a9a0d2ec8e exploited Beefy Finance's CAKE vault at 0x489afbaed0ea796712c9a6d366c16ca3876d8184. The attacker deposited 50000000000000000000000 CAKE into the vault while the vault priced shares against a stale strategy balance from StrategySyrup at 0xc2562dd7e4caee53df0f9cd7d4dddaa53bcd3d9b. That balance excluded already-claimable rewards: 53786404578434280321792 pending CAKE in Pancake MasterChef and 814705629 pending CTK in SmartChef. Because those rewards were omitted from the share-pricing denominator, the vault minted 44955527433503234324261 shares to the attacker, then the attacker realized the omitted rewards and exited with 79913717660761050982637 CAKE, extracting 29913717660761050982637 CAKE from incumbent vault holders.
The root cause is a combination of stale accounting and ineffective caller filtering. BeefyVault.deposit(uint256) computes shares from balance() before calling earn(), but the strategy's balanceOf() only counts idle CAKE plus staked principal and omits claimable MasterChef and SmartChef rewards. The attacker then used a constructor-based helper to call StrategySyrup.harvest(), bypassing the strategy's require(!Address.isContract(msg.sender), "!contract") check because a contract has zero code size during construction.
2. Key Background
The victim vault is the Beefy CAKE vault, which mints shares according to shares = amount * totalSupply / pool when supply is nonzero. Here, pool is the result of balance(), and balance() includes the vault's idle token balance plus IStrategy(strategy).balanceOf().
The strategy behind the vault is Beefy's StrategySyrup, which deposits CAKE into Pancake MasterChef, receives SYRUP, and then stakes SYRUP in SmartChef to earn CTK. Its accounting function is:
function balanceOf() public view returns (uint256) {
return balanceOfCake()
.add(balanceOfPool());
}
balanceOfPool() only reads MasterChef.userInfo(0, address(this)).amount, so it counts staked CAKE principal but not pending CAKE rewards in MasterChef and not pending CTK rewards in SmartChef.
SmartChef reward realization is also permissionless once the strategy is the staker. pendingReward(address) exposes the claimable CTK amount, and deposit(0) or deposit(amount) pays that reward from the contract's rewardToken balance. Because CTK itself is transferable, an attacker can permissionlessly top up SmartChef's reward inventory if needed.
3. Vulnerability Analysis & Root Cause Summary
The vulnerability is an ACT-style share inflation attack against a vault that priced deposits using stale strategy accounting. The key invariant is that new shares must be minted against the full economic value already owned by incumbent shareholders, including rewards that are already claimable by the strategy without introducing new external value. Beefy's vault violated that invariant by computing _pool = balance() before minting, while StrategySyrup.balanceOf() excluded MasterChef.pendingCake(0, strategy) and SmartChef.pendingReward(strategy).
That stale denominator made the attacker deposit look larger in proportional terms than it really was. The attacker then realized the omitted assets through two public mechanisms already wired into the strategy: deposit() caused MasterChef and SmartChef reward realization as it restaked, and harvest() claimed rewards, swapped CTK to CAKE, split fees, and redeposited. The final value transfer occurred when the attacker withdrew the oversized share position after those omitted rewards had been internalized into vault assets.
The code-level breakpoints are concrete:
function deposit(uint _amount) public {
uint _pool = balance();
...
shares = (_amount.mul(totalSupply())).div(_pool);
_mint(msg.sender, shares);
earn();
}
and:
function harvest() public {
require(!Address.isContract(msg.sender), "!contract");
IMasterChef(masterchef).leaveStaking(0);
ISmartChef(smartchef).deposit(0);
doswap();
dosplit();
deposit();
}
The caller filter is not a real permission boundary because constructor calls bypass Address.isContract.
4. Detailed Root Cause Analysis
At the exploitable pre-state immediately before the incident transaction, the strategy publicly exposed a large mismatch between reported and economically owned assets. StrategySyrup.balanceOf() returned 35394957721701860506470, while the same pre-state also exposed 53786404578434280321792 pending CAKE in MasterChef and 814705629 pending CTK in SmartChef. The vault itself held zero idle CAKE, so deposit pricing was driven by the stale strategy value alone.
The seed trace shows the attacker contract 0x71ac864f9388ebd8e55a3cdbc501d79c3810467c transferring 50000000000000000000000 CAKE into the vault and receiving 44955527433503234324261 freshly minted vault shares:
CakeToken::transferFrom(attacker, BeefyVault, 50000000000000000000000)
emit Transfer(from: 0x0000000000000000000000000000000000000000, to: attacker, value: 44955527433503234324261)
Immediately afterward, the vault forwarded the deposited CAKE to the strategy and called StrategySyrup.deposit(). The trace shows that this call itself realized the previously omitted rewards. MasterChef paid 53786404578434280321792 CAKE to the strategy, and SmartChef paid 814705629 CTK during the deposit path:
SyrupBar::safeCakeTransfer(strategy, 53786404578434280321792)
emit Transfer(from: SmartChef, to: strategy, value: 814705629)
The attacker then forced an explicit harvest through helper contract 0x2db641640eb55db79bb19b5719812e4b7c3ad2fe, created and executed during construction. The trace records the harvest path, the CTK-to-CAKE value realization, and the call fee payment to that helper:
StrategySyrup::harvest()
emit Transfer(from: strategy, to: 0x2Db641640eb55db79bB19B5719812e4B7c3Ad2Fe, value: 7507504182259121186)
That constructor execution is consistent with the code's weak access control: require(!Address.isContract(msg.sender), "!contract") evaluates to true while the helper is still being created.
Once harvest had moved omitted rewards into the strategy's accounted CAKE position, the attacker withdrew all vault shares. The trace shows the burn of 44955527433503234324261 shares and a transfer of 79913717660761050982637 CAKE back to the attacker:
BeefyVault::withdrawAll()
emit Transfer(from: attacker, to: 0x0000000000000000000000000000000000000000, value: 44955527433503234324261)
CakeToken::transfer(attacker, 79913717660761050982637)
The loss mechanism is therefore deterministic: incumbent shareholders had already earned the pending rewards economically, but the vault failed to include them in share pricing before minting a new large position. The attacker paid 50000 CAKE into the vault cycle and exited with 79913.717660761050982637 CAKE from the same cycle, capturing 29913.717660761050982637 CAKE that should have remained with pre-existing shareholders.
5. Adversary Flow Analysis
The adversary cluster in the collected artifacts contains three relevant addresses:
- EOA
0x35700c4a7bd65048f01d6675f09d15771c0facd5, which sent the seed transaction and funded the exploit path. - Exploit contract
0x71ac864f9388ebd8e55a3cdbc501d79c3810467c, which executed the deposit, harvest, withdraw, and financing cycle. - Constructor helper
0x2db641640eb55db79bb19b5719812e4b7c3ad2fe, which calledStrategySyrup.harvest()during construction and received the WBNB call fee.
The observed execution flow was:
- The EOA funded the exploit path with initial CTK and BUSD.
- The exploit contract assembled temporary capital using public financing routes to reach the required
50000CAKE deposit size. - The exploit contract deposited
50000CAKE into the Beefy vault and received underpriced shares because pending rewards were missing from the pricing denominator. - The strategy's own deposit path realized pending MasterChef CAKE and SmartChef CTK.
- A freshly deployed constructor helper called
harvest(), bypassed theisContractguard, collected the WBNB caller fee, and caused the strategy to swap CTK to CAKE and redeposit. - The exploit contract withdrew all shares, repaid its financing, and retained the residual value.
The original attack path used DODO and Venus for financing, but those are not the root-cause-critical elements. The exploit predicate depends on permissionless deposit access, stale strategy accounting, and permissionless reward realization, all of which are public and reproducible without attacker-specific privileges.
6. Impact & Losses
The measured vault loss is 29913717660761050982637 raw CAKE units, with 18 decimals, which equals 29913.717660761050982637 CAKE. The attack did not require privileged keys, governance access, or trusted off-chain infrastructure. It transferred value from incumbent Beefy CAKE vault shareholders to a newly minted attacker position by mispricing the vault's shares against stale strategy state.
The affected public protocol components are:
- Beefy CAKE Vault at
0x489afbaed0ea796712c9a6d366c16ca3876d8184 - StrategySyrup at
0xc2562dd7e4caee53df0f9cd7d4dddaa53bcd3d9b - Pancake MasterChef at
0x73feaa1ee314f8c655e354234017be2193c9e24e - SmartChef at
0xf35d63df93f32e025bce4a1b98dcec1fe07ad892
7. References
- Seed transaction metadata for
0x03d363462519029cf9a544d44046cad0c7e64c5fb1f2adf5dd5438a9a0d2ec8e, including sender0x35700c4a7bd65048f01d6675f09d15771c0facd5and target0x71ac864f9388ebd8e55a3cdbc501d79c3810467c. - Seed execution trace for the same transaction, showing the CAKE deposit, oversized share mint, reward realization, helper fee payment, and CAKE withdrawal.
- Verified BeefyVault source for
0x489afbaed0ea796712c9a6d366c16ca3876d8184, especiallybalance(),deposit(uint256),earn(), andwithdrawAll(). - Verified StrategySyrup source for
0xc2562dd7e4caee53df0f9cd7d4dddaa53bcd3d9b, especiallydeposit(),harvest(),balanceOf(), andbalanceOfPool(). - Verified SmartChef source for
0xf35d63df93f32e025bce4a1b98dcec1fe07ad892, especiallypendingReward(address)anddeposit(uint256). - Decompilation artifact for attacker contract
0x71ac864f9388ebd8e55a3cdbc501d79c3810467c, supporting the multi-step exploit orchestration.