Streaming Protocol contest - harleythedog's results

General Information

Platform: Code4rena

Start Date: 30/11/2021

Pot Size: $100,000 USDC

Total HM: 15

Participants: 36

Period: 7 days

Judge: 0xean

Total Solo HM: 4

Id: 62

League: ETH

Streaming Protocol

Findings Distribution

Researcher Performance

Rank: 19/36

Findings: 4

Award: $1,860.24

🌟 Selected for report: 3

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: WatchPug

Also found by: 0x0x0x, ScopeLift, gpersoon, gzeon, harleythedog, hyh, jonah1005, kenzo

Labels

bug
duplicate
3 (High Risk)
sponsor confirmed

Awards

385.4189 USDC - $385.42

External Links

Handle

harleythedog

Vulnerability details

Impact

In the case where recoverTokens is called with token == depositToken, the calculation for the excess number of deposit tokens in the contract is:

uint256 excess = ERC20(token).balanceOf(address(this)) - (depositTokenAmount - redeemedDepositTokens);

This does not account for the amount of deposit tokens in the contract due to flashloan fees, which is recorded in the variable depositTokenFlashloanFeeAmount. As a result, a recoverTokens call made by the stream creator to a user that needs tokens recovered would send MORE tokens than the user actually deserves. The function would send all depositTokenFlashloanFeeAmount deposit tokens to the user, which could be a very large amount due to the nature of flashloans.

Due to the fact that this could be such a large mistake, I believe this to be a high severity issue. An attacker could send a small amount of deposit tokens to the contract intentionally, and then act like it was a mistake. It would be hard to spot the bug that the recovery would also send all of the flashloan fees to the attacker as well, so it is a very real possibility that the stream creator would fall for this trick. At the very least, the function in its current state is unusable because of this bug.

Proof of Concept

See the calculation of excess deposit tokens here: https://github.com/code-423n4/2021-11-streaming/blob/56d81204a00fc949d29ddd277169690318b36821/Streaming/src/Locke.sol#L654

Tools Used

Inspection

Change the calculation to instead be:

uint256 excess = ERC20(token).balanceOf(address(this)) - (depositTokenAmount + depositTokenFlashloanFeeAmount - redeemedDepositTokens);

#0 - 0xean

2022-01-14T20:53:31Z

dupe of #241

Findings Information

🌟 Selected for report: harleythedog

Also found by: hyh, kenzo, pauliax, pedroais

Labels

bug
3 (High Risk)

Awards

1057.3907 USDC - $1,057.39

External Links

Handle

harleythedog

Vulnerability details

Impact

In recoverTokens, the logic to calculate the excess number of deposit tokens in the contract is:

uint256 excess = ERC20(token).balanceOf(address(this)) - (depositTokenAmount - redeemedDepositTokens);

This breaks in the case where isSale is true and the deposit tokens have already been claimed through the use of creatorClaimSoldTokens. In this case, redemeedDepositTokens will be zero, and depositTokenAmount will still be at its original value when the streaming ended. As a result, any attempts to recover deposit tokens from the contract would either revert or send less tokens than should be sent, since the logic above would still think that there are the full amount of deposit tokens in the contract. This breaks the functionality of the function completely in this case.

Proof of Concept

See the excess calculation here: https://github.com/code-423n4/2021-11-streaming/blob/56d81204a00fc949d29ddd277169690318b36821/Streaming/src/Locke.sol#L654

See creatorClaimSoldTokens here: https://github.com/code-423n4/2021-11-streaming/blob/56d81204a00fc949d29ddd277169690318b36821/Streaming/src/Locke.sol#L583

Notice that creatorClaimSoldTokens does not change depositTokenAmount or redeemedDepositTokens, so the excess calculation will be incorrect in the case of sales.

Tools Used

Inspection

I would recommend setting redeemedDepositTokens to be depositTokenAmount in the function creatorClaimSoldTokens, since claiming the sold tokens is like "redeeming" them in a sense. This would fix the logic issue in recoverTokens.

#0 - brockelmore

2021-12-08T22:38:41Z

duplicates: #180, #268, #88

#1 - 0xean

2022-01-15T01:29:51Z

upgrading to High as assets would be lost in the case outlined by the warden

3 — High: Assets can be stolen/lost/compromised directly (or indirectly if there is a valid attack path that does not have hand-wavy hypotheticals).

Findings Information

🌟 Selected for report: harleythedog

Also found by: WatchPug, csanuragjain, gpersoon, hubble

Labels

bug
2 (Med Risk)
sponsor confirmed

Awards

317.2172 USDC - $317.22

External Links

Handle

harleythedog

Vulnerability details

Impact

The storage variable unstreamed keeps track of the global amount of deposit token in the contract that have not been streamed yet. This variable is a public variable, and users that read this variable likely want to use its value to determine whether or not they want to stake in the stream.

The issue here is that unstreamed is incremented on calls to stake, but it is not being decremented on calls to withdraw. As a result, a malicious user could simply stake, immediately withdraw their staked amount, and they will have increased unstreamed. They could do this repeatedly or with large amounts to intentionally inflate unstreamed to be as large as they want.

Other users would see this large amount and be deterred to stake in the stream, since they would get very little reward relative to the large amount of unstreamed deposit tokens that appear to be in the contract. This benefits the attacker as less users will want to stake in the stream, which leaves more rewards for them.

Proof of Concept

See stake here: https://github.com/code-423n4/2021-11-streaming/blob/56d81204a00fc949d29ddd277169690318b36821/Streaming/src/Locke.sol#L417

See withdraw here: https://github.com/code-423n4/2021-11-streaming/blob/56d81204a00fc949d29ddd277169690318b36821/Streaming/src/Locke.sol#L455

Notice that stake increments unstreamed but withdraw does not affect unstreamed at all, even though withdraw is indeed removing unstreamed deposit tokens from the contract.

Tools Used

Inspection

Add the following line to withdraw to fix this issue:

unstreamed -= amount;

#0 - brockelmore

2021-12-08T22:42:47Z

duplicates: #270, #251, #224, #126

Findings Information

🌟 Selected for report: harleythedog

Labels

bug
G (Gas Optimization)

Awards

100.2104 USDC - $100.21

External Links

Handle

harleythedog

Vulnerability details

Impact

In claimReward, the following lines of code happen sequentially:

ts.rewards = earned(ts, cumulativeRewardPerToken); ... uint256 rewardAmt = ts.rewards; ts.rewards = 0;

It would save gas if you simplified it to:

uint256 rewardAmt = earned(ts, cumulativeRewardPerToken); ts.rewards = 0;

which is equivalent and makes more sense.

Proof of Concept

See claimReward here: https://github.com/code-423n4/2021-11-streaming/blob/56d81204a00fc949d29ddd277169690318b36821/Streaming/src/Locke.sol#L555

Tools Used

Inspection

Change the code as described above to save gas and simplify logic.

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter