Skip to content
This repository has been archived by the owner on Apr 28, 2024. It is now read-only.

0x52 - Adversary can overwrite function selector in _patchAmountAndCall due to inline assembly lack of overflow protection #82

Open
sherlock-admin2 opened this issue Oct 23, 2023 · 4 comments
Assignees
Labels
Medium A valid Medium severity issue Reward A payout will be made for this issue Sponsor Confirmed The sponsor acknowledged this issue is valid Will Fix The sponsor confirmed this issue will be fixed

Comments

@sherlock-admin2
Copy link
Contributor

sherlock-admin2 commented Oct 23, 2023

0x52

high

Adversary can overwrite function selector in _patchAmountAndCall due to inline assembly lack of overflow protection

Summary

When using inline assembly, the standard overflow/underflow protections do not apply. This allows an adversary to specify a swapAmountInDataIndex which after multiplication and addition allows them to overwrite the function selector. Using a created token in a UniV3 LP pair they can manufacture any value for swapAmountInDataValue.

Vulnerability Detail

The use of YUL or inline assembly in a solidity smart contract also makes integer overflow/ underflow possible even if the compiler version of solidity is 0.8. In YUL programming language, integer underflow & overflow is possible in the same way as Solidity and it does not check automatically for it as YUL is a low-level language that is mostly used for making the code more optimized, which does this by omitting many opcodes. Because of its low-level nature, YUL does not perform many security checks therefore it is recommended to use as little of it as possible in your smart contracts.

Source

Inline assembly lacks overflow/underflow protections, which opens the possibility of this exploit.

ExternalCall.sol#L27-L38

        if gt(swapAmountInDataValue, 0) {
            mstore(add(add(ptr, 0x24), mul(swapAmountInDataIndex, 0x20)), swapAmountInDataValue)
        }
        success := call(
            maxGas,
            target,
            0, //value
            ptr, //Inputs are stored at location ptr
            data.length,
            0,
            0
        )

In the code above we see that swapAmountInDataValue is stored at ptr + 36 (0x24) + swapAmountInDataIndex * 32 (0x20). The addition of 36 (0x24) in this scenario should prevent the function selector from being overwritten because of the extra 4 bytes (using 36 instead of 32). This is not the case though because mul(swapAmountInDataIndex, 0x20) can overflow since it is a uint256. This allows the attacker to target any part of the memory they choose by selectively overflowing to make it write to the desired position.

As shown above, overwriting the function selector is possible although most of the time this value would be a complete nonsense since swapAmountInDataValue is calculated elsewhere and isn't user supplied. This also has a work around. By creating their own token and adding it as LP to a UniV3 pool, swapAmountInDataValue can be carefully manipulated to any value. This allows the attacker to selectively overwrite the function selector with any value they chose. This bypasses function selectors restrictions and opens calls to dangerous functions.

Impact

Attacker can bypass function restrictions and call dangerous/unintended functions

Code Snippet

ExternalCall.sol#L14-L47

Tool used

Manual Review

Recommendation

Limit swapAmountInDataIndex to a reasonable value such as uint128.max, preventing any overflow.

@github-actions github-actions bot added the Medium A valid Medium severity issue label Oct 26, 2023
@fann95 fann95 added Sponsor Disputed The sponsor disputed this issue's validity Won't Fix The sponsor confirmed this issue will not be fixed labels Oct 29, 2023
@fann95
Copy link

fann95 commented Oct 29, 2023

I don't think this is a realistic scenario. You won't be able to get the offset to the function selector by overflowing the results of the multiplication or addition here. Using which index can you get the mload(0x40) ?

@fann95 fann95 self-assigned this Oct 29, 2023
@Czar102
Copy link

Czar102 commented Oct 30, 2023

I think overwriting the selector could be done if the swapAmountInDataIndex was k * 2 ** 251 - 2 for any integer k. This is because swapAmountInDataIndex * 0x20 = k * 2 ** 251 * 2 ** 5 - 2 * 2 ** 5 = k * 2 ** 256 - 64 = - 64 mod 2 ** 256. Changing memory at a pointer with such modification would collide with the selector at 4 least significant bytes and would write 28 bytes to previously allocated memory (memory corruption).
My recommendation would be to limit swapAmountInDataIndex to div(data.length, 0x20). Then, one would be able to write swapAmountInDataValue at any correct index in the calldata or right behind the calldata, if that amount is not to show up in the function call.

@fann95 fann95 added Sponsor Confirmed The sponsor acknowledged this issue is valid Will Fix The sponsor confirmed this issue will be fixed and removed Sponsor Disputed The sponsor disputed this issue's validity Won't Fix The sponsor confirmed this issue will not be fixed labels Oct 30, 2023
@sherlock-admin2 sherlock-admin2 changed the title Ancient Malachite Jay - Adversary can overwrite function selector in _patchAmountAndCall due to inline assembly lack of overflow protection 0x52 - Adversary can overwrite function selector in _patchAmountAndCall due to inline assembly lack of overflow protection Oct 30, 2023
@sherlock-admin2 sherlock-admin2 added the Reward A payout will be made for this issue label Oct 30, 2023
@fann95
Copy link

fann95 commented Oct 31, 2023

Fixed: RealWagmi/wagmi-leverage@b568b21

@IAm0x52
Copy link

IAm0x52 commented Nov 16, 2023

Fix looks good. Indexes that indicate a position larger then the calldata size cause the function to revert.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Medium A valid Medium severity issue Reward A payout will be made for this issue Sponsor Confirmed The sponsor acknowledged this issue is valid Will Fix The sponsor confirmed this issue will be fixed
Projects
None yet
Development

No branches or pull requests

4 participants