Canto contest - 0x1f8b's results

Execution layer for original work.

General Information

Platform: Code4rena

Start Date: 14/06/2022

Pot Size: $100,000 USDC

Total HM: 26

Participants: 59

Period: 7 days

Judge: GalloDaSballo

Total Solo HM: 9

Id: 133

League: ETH

Canto

Findings Distribution

Researcher Performance

Rank: 17/59

Findings: 6

Award: $1,789.87

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: WatchPug

Also found by: 0x1f8b, Chom, gzeon

Labels

bug
duplicate
3 (High Risk)

Awards

594.3197 USDC - $594.32

3679.998 CANTO - $594.32

External Links

Lines of code

https://github.com/Plex-Engineer/lending-market/blob/755424c1f9ab3f9f0408443e6606f94e4f08a990/contracts/NoteInterest.sol#L92

Vulnerability details

Impact

The rand seed used for borrow rate is controlled by the user.

Proof of Concept

In the getBorrowRate method of the NoteInterest contract, a rand value is used for the borrow rate, but it was taken from the msg.sender address, the public address of any EOA could be brute-forced to take advantage of this randomness

    function getBorrowRate(uint cash, uint borrows, uint reserves) public view override returns (uint) {
        // Gets the Note/gUSDC TWAP in a given interval, as a mantissa (scaled by 1e18)
        // uint twapMantissa = getUnderlyingPrice(note);
        uint rand = uint(keccak256(abi.encodePacked(msg.sender))) % 100;        // <------- INSECURE
        uint ir = (100 - rand).mul(adjusterCoefficient).add(baseRatePerYear).mul(1e16);
        uint newRatePerYear = ir >= 0 ? ir : 0;
        // convert it to base rate per block
        uint newRatePerBlock = newRatePerYear.div(blocksPerYear);
        return newRatePerBlock;
    }
  • Change the rand seed origin.

#0 - nivasan1

2022-06-21T17:54:22Z

duplicate of issue #202

#1 - GalloDaSballo

2022-08-04T21:44:48Z

Dup of #166

Findings Information

🌟 Selected for report: Soosh

Also found by: 0x1f8b, Ruhum, TerrierLover, WatchPug, cccz, csanuragjain, hake, p4st13r4, zzzitron

Labels

bug
duplicate
3 (High Risk)

Awards

126.3383 USDC - $126.34

782.2807 CANTO - $126.34

External Links

Lines of code

https://github.com/Plex-Engineer/manifest/blob/cf88a30070689b71c2678642dc0fdfa1740f666c/contracts/Proposal-Store.sol#L39-L50

Vulnerability details

Impact

There are a lack of governance during proposal management.

Proof of Concept

The AddProposal method of ProposalStore does not contain any restrictions on which user can call it. So anyone can add any proposal, and what is worse, overwriting proposals, since it does not take into account that the proposal already exists.

    constructor(uint propId, string memory title, string memory desc, address[] memory targets, 
                        uint[] memory values, string[] memory signatures, bytes[] memory calldatas) {
	UniGovModAcct = msg.sender;
	Proposal memory prop = Proposal(propId, title, desc, targets, values, signatures, calldatas);
	proposals[propId] = prop;
    }
    
    function AddProposal(uint propId, string memory title, string memory desc, address[] memory targets, 
                        uint[] memory values, string[] memory signatures, bytes[] memory calldatas) public {
        Proposal memory newProp = Proposal(propId, title, desc, targets, values, signatures, calldatas);
        proposals[propId] = newProp;
    }

A malicious user can carry out a front-running attack and replace the propId that is generated during the contract deployment, with different values, so that if the owner does not realize it, a replaced proposal can be approved.

It also allows third parties to deny service, continuously replacing legitimate proposals with attacks.

  • Add OnlyUniGov modifier

#0 - tkkwon1998

2022-06-22T17:41:07Z

Duplicate of issue #26

