Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

storage collision #260

Closed
c4-bot-3 opened this issue Dec 4, 2023 · 4 comments
Closed

storage collision #260

c4-bot-3 opened this issue Dec 4, 2023 · 4 comments
Labels
3 (High Risk) Assets can be stolen/lost/compromised directly bug Something isn't working edited-by-warden primary issue Highest quality submission among a set of duplicates sponsor disputed Sponsor cannot duplicate the issue, or otherwise disagrees this is an issue unsatisfactory does not satisfy C4 submission criteria; not eligible for awards

Comments

@c4-bot-3
Copy link
Contributor

c4-bot-3 commented Dec 4, 2023

Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/main/contracts/SemiFungiblePositionManager.sol#L152
https://github.com/code-423n4/2023-11-panoptic/blob/main/contracts/SemiFungiblePositionManager.sol#L375-L378

Vulnerability details

Impact

In SemiFungiblePositionManager::initializeAMMPool we use FACTORY.getPool to get the pool from uniswap with token 0, token 1
and fee, it returns the address of the pool and then we use PanopticMath.getPoolId to get the first 8 bytes of the address.
The problem is that an attacker can create a collision because we only use 8 bytes. It seems that collision is difficult
to happen, but it is not that difficult if we use a fast language such as rust or go, where you can generate address that has
the same first 8 bytes. This is code written in go.

this code could generate billion of addresses in short period.
simple code to brute force first 8 bytes go

the impact here is that an attacker can change the storage here
because it will store changes to the same storage slot when first 8 bytes matches here.
so an attacker can change the storage and this will effect the whole protocol.

  • the protocol must not risk with such thing because it won't take long time until an attacker finds a collision.

Proof of Concept

simple code to brute force first 8 bytes go

NOTE: in this example we are going to use address provided in PanopticMath: 0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8
as and example

  1. attacker uses Golang code i provided to brute force Token address simulating createPool
  2. when Golang code finds the targeted first 8 bytes he uses createPool to Create Pool USDC/TokenX:
    • token0: for example USDC.
    • token1: this is attacker's ERC20 token address (TokenX).
    • fee : fee
  • createPool will create pool with address 0x8ad599c3A0ff1De0fffffffffffffffffffffff
  1. then attacker calls SemiFungiblePositionManager::initializeAMMPool with parms: token0, token1, fee.
  2. getPool will return attacker's pool address:
    • 0x8ad599c3A0ff1De0ffffffffffffffffffffffff (just example)
  3. PanopticMath.getPoolId this will return first 8 bytes which is:
    • 0x8ad599c3A0ff1De0
  4. now if there is something stored in s_poolContext[uint64(0x8ad599c3A0ff1De0)] it will be change because of the collision with attacker's pool address
    and storage slot will be rewritten with attacker's pool address.
        s_poolContext[poolId] = PoolAddressAndLock({
            pool: IUniswapV3Pool(univ3pool), //  <= this will be rewritten
            locked: false
        });
    from 0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8 to 0x8ad599c3A0ff1De0ffffffffffffffffffffffff

Tools Used

manually

Recommended Mitigation Steps

    uint64 poolId = PanopticMath.getPoolId(univ3pool);
    
+   if(address(s_poolContext[poolId].pool) != address(0)) revert PoolId_Exits(); 

    while (address(s_poolContext[poolId].pool) != address(0)) {
        poolId = PanopticMath.getFinalPoolId(poolId, token0, token1, fee);
    }
    // store the poolId => UniswapV3Pool information in a mapping
    // `locked` can be initialized to false because the pool address makes the slot nonzero
    s_poolContext[poolId] = PoolAddressAndLock({
        pool: IUniswapV3Pool(univ3pool),
        locked: false
    });

this would be huge problem for the protocol in long term.

Assessed type

Other

@c4-bot-3 c4-bot-3 added 3 (High Risk) Assets can be stolen/lost/compromised directly bug Something isn't working labels Dec 4, 2023
c4-bot-10 added a commit that referenced this issue Dec 4, 2023
@c4-judge c4-judge added the primary issue Highest quality submission among a set of duplicates label Dec 13, 2023
@c4-judge
Copy link
Contributor

Picodes marked the issue as primary issue

@c4-sponsor
Copy link

dyedm1 (sponsor) disputed

@c4-sponsor c4-sponsor added the sponsor disputed Sponsor cannot duplicate the issue, or otherwise disagrees this is an issue label Dec 17, 2023
@dyedm1
Copy link
Member

dyedm1 commented Dec 17, 2023

This line while (address(s_poolContext[poolId].pool) != address(0)) { poolId = PanopticMath.getFinalPoolId(poolId, token0, token1, fee); }

prevents any such storage collision from occuring as described (and has the same functionality of the suggested check, but the added benefit of not precluding multiple pools with the same last 64 bits from being deployed)

@c4-judge
Copy link
Contributor

Picodes marked the issue as unsatisfactory:
Invalid

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3 (High Risk) Assets can be stolen/lost/compromised directly bug Something isn't working edited-by-warden primary issue Highest quality submission among a set of duplicates sponsor disputed Sponsor cannot duplicate the issue, or otherwise disagrees this is an issue unsatisfactory does not satisfy C4 submission criteria; not eligible for awards
Projects
None yet
Development

No branches or pull requests

5 participants