PoolTogether TwabRewards contest - cmichel's results

A protocol for no loss prize savings on Ethereum

General Information

Platform: Code4rena

Start Date: 09/12/2021

Pot Size: $25,000 USDC

Total HM: 12

Participants: 25

Period: 4 days

Judge: LSDan

Total Solo HM: 4

Id: 64

League: ETH

PoolTogether

Findings Distribution

Researcher Performance

Rank: 16/25

Findings: 3

Award: $393.76

🌟 Selected for report: 1

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: johnnycash

Also found by: 0x421f, cmichel, gpersoon, gzeon, harleythedog, kemmio, kenzo, sirhashalot

Labels

bug
duplicate
3 (High Risk)

Awards

108.6174 USDC - $108.62

External Links

Handle

cmichel

Vulnerability details

The TwabRewards contract has an implicit restriction of 256 epochs per promotion as it uses a bitmask in a uint256 to mark claimed epochs 0-255, see _isClaimedEpoch.

"/// @dev We pack epochs claimed by a user into a uint256. So we can't store more than 255 epochs."

First, this comment should be "cannot store more than 256 epochs" as there are 256 bits that can be set in a uint256, but the epoch index cannot be more than 255 (epochs are indexed at 0).

Second, this restriction is never enforced and the 257th epoch can be claimed over and over again, which allows an attacker to steal the entire contract balance.

POC
  • attacker creates a promotion using a _token that is already used in a different promotion (for example, WETH) with 257 epochs: createPromotion(_ticket, _token=WETH, _tokensPerEpoch=1e18, _startTimestamp=now, _epochDuration=0, _numberOfEpochs=257). They need to transfer 257 * 1e18 tokens. (The ticket contract does not matter, they can even create their own fake ticket contract where they own 100% of the total supply)
  • They can repeatedly claim the 257th epoch 10,000 times by calling claimRewards([256, 256, 256, ...]). The _updateClaimedEpoch(_userClaimedEpochs, _epochId=256) call that is supposed to mark the 257th epoch as claimed does not do anything as the 1 << 256 does not fit into a uint256 and is 0. Then _userClaimedEpochs | (uint256(1) << _epochId) = 0 | (1 << 256) = 0 | 0 = 0 returns 0, the initial value, which allows reclaiming it.
  • The attacker steals the 1e18 tokens 10,000 times but only paid 1e18 257 times.

I tested the relevant code in this test file.

Impact

All tokens in the contract (from other promotions) can be stolen.

Check _numberOfEpochs <= 256 in createPromotion and _promotion.numberOfEpochs + _numberOfEpochs <= 256 in extendPromotion

#0 - PierrickGT

2021-12-13T16:14:53Z

Findings Information

🌟 Selected for report: cmichel

Labels

bug
1 (Low Risk)
sponsor acknowledged

Awards

280.3604 USDC - $280.36

External Links

Handle

cmichel

Vulnerability details

The TwabRewards.claimRewards function allows claiming the tokens of other users. While the tokens are sent to the correct address, this can lead to issues with smart contracts that might rely on claiming the tokens themselves.

As one example, suppose the _to address corresponds to a smart contract that has a function of the following form:

solidity function claimAndDoSomething(epochs) { uint256 claimedAmount = token.balanceOf(address(this)); twabRewards.claimRewards(address(this), 123, epochs); claimedAmount = token.balanceOf(address(this)) - claimedAmount; token.transfer(externalWallet, claimedAmount); }

Impact

If the contract has no other functions to transfer out funds, they may be locked forever in this contract. Claiming can also incur a taxable event and the timing is better left to the actual owner.

Do not allow users to claim on behalf of other addresses.

#0 - PierrickGT

2021-12-15T22:13:34Z

It's a design pattern that we use pretty regularly across our code base. In order to avoid scenarios like the one described in the issue, we expect developers to pay attention when calling this function. About taxable events, this is a relevant concern and we have not yet heard any user complain about someone claiming on their behalf. For these reasons, I've acknowledged the issue but we won't make any changes.

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