VTVL contest - hansfriese's results

Building no-code token management tools to empower web3 founders and investors, starting with token vesting.

General Information

Platform: Code4rena

Start Date: 20/09/2022

Pot Size: $30,000 USDC

Total HM: 12

Participants: 198

Period: 3 days

Judge: 0xean

Total Solo HM: 2

Id: 164

League: ETH

VTVL

Findings Distribution

Researcher Performance

Rank: 10/198

Findings: 3

Award: $636.42

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: Trust

Also found by: 0xSky, CertoraInc, KIntern_NA, bin2chen, hansfriese, neko_nyaa, neumo, rokinot, wastewa

Labels

bug
duplicate
3 (High Risk)

Awards

388.9184 USDC - $388.92

External Links

Lines of code

https://github.com/code-423n4/2022-09-vtvl/blob/f68b7f3e61dad0d873b5b5a1e8126b839afeab5f/contracts/VTVLVesting.sol#L176

Vulnerability details

Impact

In VTVLVesting._baseVestedAmount(), the funds might be locked inside the contract forever with uint112 overflow.

Currently, it doesn't consider uint112 overflow during multiply and it's very likely to happen when the vesting duration is not short like 1 year.

In this case, the funds for that claim will be locked inside the contract permanently.

Proof of Concept

As we can see here, the allowed token amount for uint112 is around 5e15 after ignoring 18 decimals.

But when the vest duration is 1 year here, truncatedCurrentVestingDurationSecs is 3600 * 24 * 365 = 31536000.

So if token amount is greater than 5e15 / 31536000 = 1.6e8, it will revert with uint112 overflow and below scenario would be possible.

  • The Admin created a claim for a user A. linearVestAmount = 2e8, VestDuration(= endTimestamp - startTimestamp) = 2 years.
  • After 1 year, A tried to withdraw the available amount but it's failed here.
  • Admin can't revoke the claim also because it will revert also with this function because final verst duration is longer than 1 year.
  • So the funds for this claim can't be withdrawn forever.

It will be easier to happen when the token decimals are longer than 18.

Tools Used

Solidity Visual Developer of VSCode

We should modify this calculation like below.

uint112 linearVestAmount = uint112(((uint256)_claim.linearVestAmount) * truncatedCurrentVestingDurationSecs / finalVestingDurationSecs);

#0 - 0xean

2022-09-24T19:19:15Z

dupe of #95

Findings Information

🌟 Selected for report: Czar102

Also found by: 0xSmartContract, Lambda, Respx, csanuragjain, hansfriese, sashik_eth

Labels

bug
duplicate
2 (Med Risk)

Awards

228.641 USDC - $228.64

External Links

Lines of code

https://github.com/code-423n4/2022-09-vtvl/blob/f68b7f3e61dad0d873b5b5a1e8126b839afeab5f/contracts/VTVLVesting.sol#L398

Vulnerability details

Impact

The reentrance is possible with VTVL.withdrawAdmin().

As we can see here, the contract should contain enough balance for users' active claims so that users can trust they get receive the shares if the claims aren't revoked by admin.

But the admin can withdraw all funds using VTVL.withdrawAdmin() and users can't receive as expected even though the claims are active.

Proof of Concept

As we can see here, the token for this contract can be any ERC20 token and we can consider it's possible to be an ERC777 token.

Also, we can assume the token contains a hook _beforeTokenTransfer() like this.

  • The contract has 100 tokens, numTokensReservedForVesting = 90.
  • The admin calls withdrawAdmin() using _amountRequested = 10.
  • At the first time, this require() is true and transfer will be started.
  • But before it updates the token balances of the contract, the admin receives a hook _beforeTokenTransfer() and he calls withdrawAdmin() again with the same param.
  • Then tokenAddress.balanceOf(address(this)) will be still 100 because the balance is not updated yet and it will work properly.
  • Admin continues recalling 10 times and he can withdraw all of 100 tokens and users can't get the shares with the active claims.
  • When the admin revokes the claims using revokeClaim(), there is an opportunity that users can withdraw the available tokens by front-running but they wouldn't take care of withdrawAdmin() as it's designed to withdraw some free tokens.

Even though the admin can create/revoke claims as he wants, the above case should be fixed.

Tools Used

Solidity Visual Developer of VSCode

Recommend using the ReentrancyGuard library of Openzeppelin

#0 - 0xean

2022-09-25T13:56:54Z

dupe of #6

Awards

18.8574 USDC - $18.86

Labels

bug
duplicate
QA (Quality Assurance)

External Links

Lines of code

https://github.com/code-423n4/2022-09-vtvl/blob/f68b7f3e61dad0d873b5b5a1e8126b839afeab5f/contracts/AccessProtected.sol#L39

Vulnerability details

Impact

AccessProtected.setAdmin() can enable/disable admin access and msg.sender(active admin) can disable himself using this function.

So if admin disables himself by fault, admin roles will be lost forever.

Proof of Concept

I don't think the admin might disable himself when there is only one admin.

But below scenario would be possible.

  • There are 2 admins A and B now and they discussed to remove one of them.
  • So A removed his role by calling setAdmin(A, false).
  • But B forgot which admin to remove and removed his account also at the same time.
  • Then they won't have admin roles forever.

Tools Used

Solidity Visual Developer of VSCode

I think it would be good to add more requirements for this function.

We can modify msg.sender can't disable himself like below.

function setAdmin(address admin, bool isEnabled) public onlyAdmin { require(admin != address(0), "INVALID_ADDRESS"); if(!isEnabled) { require(admin != msg.sender, "can't disable"); } _admins[admin] = isEnabled; emit AdminAccessSet(admin, isEnabled); }

#0 - 0xean

2022-09-23T23:25:53Z

dupe of #469

#1 - 0xean

2022-10-09T23:49:47Z

downgraded to QA

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