Platform: Code4rena
Start Date: 31/01/2023
Pot Size: $90,500 USDC
Total HM: 47
Participants: 169
Period: 7 days
Judge: LSDan
Total Solo HM: 9
Id: 211
League: ETH
Rank: 15/169
Findings: 4
Award: $1,430.07
π Selected for report: 1
π Solo Findings: 0
1357.6447 USDC - $1,357.64
A malicious strategy has access to the adapter's storage and can therefore freely change any values.
Because AdapterBase
calls the Strategy
using delegatecall
, the Strategy
has access to the calling contract's storage and can be manipulated directly.
In the following proof of concept, a MaliciousStrategy
is paired with the BeefyAdapter
and when called will manipulate the performanceFee
and highWaterMark
values. Of course, any other storage slots of the adapter could also be manipulated or any other calls to external contracts on behalf of the msg.sender
could be performed.
MaliciousStrategy
implementation showing the exploit -
https://gist.github.com/alpeware/e0b1c9f330419986142711e814bfdc7b#file-beefyadapter-t-sol-L18
Adapter
helper used to determine the storage slots -
https://gist.github.com/alpeware/e0b1c9f330419986142711e814bfdc7b#file-beefyadapter-t-sol-L65
BeefyAdapterTest
changes made to tests -
Adding the malicious strategy - https://gist.github.com/alpeware/e0b1c9f330419986142711e814bfdc7b#file-beefyadapter-t-sol-L123
Adding new test test__StrategyHarvest()
executing harvest()
-
https://gist.github.com/alpeware/e0b1c9f330419986142711e814bfdc7b#file-beefyadapter-t-sol-L132
Log output - https://gist.github.com/alpeware/e0b1c9f330419986142711e814bfdc7b#file-log-txt
Foundry
From chatting with the devs, the goal is to mix and match adapters and strategies. I don't think delegatecall
should be used and adapters and strategies should be treated as separate contracts. Relevant approvals should be given individually instead.
#0 - c4-judge
2023-02-16T06:53:04Z
dmvt marked the issue as primary issue
#1 - c4-sponsor
2023-02-17T10:01:41Z
RedVeil marked the issue as sponsor acknowledged
#2 - c4-judge
2023-02-25T16:21:59Z
dmvt marked the issue as selected for report
#3 - c4-judge
2023-03-01T23:28:18Z
dmvt marked issue #475 as primary and marked this issue as a duplicate of 475
#4 - c4-judge
2023-03-01T23:28:32Z
dmvt marked the issue as not a duplicate
#5 - c4-judge
2023-03-01T23:28:38Z
dmvt marked the issue as unsatisfactory: Invalid
#6 - c4-judge
2023-03-01T23:29:15Z
dmvt marked the issue as duplicate of #475
#7 - c4-judge
2023-03-01T23:29:26Z
dmvt marked the issue as selected for report
#8 - c4-judge
2023-03-03T19:50:32Z
dmvt marked the issue as satisfactory
π Selected for report: imare
Also found by: 7siech, Ch_301, Hawkeye, KIntern_NA, Malatrax, Ruhum, Walter, bin2chen, eccentricexit, hansfriese, ladboy233, peakbolt, rbserver, rvierdiiev, thecatking
14.932 USDC - $14.93
AdapterBase
implements a cooldown period that's supposed to limit the Strategy
from harvesting. However, the lastHarvest
time is never updated resulting in every call to harvest()
to call the underlying strategy despite there potentially being a cooldown period.
Since lastHarvest
time is never updated, as soon as (lastHarvest + harvestCooldown)
is > block.timestamp
, any subsequent calls to harvest
will call the strategy
since (lastHarvest + harvestCooldown)
will always be less than block.timestamp
.
function harvest() public takeFees { if ( address(strategy) != address(0) && ((lastHarvest + harvestCooldown) < block.timestamp) ) { // solhint-disable address(strategy).delegatecall( abi.encodeWithSignature("harvest()") ); } emit Harvested(); }
Foundry
Update lastHarvest
time -
function harvest() public takeFees { if ( address(strategy) != address(0) && ((lastHarvest + harvestCooldown) < block.timestamp) ) { // *** Update last harvest time *** lastHarvest = block.timestamp; // *** Update last harvest time *** // solhint-disable address(strategy).delegatecall( abi.encodeWithSignature("harvest()") ); } emit Harvested(); }
#0 - c4-judge
2023-02-16T04:25:50Z
dmvt marked the issue as duplicate of #199
#1 - c4-sponsor
2023-02-18T12:00:32Z
RedVeil marked the issue as sponsor confirmed
#2 - c4-judge
2023-02-23T22:30:13Z
dmvt marked the issue as satisfactory
22.0241 USDC - $22.02
https://github.com/code-423n4/2023-01-popcorn//blob/main/src/vault/Vault.sol#L88
It is possible to deploy a vault with 1e18 deposit, withdraw, management, performance fees locking up user assets.
https://gist.github.com/alpeware/bf3bf78112f3106d667c0f2dca25679e#file-vaultmaxfee-t-sol https://gist.github.com/alpeware/bf3bf78112f3106d667c0f2dca25679e#file-log-txt
Foundry
In the constructor, call proposeFees(fees)
[0] and changeFees()
[1] instead.
[0] https://github.com/code-423n4/2023-01-popcorn//blob/main/src/vault/Vault.sol#L525 [1] https://github.com/code-423n4/2023-01-popcorn//blob/main/src/vault/Vault.sol#L540
#0 - c4-judge
2023-02-16T04:18:58Z
dmvt marked the issue as duplicate of #198
#1 - c4-sponsor
2023-02-18T12:00:06Z
RedVeil marked the issue as disagree with severity
#2 - c4-sponsor
2023-02-18T12:00:11Z
RedVeil marked the issue as sponsor confirmed
#3 - c4-judge
2023-02-23T16:19:42Z
dmvt changed the severity to 2 (Med Risk)
#4 - c4-judge
2023-02-23T23:03:00Z
dmvt marked the issue as partial-25