Tigris Trade contest - imare's results

A multi-chain decentralized leveraged exchange featuring instant settlement and guaranteed price execution on 30+ pairs.

General Information

Platform: Code4rena

Start Date: 09/12/2022

Pot Size: $90,500 USDC

Total HM: 35

Participants: 84

Period: 7 days

Judge: GalloDaSballo

Total Solo HM: 12

Id: 192

League: ETH

Tigris Trade

Findings Distribution

Researcher Performance

Rank: 62/84

Findings: 2

Award: $61.52

🌟 Selected for report: 0

πŸš€ Solo Findings: 0

Findings Information

🌟 Selected for report: 0x4non

Also found by: 0xNazgul, Deivitto, __141345__, cccz, eierina, imare, kwhuo68, rvierdiiev

Labels

bug
2 (Med Risk)
satisfactory
duplicate-104

Awards

60.3691 USDC - $60.37

External Links

Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/588c84b7bb354d20cbca6034544c4faa46e6a80e/contracts/Lock.sol#L110-L120

Vulnerability details

Impact

Inside the Lock contract claimGovFees function can be DOSed on tokens like USDT, KNC because token like this needs to first be approved to zero.

In case claimGovFees reverts because a token like this. The Lock contract functionality is compromised because claimGovFees is internally the first call in all the main contract methods: release claim claimDebt lock

Notice: The problem is that a token like this cannot be whitelisted with Lock::editAsset because it comes from bondNFT state variable. And this BondNFT contract can add an unsupported token later with BondNFT::addAsset. And once is added it cannot be removed. Even setAllowedAsset will not stop the DOSing because BondNFT::getAssets() will still return the offending token.

Proof of Concept

https://github.com/code-423n4/2022-12-tigris/blob/588c84b7bb354d20cbca6034544c4faa46e6a80e/contracts/Lock.sol#L110-L120

Tools Used

Manual review

Approve ERC20 token to zero first.

    function claimGovFees() public {
        address[] memory assets = bondNFT.getAssets();

        for (uint i=0; i < assets.length; i++) {
            uint balanceBefore = IERC20(assets[i]).balanceOf(address(this));
            IGovNFT(govNFT).claim(assets[i]);
            uint balanceAfter = IERC20(assets[i]).balanceOf(address(this));
+           IERC20(assets[i]).approve(address(bondNFT), 0);           
            IERC20(assets[i]).approve(address(bondNFT), type(uint256).max);
            bondNFT.distribute(assets[i], balanceAfter - balanceBefore);
        }
    }

#0 - c4-judge

2022-12-20T15:49:37Z

GalloDaSballo marked the issue as duplicate of #104

#1 - c4-judge

2023-01-22T17:45:47Z

GalloDaSballo marked the issue as satisfactory

Awards

1.1472 USDC - $1.15

Labels

bug
2 (Med Risk)
disagree with severity
satisfactory
sponsor acknowledged
duplicate-377

External Links

Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/588c84b7bb354d20cbca6034544c4faa46e6a80e/contracts/GovNFT.sol#L64-L71 https://github.com/code-423n4/2022-12-tigris/blob/588c84b7bb354d20cbca6034544c4faa46e6a80e/contracts/GovNFTBridged.sol#L48-L55

Vulnerability details

Impact

The method _bridgeMint is used inside NFT token contracts in two cases:

  1. When new tokens are to be minted by receiving the message from layerZero. In this case _bridgeMint is called inside _nonblockingLzReceive internal function

  2. If a transaction from layerZero fails it can be replayed with the method retryMessage which again calls _nonblockingLzReceive which calls _bridgeMint

As we can see its used to create new tokens from other chains in a first attempt or a latter retry. And because of this it can be only called by the owner (for replaying failed messages) or by the bridge address (when receiving a message).

    require(msg.sender == address(this) || _msgSender() == owner(), "NotBridge");

The main problem is that it can be also called at will by the contract owner because it is a public contract method. And the only check for minting new token is that we pass a tokenId that is smaller then the maximal tokens cap.

It should never happen but by the current contract implementation it can happen. (example by a leakage of the owner private key etc.)

Proof of Concept

https://github.com/code-423n4/2022-12-tigris/blob/588c84b7bb354d20cbca6034544c4faa46e6a80e/contracts/GovNFT.sol#L64-L71

https://github.com/code-423n4/2022-12-tigris/blob/588c84b7bb354d20cbca6034544c4faa46e6a80e/contracts/GovNFTBridged.sol#L48-L55

Tools Used

Manual review

Change _bridgeMint access from public to internal. It will still work for replaying and receiving new messages. But it can not be called at will.

#0 - c4-judge

2022-12-23T17:45:25Z

GalloDaSballo marked the issue as primary issue

#1 - TriHaz

2022-12-26T11:37:21Z

We are aware of the centralization risks, owner will be a multi-sig treasury for now. I think this should be low risk.

#2 - c4-sponsor

2022-12-26T11:37:29Z

TriHaz marked the issue as sponsor acknowledged

#3 - c4-sponsor

2022-12-26T11:37:58Z

TriHaz marked the issue as disagree with severity

#4 - GalloDaSballo

2023-01-16T08:14:49Z

Making dup of #377 as it highlights a centralization risk

#5 - c4-judge

2023-01-16T08:15:23Z

GalloDaSballo marked the issue as duplicate of #377

#6 - c4-judge

2023-01-22T17:34:50Z

GalloDaSballo 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