Lybra Finance - Brenzee's results

A protocol building the first interest-bearing omnichain stablecoin backed by LSD.

General Information

Platform: Code4rena

Start Date: 23/06/2023

Pot Size: $60,500 USDC

Total HM: 31

Participants: 132

Period: 10 days

Judge: 0xean

Total Solo HM: 10

Id: 254

League: ETH

Lybra Finance

Findings Distribution

Researcher Performance

Rank: 26/132

Findings: 3

Award: $432.66

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: No12Samurai

Also found by: 0xRobocop, Brenzee, Toshii, kutugu

Labels

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

Awards

145.801 USDC - $145.80

External Links

Lines of code

https://github.com/code-423n4/2023-06-lybra/blob/481520a1440e2637e94c1200e798841b47825a91/contracts/lybra/miner/ProtocolRewardsPool.sol#L201

Vulnerability details

Impact

In ProtocolRewardsPool.getReward function, peUSDBalance gets compared to eUSD shares, which may cause unexpected actions for user, who is trying to withdraw his reward, and for protocol, who unexpectedly would send different tokens.

Proof of Concept

In getReward:L201 function checks, if peUSDBalance >= reward - eUSDShare.

                if(peUSDBalance >= reward - eUSDShare) {
                    peUSD.transfer(msg.sender, reward - eUSDShare);
                    emit ClaimReward(msg.sender, EUSD.getMintedEUSDByShares(eUSDShare), address(peUSD), reward - eUSDShare, block.timestamp);
                } else {
                    if(peUSDBalance > 0) {
                        peUSD.transfer(msg.sender, peUSDBalance);
                    }
                    ERC20 token = ERC20(configurator.stableToken());
                    uint256 tokenAmount = (reward - eUSDShare - peUSDBalance) * token.decimals() / 1e18;
                    token.transfer(msg.sender, tokenAmount);
                    emit ClaimReward(msg.sender, EUSD.getMintedEUSDByShares(eUSDShare), address(token), reward - eUSDShare, block.timestamp);
                }

The issue is that peUSDBalance is not the same as eUSD shares. Proof - in converToPeUSD function in PeUSDMainnetStableVision.sol contract eusdAmount, not eUSD share, gets converted to peUSD token.

This means that getReward contract may unexpectedly send configurator.stableToken instead of peUSD token, which may be a loss for a protocol.

Tools Used

Manual review

Make sure that peUSDBalance gets converted to shared before it is compared to reward and eUSDShare

Assessed type

Context

#0 - c4-pre-sort

2023-07-11T15:42:07Z

JeffCX marked the issue as primary issue

#1 - c4-sponsor

2023-07-18T09:02:35Z

LybraFinance marked the issue as sponsor confirmed

#2 - c4-judge

2023-07-26T13:28:48Z

0xean marked the issue as duplicate of #869

#3 - c4-judge

2023-07-28T15:36:58Z

0xean marked the issue as satisfactory

#4 - c4-judge

2023-07-29T22:06:38Z

0xean marked the issue as duplicate of #604

Findings Information

🌟 Selected for report: SpicyMeatball

Also found by: Brenzee, Kenshin, Musaka

Labels

bug
2 (Med Risk)
downgraded by judge
satisfactory
duplicate-442

Awards

202.5014 USDC - $202.50

External Links

Lines of code

https://github.com/code-423n4/2023-06-lybra/blob/824de2eefa30101622b65e43b79886667abad916/contracts/lybra/miner/EUSDMiningIncentives.sol#L153-L154 https://github.com/code-423n4/2023-06-lybra/blob/824de2eefa30101622b65e43b79886667abad916/contracts/lybra/miner/EUSDMiningIncentives.sol#L188-L190

Vulnerability details

Impact

Malicious users can manipulate the etherInLp and lbrInLp values which are used to calculate staked LBR LP value. As a result - malicious users in that way can purchase other people's earnings by manipulating the token amount in WETH/LBR Uniswap V2 pool with flash swaps, when it should not be possible.

Proof of Concept

