Good Entry - T1MOH's results

The best day trading platform to make every trade entry a Good Entry.

General Information

Platform: Code4rena

Start Date: 01/08/2023

Pot Size: $91,500 USDC

Total HM: 14

Participants: 80

Period: 6 days

Judge: gzeon

Total Solo HM: 6

Id: 269

League: ETH

Good Entry

Findings Distribution

Researcher Performance

Rank: 10/80

Findings: 3

Award: $1,120.80

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: said

Also found by: 0xmuxyz, LokiThe5th, Satyam_Sharma, T1MOH, Team_FliBit, radev_sw

Labels

bug
3 (High Risk)
satisfactory
upgraded by judge
duplicate-140

Awards

482.4792 USDC - $482.48

External Links

Lines of code

https://github.com/code-423n4/2023-08-goodentry/blob/71c0c0eca8af957202ccdbf5ce2f2a514ffe2e24/contracts/GeVault.sol#L367-L378

Vulnerability details

Impact

Function poolMatchesOracle() is called before deposit, withdraw, rebalance. This function overflows because of high sqrtPriceX96 on some pools, such as WBTC/ETH, WBTC/DAI

Proof of Concept

It is foundry test. Run command forge test --match-test testB -vvv --fork-url <MAINNET_RPC_URL> And you'll see that calculation overflows

pragma solidity 0.8.19;

import "forge-std/Test.sol";
import "../src/helper/V3Proxy.sol";
import "../interfaces/IUniswapV3Pool.sol";


contract V3ProxyTest is Test {

    function testB() public {
        //You can use WBTC/ETH https://info.uniswap.org/#/pools/0x4585fe77225b41b697c938b018e2ac67ac5a20c0
        //OR WBTC/DAI https://info.uniswap.org/#/pools/0x391e8501b626c623d39474afca6f9e46c2686649
        IUniswapV3Pool pool = IUniswapV3Pool(0x391E8501b626C623d39474AfcA6f9e46c2686649);

        (uint160 sqrtPriceX96,,,,,,) = pool.slot0();
    
        uint decimals0 = ERC20(pool.token0()).decimals();
        uint decimals1 = ERC20(pool.token1()).decimals();
        uint priceX8 = 10**decimals0;
        // Overflow if dont scale down the sqrtPrice before div 2*192
        priceX8 = priceX8 * uint(sqrtPriceX96 / 2 ** 12) ** 2 * 1e8 / 2**168;
        priceX8 = priceX8 / 10**decimals1;
    }
}

Tools Used

Manual Review

Now you perform calculations with price X84, consider moving to price X64 for example or less

-   priceX8 = priceX8 * uint(sqrtPriceX96 / 2 ** 12) ** 2 * 1e8 / 2**168;
-   priceX8 = priceX8 * uint(sqrtPriceX96 / 2 ** 32) ** 2 * 1e8 / 2**128;

Assessed type

Uniswap

#0 - c4-pre-sort

2023-08-09T10:35:48Z

141345 marked the issue as duplicate of #140

#1 - c4-judge

2023-08-20T17:32:23Z

gzeon-c4 changed the severity to 3 (High Risk)

#2 - c4-judge

2023-08-20T17:32:29Z

gzeon-c4 marked the issue as satisfactory

Findings Information

🌟 Selected for report: 3docSec

Also found by: DanielArmstrong, Fulum, Krace, Limbooo, T1MOH

Labels

bug
3 (High Risk)
satisfactory
upgraded by judge
duplicate-64

Awards

625.436 USDC - $625.44

External Links

Lines of code

https://github.com/code-423n4/2023-08-goodentry/blob/71c0c0eca8af957202ccdbf5ce2f2a514ffe2e24/contracts/helper/V3Proxy.sol#L160-L176

Vulnerability details

Impact

In function swapTokensForExactETH() there is parameter amountInMax that specifies slippage. It is maximum that caller supposes to spend to receive exact amount of ETH. However if actual amountIn is less, ProxyV3.sol won't transfer back excess. As a result all swaps via this function are executed with maximum allowed slippage

Proof of Concept

