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

Hash collision in the PanopticMath.updatePositionsHash function could prompt wrong positions in the positionIdList to be validated #496

Closed
c4-bot-3 opened this issue Apr 22, 2024 · 4 comments
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working downgraded by judge Judge downgraded the risk level of this issue duplicate-498 🤖_178_group AI based duplicate group recommendation satisfactory satisfies C4 submission criteria; eligible for awards

Comments

@c4-bot-3
Copy link
Contributor

Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/libraries/PanopticMath.sol#L100-L109
https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticPool.sol#L1382-L1394

Vulnerability details

Impact

The PanopticPool._validatePositionList is a critical function in the Panoptic protocol. This function is used to ensure that the positions in the incoming user's list match the existing active option positions.

The _validatePositionList function is used by multiple critical functions in the protocol such as _mintOptions, _validateSolvency, liquidate, forceExercise and settleLongPremium. These functions ensure the solvency of the protocol and hence can be considered as highly critical functions for the health and sustainability of the protocol.

The PanopticPool._validatePositionList function calls the PanopticMath.updatePositionsHash in its execution flow. The PanopticMath.updatePositionsHash function is used to update an existing account's "positions hash" with a new single position tokenId. This updated position hash is calculated as shown below:

            uint248 updatedHash = uint248(existingHash) ^ 
                (uint248(uint256(keccak256(abi.encode(tokenId))))); 

But there is an issue with the above calculation since the hashed value of the encoded tokenId, by the keccak256 is truncated to a uint248 (from uint256) before performing XOR with the existingHash. This truncation is performed to keep the most significant 8 bytes to store the count of the positions.

Due to the above truncation a hash collision could occur where two different tokenIds with two different option positions could result in the same truncated uint248 hash value. As a result a malicious user can provide a manipulated tokenId inplace of a valid tokenId in the positionIdList, as an input to the PanopticPool._validatePositionList function such that when truncated (hash value) to uint248, the resulting updatedHash would equal to the existing active option positions stored in the s_positionsHash[account] mapping (stored in the storage).

As it was explained earlier since the _validatePositionList is used by critical functions such as _mintOptions, _validateSolvency, liquidate, forceExercise and settleLongPremium, the above vulnerability could create many undesired behaviors in the protocol and could break the protocol.

For example during the liquidate function call a malicious liquidator can use the above vulnerability to provide malicious positionIdListLiquidator and positionIdList as input TokenId[] arrays to the PanopticPool.liquidate function. If the malcious tokenId or tokenIds result in the same truncated hash as of the valid tokenIds in the storage option position, the transaction will proceed without revert.

As a result the malicious liquidator can liquidate a position with different position parameters for a given liquidatee which is an undesirable state to the protocol. This could lead to more liquidity being removed or added to the UniswapV3 pool than the valid position truly hold. This could further lead to broken state in the fee calculation as well. Further to the above a malicious liquidator can bypass the _checkSolvencyAtTick check in the liquidate function by providing malicoius tokenIds which would give him enough balance to stay solvent. This happens because the preceeding _validatePositionList check will not be able to identify the malicious tokenId in the positionIdListLiquidator array since the truncated hash (to uint248) matches the valid tokenId hash, stored in the storage (s_positionsHash[account]) for the given tokenId position.

The above vulnerability could create such undesired behavior and broken state in the other critical functions I explained above as well. This could lead the panoptic protocol to be insolvent as well.

Proof of Concept

        unchecked {
            // update hash by taking the XOR of the new tokenId
            uint248 updatedHash = uint248(existingHash) ^
                (uint248(uint256(keccak256(abi.encode(tokenId)))));
            // increment the top 8 bit if addflag=true, decrement otherwise
            return
                addFlag
                    ? uint256(updatedHash) + (((existingHash >> 248) + 1) << 248)
                    : uint256(updatedHash) + (((existingHash >> 248) - 1) << 248);
        }

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/libraries/PanopticMath.sol#L100-L109

        for (uint256 i = 0; i < pLength; ) {
            fingerprintIncomingList = PanopticMath.updatePositionsHash(
                fingerprintIncomingList,
                positionIdList[i],
                ADD
            );
            unchecked {
                ++i;
            }
        }

        // revert if fingerprint for provided '_positionIdList' does not match the one stored for the '_account'
        if (fingerprintIncomingList != currentHash) revert Errors.InputListFail();

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticPool.sol#L1382-L1394

Tools Used

Manual Review and VSCode

Recommended Mitigation Steps

Hence it is recommended to use the complete hash value of the tokenId (uint256) when updatedHash is calculated in the PanopticMath.updatePositionsHash function. This will ensure no hash collision will occur for two different tokenIds. To save the count of the positions a separate state variable can be used in the PanopticPool contract.

Assessed type

Other

@c4-bot-3 c4-bot-3 added 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working labels Apr 22, 2024
c4-bot-10 added a commit that referenced this issue Apr 22, 2024
@c4-bot-13 c4-bot-13 added the 🤖_178_group AI based duplicate group recommendation label Apr 22, 2024
@c4-judge
Copy link
Contributor

Picodes marked the issue as duplicate of #498

@c4-judge c4-judge added duplicate-498 and removed 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value labels Apr 25, 2024
@c4-judge
Copy link
Contributor

Picodes changed the severity to 3 (High Risk)

@c4-judge c4-judge added 3 (High Risk) Assets can be stolen/lost/compromised directly upgraded by judge Original issue severity upgraded from QA/Gas by judge labels Apr 30, 2024
@c4-judge
Copy link
Contributor

c4-judge commented May 6, 2024

Picodes marked the issue as satisfactory

@c4-judge c4-judge added satisfactory satisfies C4 submission criteria; eligible for awards 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value and removed 3 (High Risk) Assets can be stolen/lost/compromised directly upgraded by judge Original issue severity upgraded from QA/Gas by judge labels May 6, 2024
@c4-judge
Copy link
Contributor

c4-judge commented May 9, 2024

Picodes changed the severity to 2 (Med Risk)

@c4-judge c4-judge added the downgraded by judge Judge downgraded the risk level of this issue label May 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working downgraded by judge Judge downgraded the risk level of this issue duplicate-498 🤖_178_group AI based duplicate group recommendation satisfactory satisfies C4 submission criteria; eligible for awards
Projects
None yet
Development

No branches or pull requests

3 participants