Olas - BugzyVonBuggernaut's results

Olas is a unified network for off-chain services like automation, oracles, co-owned AI. It offers a stack for building services and a protocol for incentivizing their creation and their operation in a co-owned and decentralized way.

General Information

Platform: Code4rena

Start Date: 21/12/2023

Pot Size: $90,500 USDC

Total HM: 10

Participants: 39

Period: 18 days

Judge: LSDan

Total Solo HM: 5

Id: 315

League: ETH

Olas

Findings Distribution

Researcher Performance

Rank: 4/39

Findings: 2

Award: $3,690.64

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: erebus

Also found by: BugzyVonBuggernaut, hash

Labels

bug
3 (High Risk)
satisfactory
sufficient quality report
duplicate-341

Awards

2460.4271 USDC - $2,460.43

External Links

Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/liquidity_lockbox.sol#L223-L225

Vulnerability details

Impact

liquidity_lockbox#withdraw is at high risk of a permanent DOS due to "0" amount liquidity positions

Proof of Concept

liquidity_lockbox.sol#L95-L97

if (positionData.liquidity > type(uint64).max) {
    revert("Liquidity overflow");
}

There is no minimum amount liquidity check during LP deposits.

liquidity_lockbox.sol#L223-L225

    function withdraw(uint64 amount) external {
        address positionAddress = positionAccounts[firstAvailablePositionAccountIndex];
        if (positionAddress != tx.accounts.position.key) {
            revert("Wrong liquidity token account");
        }

        address pdaPositionAta = tx.accounts.pdaPositionAccount.key;
        if (mapPositionAccountPdaAta[positionAddress] != pdaPositionAta) {
            revert("Wrong position ATA");
        }

        uint64 positionLiquidity = mapPositionAccountLiquidity[positionAddress];
        // Check that the token account exists
        if (positionLiquidity == 0) {
            revert("No liquidity on a provided token account");
        }

However, if the next available firstAvailablePositionAccountIndex has zero positionLiquidity, the position will revert.

liquidity_lockbox.sol#L321

firstAvailablePositionAccountIndex++;

The index only increments at the end of a successful withdraw call.

whirlpool-impl.ts#L268

invariant(liquidity.gt(new BN(0)), "liquidity must be greater than zero");

When depositing and opening a position via the Orca SDK, there is a check to ensure that the liquidity deposited is greater than 0.

However, this check is part of the SDK, not the on-chain code.

lib.rs#L160-L182

/// Open a position in a Whirlpool. A unique token will be minted to represent the position
/// in the users wallet. The position will start off with 0 liquidity.

So long as the SDK is bypassed, a 0 liquidity position can be open.

Due to the existing CPI issues between Autonolas and Orca, it's very difficult to prove the DOS directly. However, it is possible to demonstrate how to open a 0 liquidity position on Orca by removing the frontend invariant check and running their test suite.

For that please refer to this gist. Note that due to the changes, various tests will fail, but the one we are interested in that does pass is: open and add liquidity to a position, then close

Tools Used

VSC, Anchor

The required step is to include a minimum liquidity constraint on Lockbox deposits.

An optional second step is to address the 0 position liquidity check in withdraw.

if (positionLiquidity == 0) {
	revert("No liquidity on a provided token account");
}

One option is to remove it entirely, or remove the revert and instead increment the index and exit the function.

if (positionLiquidity == 0) {
	firstAvailablePositionAccountIndex++
	return
}

This would remove the risk of a 0 amount DOS in the event that it somehow occurs despite the extra check on deposits.

Assessed type

DoS

#0 - c4-pre-sort

2024-01-10T15:18:49Z

alex-ppg marked the issue as duplicate of #341

#1 - c4-pre-sort

2024-01-10T15:18:53Z

alex-ppg marked the issue as sufficient quality report

#2 - c4-judge

2024-01-19T20:05:01Z

dmvt marked the issue as satisfactory

Findings Information

🌟 Selected for report: EV_om

Also found by: BugzyVonBuggernaut

Labels

bug
2 (Med Risk)
satisfactory
sufficient quality report
duplicate-443

Awards

1230.2135 USDC - $1,230.21

External Links

Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/liquidity_lockbox.sol#L210-L211 https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/liquidity_lockbox.sol#L320-L321

Vulnerability details

Impact

Withdrawing non-programmatically from the lockbox becomes impossible in practice due to an attacker creating many small positions.

This is due to two existing conditions: a) No deposit lower bound b) Withdrawals happen in strict FIFO order.

Proof of Concept

liquidity_lockbox.sol#L210-L211

function withdraw(uint64 amount) external {
        address positionAddress = positionAccounts[firstAvailablePositionAccountIndex];

As outlined in the docs, lockbox withdrawals "checks whether the liquidity L associated with the least recently deposited NFT is larger than or equals to the requested withdrawal amount X. If this condition is met, the corresponding liquidity is liquidated, and the X amount of fungible tokens is burnt"

Importantly, "multiple withdrawal calls may be necessary, contingent on whether the user's fungible token amount, X, is greater than the least recently deposited NFT".

liquidity_lockbox.sol#L320-L321

// Increase the first available position account index
firstAvailablePositionAccountIndex++;

At the end of withdraw, the index is incremented.

liquidity_lockbox.sol#L140

There is no minimum deposit amount in liquidity_lockbox::deposit.

liquidity_lockbox.ts#L48

- const usdc_amount = DecimalUtil.toBN(new Decimal("10" /* usdc */), 6);
+ const usdc_amount = DecimalUtil.toBN(new Decimal("1" /* usdc */), 0);

Run the tests with the above adjustment to verify.

An attacker can grief lockbox users by creating thousands of small LP positions and forcing users to execute withdraw thousands of times in order to get their LP tokens back.

Tools Used

VSC

Include a minimum deposit amount in the lockbox and consider allowing users to withdraw from specific positions, or consider reordering the LP positions from large to small.

The latter two would circumvent the potential issue where even if there were many reasonable minimum deposits (1000 USD), a user dealing in much more size (1,000,000 USD) wouldn't have to call withdraw 1000 times or more.

Assessed type

Other

#0 - c4-pre-sort

2024-01-10T14:59:38Z

alex-ppg marked the issue as duplicate of #443

#1 - c4-pre-sort

2024-01-10T14:59:42Z

alex-ppg marked the issue as sufficient quality report

#2 - c4-judge

2024-01-19T20:09:26Z

dmvt 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