Awards

95.0451 USDC - $95.05

687.9945 CANTO - $111.11

Labels

bug
QA (Quality Assurance)

External Links

Low

Factory can call twice initialize and change the tokens

The method initialize doesn't check that was already initialized. So if the user factory call them again, could produce lose of tokens.

Affected source code:

Lack of checks

The following methods have a lack checks if the received argument is an address, it's good practice in order to reduce human error to check that the address specified in the constructor or initialize is different than address(0).

Affected source code:

address(0):

Emit event for important state changes

It's important to emit events when the governance change important values in the contract, in order to be more open and transparent.

Affected source code:

Unsafe ERC20 calls

The following code doesn't check the result of the ERC20 calls. ERC20 standard specify that the token can return false if these calls fails, so it's mandatory to check the result of these ERC20 methods.

Reference:

NOTES: The following specifications use syntax from Solidity 0.4.17 (or above) Callers MUST handle false from returns (bool success). Callers MUST NOT assume that false is never returned!

Affected source code for transfer:

Open TODO

The code that contains "open todos" reflects that the development is not finished and that the code can change a posteriori, prior release, with or without audit.

// TODO: Don't distribute supplier COMP if the user is not in the borrower market.:

Non Critical

Lack of ACK during owner change

It's possible to lose the ownership under specific circumstances.

Because an human error it's possible to set a new invalid owner. When you want to change the owner's address it's better to propose a new owner, and then accept this ownership with the new wallet.

Affected source code:

Wrong comments

In the contract ProposalStore there are copy/pasted comment:

Affected source code:

// This is an evil token. Whenever an A -> B transfer is called, half of the amount goes to B and half to a predefined C:

reserveFactorMantissa not used

Affected source code:

#0 - GalloDaSballo

2022-07-30T02:32:39Z

Factory can call twice initialize and change the tokens

I believe the finding to be invalid because while technically you could call initialize twice, given the system we have in which only the Factory can do so, and it doesn't, then the finding cannot be enacted.

See: https://github.com/Plex-Engineer/zeroswap/blob/0fa049912bc14c27ba60efbada23fc1cc18b04e4/contracts/uniswapv2/UniswapV2Factory.sol#L40

Lack of checks

Don't agree for all (pair), but some are valid.

Valid Low

Emit event for important state changes

Valid NC

#1 - GalloDaSballo

2022-08-01T22:55:54Z

Unsafe ERC20 calls

The code provided shows note and cToken as the only tokens being transferred and not-checked

Because the implementation is known: https://github.com/Plex-Engineer/lending-market/blob/755424c1f9ab3f9f0408443e6606f94e4f08a990/contracts/ERC20.sol#L93

https://github.com/Plex-Engineer/lending-market/blob/755424c1f9ab3f9f0408443e6606f94e4f08a990/contracts/CToken.sol#L68

And they never return false

I believe Non-Critical is more appropriate

#2 - GalloDaSballo

2022-08-01T22:56:59Z

Open TODO

NC

Lack of ACK during owner change

NC

Wrong comments

NC

reserveFactorMantissa not used

NC

Pretty good report 1L 6NC

Awards

396.9199 CANTO - $64.10

78.2901 USDC - $78.29

Labels

bug
G (Gas Optimization)

External Links

Use right type

Use bool for unlocked and move this var along with blockTimestampLast to share the same storage slot:

Affected source code:

Use Custom Errors instead of Revert Strings to save Gas

Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met)

Source Custom Errors in Solidity:

Starting from Solidity v0.8.4, there is a convenient and gas-efficient way to explain to users why an operation failed through the use of custom errors. Until now, you could already use strings to give more information about failures (e.g., revert("Insufficient funds.");), but they are rather expensive, especially when it comes to deploy cost, and it is difficult to use dynamic information in them.

Custom errors are defined using the error statement, which can be used inside and outside of contracts (including interfaces and libraries).

