VTVL contest - dipp'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: 24/198

Findings: 1

Award: $296.39

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: CertoraInc

Also found by: 0xhunter, Lambda, datapunk, dipp, wuwe1

Labels

bug
duplicate
2 (Med Risk)

Awards

296.3865 USDC - $296.39

External Links

Lines of code

https://github.com/code-423n4/2022-09-vtvl/blob/main/contracts/VTVLVesting.sol#L446-L451

Vulnerability details

Impact

Some tokens may be deployed through a proxy and so will have 2 entrypoints for its functions, the proxy contract and the implementation contract. Since any ERC20 token may be used in VTVLVesting.sol, these proxy tokens could be used.

The withdrawAdmin function in VTVLVesting.sol is used by an admin to withdraw the contract's vesting tokens (tokenAddress) that have not been allocated for vesting. An additional withdrawOtherToken function may also be used to withdraw any token from the VTVLVesting.sol contract that is not the vesting token. If a proxy token is used as the vesting token, an admin could call withdrawOtherToken with the tokenAddress's implementation contract as input to bypass the restrictions in withdrawAdmin, allowing the admin to withdraw all of the vesting tokens while the contract still has active and unfulfilled claims.

Although this could seem redundant, since an admin could just call revokeClaim on all active claims and then call withdrawAdmin to withdraw all vesting tokens, the withdrawOtherToken should not be able to withdraw any vesting tokens.

Proof of Concept

The withdrawOtherToken in VTVLVesting.sol:

    function withdrawOtherToken(IERC20 _otherTokenAddress) external onlyAdmin {
        require(_otherTokenAddress != tokenAddress, "INVALID_TOKEN"); // tokenAddress address is already sure to be nonzero due to constructor
        uint256 bal = _otherTokenAddress.balanceOf(address(this));
        require(bal > 0, "INSUFFICIENT_BALANCE");
        _otherTokenAddress.safeTransfer(_msgSender(), bal);
    }

The admin is able to bypass calling the proxy of the token by supply the address of its implementation contract as the _otherTokenAddress.

A quick fix might be to check that vesting token's balance remains unchanged at the end of withdrawOtherToken. This would still allow admins to use any token as the vesting token, but would not allow withdrawOtherToken to transfer out vesting tokens.

#0 - 0xean

2022-09-25T14:04:20Z

dupe of #429

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