The function stakedLBRLpValue calculates the user's value of staked tokens in ethlbrStakePool

    function stakedLBRLpValue(address user) public view returns (uint256) {
        uint256 totalLp = IEUSD(ethlbrLpToken).totalSupply();
        (, int etherPrice, , , ) = etherPriceFeed.latestRoundData();
        (, int lbrPrice, , , ) = lbrPriceFeed.latestRoundData();
        uint256 etherInLp = (IEUSD(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2).balanceOf(ethlbrLpToken) * uint(etherPrice)) / 1e8;
        uint256 lbrInLp = (IEUSD(LBR).balanceOf(ethlbrLpToken) * uint(lbrPrice)) / 1e8;
        uint256 userStaked = IEUSD(ethlbrStakePool).balanceOf(user);
        return (userStaked * (lbrInLp + etherInLp)) / totalLp;
    }

This function's returned value is used in the isOtherEarningsClaimable function, which determines if the user's earnings can be claimable by others.

    function isOtherEarningsClaimable(address user) public view returns (bool) {
        return (stakedLBRLpValue(user) * 10000) / stakedOf(user) < 500;
    }

The issue is that the balance of WETH and LBR tokens in WETH/LBR Uniswap V2 can be easily manipulated by calling flash swap on the pool, which withdraws the tokens from the pool.

Example

Bob has borrowed eUSD from one of the Vaults and staked in ethlbrStakePool After some time - Alice the attacker sees that if she does a flash swap on WETH/LBR pool (removes WETH and LBR tokens from the pool), she can claim Bob's earnings by calling purchaseOtherEarnings. That is because flash swap manipulates stakedLBRLpValue value.

As a result - When Bob thought he was not in danger to withdraw his rewards, Alice took advantage of the Uniswap V2 flash swap and took Bob's rewards.

Tools Used

Manual Review

It is not recommended to rely on Uniswap V2 pool balances, because they can be easily manipulated by calling flash swaps on them.

Assessed type

Uniswap

#0 - c4-pre-sort

2023-07-10T20:35:38Z

JeffCX marked the issue as duplicate of #442

#1 - c4-judge

2023-07-25T23:53:20Z

0xean changed the severity to 2 (Med Risk)

#2 - c4-judge

2023-07-28T15:42:01Z

0xean marked the issue as satisfactory

Findings Information

🌟 Selected for report: Musaka

Also found by: Brenzee, Bughunter101, HE1M, Jorgect, kutugu, pep7siup

Labels

bug
2 (Med Risk)
satisfactory
duplicate-223

Awards

84.3563 USDC - $84.36

External Links

Lines of code

https://github.com/code-423n4/2023-06-lybra/blob/481520a1440e2637e94c1200e798841b47825a91/contracts/lybra/miner/ProtocolRewardsPool.sol#L196-L197

Vulnerability details

Impact

In ProtocolRewardsPool.getReward function, if msg.sender rewards is 2x or more than the balance of eUSD tokens in ProtocolRewardsPool, the user will not be able to get any rewards

Proof of Concept

eUSDShare value in getReward function depends on whether the contract balance is more than the user rewards amount.

            uint256 eUSDShare = balance >= reward ? reward : reward - balance;

After that eUSDShare value is used in EUSD.transferShare function to send the rewards to user

            EUSD.transferShares(msg.sender, eUSDShare);

The issue is that if the reward is twice or more than the balance, getReward function will always fail.

Example

User rewards - 1000 Contract balance - 300

This means that eUSDShare amount will be 1000 - 300 = 700 Since the contract only has 300, EUSD.transferShares(msg.sender, eUSDShare) function will fail, because the contract doesn't have enough funds even though the expected action would be to send peUSD and configurator.stableToken if there is not enough eUSD in the contract

Tools Used

Manual Review

Change the eUSDShare line to the example below. This guarantees that the user can receive his reward even if there is not enough eUSD token.

            uint256 eUSDShare = balance >= reward ? reward : balance;

Assessed type

Token-Transfer

#0 - c4-pre-sort

2023-07-10T02:15:10Z

JeffCX marked the issue as duplicate of #869

#1 - c4-pre-sort

2023-07-10T13:49:14Z

JeffCX marked the issue as not a duplicate

#2 - c4-pre-sort

2023-07-10T13:49:39Z

JeffCX marked the issue as duplicate of #161

#3 - c4-judge

2023-07-28T15:44:23Z

0xean marked the issue as satisfactory

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