Gro Protocol contest - gpersoon's results

The first protocol to balance your exposure, tranche risk and boost yields all at once.

General Information

Platform: Code4rena

Start Date: 01/07/2021

Pot Size: $100,000 USDC

Total HM: 10

Participants: 7

Period: 7 days

Judge: ghoulsol

Total Solo HM: 4

Id: 17

League: ETH

Gro Protocol

Findings Distribution

Researcher Performance

Rank: 3/7

Findings: 5

Award: $21,625.53

🌟 Selected for report: 10

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: gpersoon

Also found by: cmichel

Labels

bug
3 (High Risk)
sponsor confirmed

Awards

5885.9975 USDC - $5,886.00

External Links

Handle

gpersoon

Vulnerability details

Impact

There are a few underflows that are converted via a typecast afterwards to the expected value. If solidity 0.8.x would be used, then the code would revert. int256(a-b) where a and b are uint, For example if a=1 and b=2 then the intermediate result would be uint(-1) == 2256-1 int256(-x) where x is a uint. For example if x=1 then the intermediate result would be uint(-1) == 2256-1 Its better not to have underflows by using the appropriate typecasts. This is especially relevant when moving to solidity 0.8.x

Proof of Concept

// https://github.com/code-423n4/2021-06-gro/blob/main/contracts/insurance/Exposure.sol#L178 function sortVaultsByDelta(..) .. for (uint256 i = 0; i < N_COINS; i++) { // Get difference between vault current assets and vault target int256 delta = int256(unifiedAssets[i] - unifiedTotalAssets.mul(targetPercents[i]).div(PERCENTAGE_DECIMAL_FACTOR)); // underflow in intermediate result

//https://github.com/code-423n4/2021-06-gro/blob/main/contracts/pnl/PnL.sol#L112 function decreaseGTokenLastAmount(bool pwrd, uint256 dollarAmount, uint256 bonus)... .. emit LogNewGtokenChange(pwrd, int256(-dollarAmount)); // underflow in intermediate result

// https://github.com/code-423n4/2021-06-gro/blob/main/contracts/pools/oracle/Buoy3Pool.sol#L87 function safetyCheck() external view override returns (bool) { ... _ratio = abs(int256(_ratio - lastRatio[i])); // underflow in intermediate result

Tools Used

replace int256(a-b) with int256(a)-int256(b) replace int256(-x) with -int256(x)

#0 - ghoul-sol

2021-07-25T21:32:16Z

Majority of overflow listed above seems low risk with one exception of safetyCheck. Underflow is a real risk here.safetyCheck is run every time a deposit is made. Ratios can change and the change does not need to be substantial for it to overflow. For that reason it's a high risk.

Findings Information

🌟 Selected for report: 0xRajeev

Also found by: gpersoon, pauliax

Labels

bug
duplicate
3 (High Risk)
sponsor confirmed

Awards

3531.5985 USDC - $3,531.60

External Links

Handle

gpersoon

Vulnerability details

Impact

The function distributeStrategyGainLoss does the following check to allow access to the function: require(index > 0 || index <= N_COINS + 1, "!VaultAdaptor"); However the expression index > 0 || index <= N_COINS + 1 is always TRUE, because the OR (||) is used (should have been AND &&)

This means the function can be executed from any originator. uint256 index = vaultIndexes[msg.sender]; ==> index will be 0 index is smaller than N_COINS + 1, so the require will not stop access. Also index = index - 1; will not pose an problems, because Safemath isn't used and the solidity version is lower than 8. index will be a very 2**256-1; so the rest of function can be executed without problem, disturbing the profit & loss calculation and thus disturbing the values of the tokens.

Proof of Concept

// https://github.com/code-423n4/2021-06-gro/blob/main/contracts/Controller.sol#L355 function distributeStrategyGainLoss(uint256 gain, uint256 loss) external override { uint256 index = vaultIndexes[msg.sender]; require(index > 0 || index <= N_COINS + 1, "!VaultAdaptor"); .. index = index - 1; if (index < N_COINS) { ... } else { if (gain > 0) { gainUsd = ibuoy.lpToUsd(gain); } else if (loss > 0) { lossUsd = ibuoy.lpToUsd(loss); } } ipnl.distributeStrategyGainLoss(gainUsd, lossUsd, reward); // Check if curve spot price within tollerance, if so update them if (ibuoy.updateRatios()) { // If the curve ratios were successfully updated, realize system price changes ipnl.distributePriceChange(_totalAssets()); } }

Tools Used

Change || to && Use safemath for index = index - 1;

#0 - flabble-gro

2021-07-14T20:19:05Z

Duplicate of issue #69

#1 - ghoul-sol

2021-07-26T03:47:24Z

Duplicate of #69

Findings Information

🌟 Selected for report: gpersoon

Also found by: shw

Labels

bug
3 (High Risk)
sponsor confirmed
disagree with severity

Awards

5885.9975 USDC - $5,886.00

External Links

Handle

gpersoon

Vulnerability details

Impact

The function sortVaultsByDelta doesn't always work as expected. Suppose all the delta's are positive, and delta1 >= delta2 >= delta3 > 0 Then maxIndex = 0 And (delta < minDelta (==0) ) is never true, so minIndex = 0

Then (assuming bigFirst==true): vaultIndexes[0] = maxIndex = 0 vaultIndexes[2] = minIndex = 0 vaultIndexes[1] = N_COINS - maxIndex - minIndex = 3-0-0 = 3

This is clearly not what is wanted, all vaultIndexes should be different and should be in the range [0..2] This is due to the fact that maxDelta and minDelta are initialized with the value 0. This all could results in withdrawing from the wrong vaults and reverts (because vaultIndexes[1] is out of range).

Proof of Concept

// https://github.com/code-423n4/2021-06-gro/blob/main/contracts/insurance/Exposure.sol#L178 function sortVaultsByDelta(bool bigFirst,uint256 unifiedTotalAssets,uint256[N_COINS] calldata unifiedAssets,uint256[N_COINS] calldata targetPercents) external pure override returns (uint256[N_COINS] memory vaultIndexes) { uint256 maxIndex; uint256 minIndex; int256 maxDelta; int256 minDelta; for (uint256 i = 0; i < N_COINS; i++) { // Get difference between vault current assets and vault target int256 delta = int256( unifiedAssets[i] - unifiedTotalAssets.mul(targetPercents[i]).div(PERCENTAGE_DECIMAL_FACTOR) ); // Establish order if (delta > maxDelta) { maxDelta = delta; maxIndex = i; } else if (delta < minDelta) { minDelta = delta; minIndex = i; } } if (bigFirst) { vaultIndexes[0] = maxIndex; vaultIndexes[2] = minIndex; } else { vaultIndexes[0] = minIndex; vaultIndexes[2] = maxIndex; } vaultIndexes[1] = N_COINS - maxIndex - minIndex; }

Tools Used

Initialize maxDelta and minDelta: int256 maxDelta = -2255; // or type(int256).min when using a newer solidity version int256 minDelta = 2255; // or type(int256).max when using a newer solidity version Check maxIndex and minIndex are not the same require (maxIndex != minIndex);

#0 - kitty-the-kat

2021-07-14T14:42:54Z

disagree with severity - (Low Risk) Only a problem when stable coin percent sum is less than 100%, which is set by gov.

#1 - ghoul-sol

2021-07-26T14:37:19Z

I'm not sure how sum of stable coin percentage being set to 100% avoids this issue. Scenario above looks valid to me.

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