Paladin - Warden Pledges contest - rvierdiiev's results

A governance lending protocol transforming users voting power into a new money lego.

General Information

Platform: Code4rena

Start Date: 27/10/2022

Pot Size: $33,500 USDC

Total HM: 8

Participants: 96

Period: 3 days

Judge: kirk-baird

Total Solo HM: 1

Id: 176

League: ETH

Paladin

Findings Distribution

Researcher Performance

Rank: 54/96

Findings: 2

Award: $29.55

QA:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

9.9073 USDC - $9.91

Labels

bug
2 (Med Risk)
satisfactory
sponsor confirmed
duplicate-68

External Links

Lines of code

https://github.com/code-423n4/2022-10-paladin/blob/main/contracts/WardenPledge.sol#L653-L661 https://github.com/code-423n4/2022-10-paladin/blob/main/contracts/WardenPledge.sol#L585-L592

Vulnerability details

Impact

Hacked or malicious admin can transfer all tokens of WardenPledge to himself.

Proof of Concept

WardenPledge.recoverERC20 function allows to transfer tokens controlled by contract if the token is not whitelisted.

    function recoverERC20(address token) external onlyOwner returns(bool) {
        if(minAmountRewardToken[token] != 0) revert Errors.CannotRecoverToken();


        uint256 amount = IERC20(token).balanceOf(address(this));
        if(amount == 0) revert Errors.NullValue();
        IERC20(token).safeTransfer(owner(), amount);


        return true;
    }

The check if(minAmountRewardToken[token] != 0) revert Errors.CannotRecoverToken(); is done to not allow admin to send tokens that are used for sending rewards to himself. But there is another function that allows admin to remove token from whitelisted.

    function removeRewardToken(address token) external onlyOwner {
        if(token == address(0)) revert Errors.ZeroAddress();
        if(minAmountRewardToken[token] == 0) revert Errors.NotAllowedToken();
        
        minAmountRewardToken[token] = 0;
        
        emit RemoveRewardToken(token);
    }

Using this function admin can first remove token from whitelisted and then recover tokens to himself.

I believe this is important thing and protocol should secure this. Because even if admin is not malicious, but ownership somehow was hacked, this gives ability for the attacker to get all funds and pausing can't help here.

Tools Used

VsCode

You can disallow to blocklist token if the WardenPledge balance is not 0.

#0 - Kogaroshi

2022-10-30T22:39:51Z

PoC of the Issue is correct, but Remediation brings another issue in the system: in the case of a malicious or bugged token that was whitelisted, and currently in use for a Pledge, that needs to be removed from the whitelist, this would be impossible because the contract balance won't be 0.

Will look for an in-between that could prevent the scenario doing most of the damages to the system.

#1 - Kogaroshi

2022-11-01T13:58:42Z

Fixed in PR 2, commit Decided on a different way to fix this:

  • tracking all token amounts currently belonging to Pledges
  • change the recoverERC20() method to ignore the whitelist, but forbidding the method to touch any token belonging to Pledges.

That way, admin has no way to touch any Pledge rewards or rug any funds, even after removing a token from the whitelist. The new system also allows to retrieve the reward token sent directly to the contract by mistake, without touching any Pledge rewards.

#2 - c4-judge

2022-11-09T21:29:02Z

kirk-baird marked the issue as satisfactory

#3 - c4-judge

2022-11-09T21:51:05Z

kirk-baird marked the issue as primary issue

#4 - c4-judge

2022-12-06T17:32:42Z

Simon-Busch marked the issue as duplicate of #68

1. Use solidity variable instead of calculations

No need to calculate how much is 1 week in seconds. You can use solidity 1 weeks variable instead. uint256 public constant WEEK = 1 weeks; https://github.com/code-423n4/2022-10-paladin/blob/main/contracts/WardenPledge.sol#L24

2. Use constant instead of magic number

Use constant instead of magic number 500 inside updatePlatformFee function. https://github.com/code-423n4/2022-10-paladin/blob/main/contracts/WardenPledge.sol#L626

3. WardenPledge can be initialized with 0 chest address which leads to unpredictable behaviour of protocol.

In constructor of WardenPledge there is no check that chest address is not 0. However it is in setter. chest address is used in different places of code to transfer protocol fees to it using IERC20(rewardToken).safeTransferFrom(creator, chestAddress, vars.feeAmount);. If chest value was initialized as 0 using constructor then the behaviour of protocol will depend on reward token. Different reward tokens will give different results. Some will revert, because of sending to 0 address, while someone will still work. As the result, in one option protocol will be working with reward token, but will burn protocol fees and in another option, protocol will revert when sending fees.

Make sure that chest != address(0) in constructor.

#0 - c4-judge

2022-11-09T06:48:36Z

kirk-baird marked the issue as grade-b

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