Paladin - Warden Pledges contest - ctf_sec'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: 19/96

Findings: 2

Award: $267.17

QA:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: rbserver

Also found by: 0x1f8b, 0xSmartContract, Trust, cccz, codexploder, ctf_sec, hansfriese

Labels

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

Awards

247.5252 USDC - $247.53

External Links

Lines of code

https://github.com/code-423n4/2022-10-paladin/blob/d6d0c0e57ad80f15e9691086c9c7270d4ccfe0e6/contracts/WardenPledge.sol#L636 https://github.com/code-423n4/2022-10-paladin/blob/d6d0c0e57ad80f15e9691086c9c7270d4ccfe0e6/contracts/WardenPledge.sol#L456 https://github.com/code-423n4/2022-10-paladin/blob/d6d0c0e57ad80f15e9691086c9c7270d4ccfe0e6/contracts/WardenPledge.sol#L488

Vulnerability details

Impact

Creator fund is stucked in the contract if the admin owner pause the contract and never unpause the contract.

In current implementation, the admin can pause the contract,

/**
 * @notice Pauses the contract
 */
function pause() external onlyOwner {
	_pause();
}

/**
 * @notice Unpauses the contract
 */
function unpause() external onlyOwner {
	_unpause();
}

after contract is paused, the user is not able to pledge and get reward.

 function pledge(uint256 pledgeId, uint256 amount, uint256 endTimestamp) external whenNotPaused

but the creator is not able to call

function closePledge(uint256 pledgeId, address receiver) external whenNotPaused nonReentrant
function retrievePledgeRewards(uint256 pledgeId, address receiver) external whenNotPaused nonReentrant 

then the creator fund that means to reward the pledged user is locked in the contract as long as the creator not unpause the contract.

Proof of Concept

Admin invokes the pause function

function pause() external onlyOwner

Creator cannot retrieve pledge reward or close pledge because these two function use the modifier whenNotPaused

Tools Used

Manual Review

Usually in a reward distribution contract, the admin can pause the contract disable inbound action, but not blocking the outbound action.

For example,

in a staking contract, the user can stake token for reward, admin can pause the contract to not let user stake more token. but should not block user from withdraw their staked token when pausing the contract.

Same ideas applys here, we recommend the project remove WhenNotPaused in function below

function closePledge
function retrievePledgeRewards

#0 - Kogaroshi

2022-10-31T20:19:48Z

Fixed in PR 2, commit

#1 - c4-judge

2022-11-11T07:46:13Z

kirk-baird marked the issue as primary issue

#2 - c4-judge

2022-11-11T08:05:53Z

kirk-baird marked the issue as satisfactory

#3 - c4-judge

2022-12-06T17:36:20Z

Simon-Busch marked the issue as duplicate of #269

Lines of code

https://github.com/code-423n4/2022-10-paladin/blob/d6d0c0e57ad80f15e9691086c9c7270d4ccfe0e6/contracts/WardenPledge.sol#L265

Vulnerability details

Impact

the code below calculate the reward that user eligible to claim after user delegates the veToken voting power.

// Rewards are set in the Pledge as reward/veToken/sec
// To find the total amount of veToken delegated through the whole Boost duration
// based on the Boost bias & the Boost duration, to take in account that the delegated amount decreases
// each second of the Boost duration
uint256 totalDelegatedAmount = ((bias * boostDuration) + bias) / 2;
// Then we can calculate the total amount of rewards for this Boost
uint256 rewardAmount = (totalDelegatedAmount * pledgeParams.rewardPerVote) / UNIT;

if(rewardAmount > pledgeAvailableRewardAmounts[pledgeId]) revert Errors.RewardsBalanceTooLow();
pledgeAvailableRewardAmounts[pledgeId] -= rewardAmount;

// Send the rewards to the user
IERC20(pledgeParams.rewardToken).safeTransfer(user, rewardAmount);

note the logic

// each second of the Boost duration
uint256 totalDelegatedAmount = ((bias * boostDuration) + bias) / 2;
// Then we can calculate the total amount of rewards for this Boost
uint256 rewardAmount = (totalDelegatedAmount * pledgeParams.rewardPerVote) / UNIT;

if (totalDelegatedAmount * pledgeParams.rewardPerVote) / UNIT is 0, the user is not getting any reward.

Proof of Concept

given

uint256 rewardAmount = (totalDelegatedAmount * pledgeParams.rewardPerVote) / UNIT;

UNIT is hardcoded: 10 ** 18 if pledgeParams.rewardPerVote is a small number, such as 1 wei. and totalDelegatedAmount is for example, 100000000,

100000000 * 1 / UNIT is rounded to 0.

User delegates the vote power but does not get any reward.

Tools Used

Manual Review.

Please add check to make sure that the user can get the reward after pledging.

uint256 rewardAmount = (totalDelegatedAmount * pledgeParams.rewardPerVote) / UNIT;
if(rewardAmount == 0) revert ZeroRewardAmount();

#0 - trust1995

2022-10-30T22:04:27Z

It's fine because totalDelegatedAmount uses UNIT decimals.

#1 - Kogaroshi

2022-10-30T22:54:24Z

Invalid, the Pledge reward per vote must be higher than the value minAmountRewardToken for the used token rewards, so the admin can enforce a minimal value preventing such issues.

#2 - kirk-baird

2022-11-11T21:10:01Z

While totalDelegatedAmount does use UINT decimals it's possible to round to zero still. However, minAmountRewardToken is in place to prevent the rounding to zero.

If mintAmountRewardToken is not set adequately for this token it's possible to round to zero. I'm going to consider this issue QA since it requires a misconfiguration to be exploited.

#3 - c4-judge

2022-11-11T21:10:12Z

kirk-baird changed the severity to QA (Quality Assurance)

#4 - c4-judge

2022-11-11T23:59:32Z

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