If it's not possible to use error codes due to the pragma used, it is recommended to reduce the strings to less than 32 bytes.

All contracts in scope are affected.

Obsolete pragma

The pragma version used in uniswap contracts are:

pragma solidity =0.6.12;:

But recently solidity released a new version with important Bugfixes:

  • The first one is related to ABI-encoding nested arrays directly from calldata. You can find more information here.

  • The second bug is triggered in certain inheritance structures and can cause a memory pointer to be interpreted as a calldata pointer or vice-versa. We also have a dedicated blog post about this bug.

Apart from these, there are several minor bug fixes and improvements.

The minimum required version should be 0.8.14

++i costs less gas compared to i++ or i += 1

++i costs less gas compared to i++ or i += 1 for unsigned integer, as pre-increment is cheaper (about 5 gas per iteration). This statement is true even with the optimizer enabled.

i++ increments i and returns the initial value of i. Which means:

uint i = 1;
i++; // == 1 but i == 2

But ++i returns the actual incremented value:

uint i = 1;
++i; // == 2 and i == 2 too, so no need for a temporary variable

In the first case, the compiler has to create a temporary variable (when used) for returning 1 instead of 2 I suggest using ++i instead of i++ to increment the value of an uint variable. Same thing for --i and i--

It is also recommended to not initialize the counter variable and surround the increment with an unchecked region.

Affected source code:

Use constants

Use constant instead of storage for:

Cache keccak256 results:

Dead code

The following contracts are never used. It's an excellent idea to take them out to save gas and improve the codding style.

Affected source code:

OnlyUniGov and UniGovModAcct is not used:

Solidity 8 already does the overflow/underflow checks:

Gas saving using immutable.

It's possible to avoid storage access a save gas using immutable keyword for the following variables:

Affected source code:

name and symbol:

Unchecking arithmetics operations that can't underflow/overflow

Solidity version 0.8+ comes with implicit overflow and underflow checks on unsigned integers. When an overflow or an underflow isn't possible (as an example, when a comparison is made before the arithmetic operation), some gas can be saved by using an unchecked block: https://docs.soliditylang.org/en/v0.8.10/control-structures.html#checked-or-unchecked-arithmetic

Affected source code:

There's no need to set default values for variables.

If a variable is not set/initialized, the default value is assumed (0, false, 0x0 ... depending on the data type). You are simply wasting gas if you directly initialize it with its default value.

Affected source code:

Use require instead of assert

The assert() and require() functions are a part of the error handling aspect in Solidity. Solidity makes use of state-reverting error handling exceptions. This means all changes made to the contract on that call or any sub-calls are undone if an error is thrown. It also flags an error.

They are quite similar as both check for conditions and if they are not met, would throw an error.

The big difference between the two is that the assert() function when false, uses up all the remaining gas and reverts all the changes made.

Meanwhile, a require() function when false, also reverts back all the changes made to the contract but does refund all the remaining gas fees we offered to pay. This is the most common Solidity function used by developers for debugging and error handling.

Affected source code:

Reduce boolean comparison

It's compared a boolean value using == true or == false, instead of using the boolean value, or NOT opcode, it's cheaper to use NOT when the value it's false, or just the value without == true, when it's true, because it will use less opcode inside the VM.

Affected source code:

Senseless return

The return of the following methods is the same value as the argument received by the user, so it can be eliminated.

Affected source code:

#0 - GalloDaSballo

2022-08-04T00:06:40Z

Use bool for unlocked and move this var along with blockTimestampLast to share the same storage slot:

<img width="176" alt="Screenshot 2022-08-04 at 02 04 57" src="https://user-images.githubusercontent.com/13383782/182735525-a4ed9bd2-88c7-4239-9abb-a856357cb117.png"> Almost but it won't' pack / it will make it worse. But really close. (would have saved 15k)

10k via immutable rest is negligible

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