Here you can see that whole amount amountInMax is taken from user, but excess is never returned

    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) payable external returns (uint[] memory amounts) {
        require(path.length == 2, "Direct swap only");
        require(path[1] == ROUTER.WETH9(), "Invalid path");
        ERC20 ogInAsset = ERC20(path[0]);
        //@audit TAKES WHOLE `amountInMax` amount
@>      ogInAsset.safeTransferFrom(msg.sender, address(this), amountInMax);
        ogInAsset.safeApprove(address(ROUTER), amountInMax);
        amounts = new uint[](2);
        amounts[0] = ROUTER.exactOutputSingle(ISwapRouter.ExactOutputSingleParams(path[0], path[1], feeTier, address(this), deadline, amountOut, amountInMax, 0));
      
        // RETURN SWAPPED ETH
        amounts[1] = amountOut; 
        ogInAsset.safeApprove(address(ROUTER), 0);
        IWETH9 weth = IWETH9(ROUTER.WETH9());
        acceptPayable = true;
        weth.withdraw(amountOut);
        acceptPayable = false;
        payable(msg.sender).call{value: amountOut}("");

@>      // DOESN'T RETURN `amountInMax - amounts[0]` AMOUNT OF ogInAsset token

        emit Swap(msg.sender, path[0], path[1], amounts[0], amounts[1]); 
    }

Tools Used

Manual Review

Return back excess

    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) payable external returns (uint[] memory amounts) {
        ...

+       ogInAsset.safeTransfer(msg.sender, amountInMax - amounts[0]);

        emit Swap(msg.sender, path[0], path[1], amounts[0], amounts[1]); 
    }

Assessed type

Uniswap

#0 - c4-pre-sort

2023-08-09T08:23:27Z

141345 marked the issue as duplicate of #64

#1 - c4-judge

2023-08-20T17:31:50Z

gzeon-c4 changed the severity to 3 (High Risk)

#2 - c4-judge

2023-08-20T17:31:54Z

gzeon-c4 marked the issue as satisfactory

Awards

12.8772 USDC - $12.88

Labels

bug
2 (Med Risk)
satisfactory
duplicate-83

External Links

Lines of code

https://github.com/code-423n4/2023-08-goodentry/blob/71c0c0eca8af957202ccdbf5ce2f2a514ffe2e24/contracts/helper/V3Proxy.sol#L174 https://github.com/code-423n4/2023-08-goodentry/blob/71c0c0eca8af957202ccdbf5ce2f2a514ffe2e24/contracts/helper/V3Proxy.sol#L156 https://github.com/code-423n4/2023-08-goodentry/blob/71c0c0eca8af957202ccdbf5ce2f2a514ffe2e24/contracts/helper/V3Proxy.sol#L192

Vulnerability details

Impact

Low level call is used when ProxyV3.sol transfers ETH to msg.sender. However ProxyV3.sol doesn't check whether this call was successful or not. And if result is false, ETH will be stuck in ProxyV3.sol and user loses funds.

Proof of Concept

ProxyV3.sol transfers ETH to msg.sender when performs swaps with ETH:

    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) payable external returns (uint[] memory amounts) {
        ...

        payable(msg.sender).call{value: amounts[1]}("");
        emit Swap(msg.sender, path[0], path[1], amounts[0], amounts[1]);                 
    }

    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) payable external returns (uint[] memory amounts) {
        ...

        payable(msg.sender).call{value: amountOut}("");
        emit Swap(msg.sender, path[0], path[1], amounts[0], amounts[1]); 
    }

    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) payable external returns (uint[] memory amounts) {
        ...

        msg.sender.call{value: msg.value - amounts[0]}("");
        emit Swap(msg.sender, path[0], path[1], amounts[0], amounts[1]); 
    }

Tools Used

Manual Review

Revert when result is false

(bool success,) = payable(msg.sender).call{value: amountOut}("");
require(success, "Transfer failed");

Assessed type

call/delegatecall

#0 - c4-pre-sort

2023-08-09T02:07:46Z

141345 marked the issue as duplicate of #481

#1 - c4-pre-sort

2023-08-09T09:25:56Z

141345 marked the issue as duplicate of #83

#2 - c4-judge

2023-08-20T17:11:17Z

gzeon-c4 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