Neo Tokyo contest - anodaram's results

A staking contract for the crypto gaming illuminati.

General Information

Platform: Code4rena

Start Date: 08/03/2023

Pot Size: $60,500 USDC

Total HM: 2

Participants: 123

Period: 7 days

Judge: hansfriese

Id: 220

League: ETH

Neo Tokyo

Findings Distribution

Researcher Performance

Rank: 48/123

Findings: 1

Award: $154.74

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

154.74 USDC - $154.74

Labels

bug
3 (High Risk)
satisfactory
edited-by-warden
duplicate-261

External Links

Lines of code

https://github.com/code-423n4/2023-03-neotokyo/blob/dfa5887062e47e2d0c801ef33062d44c09f6f36e/contracts/staking/NeoTokyoStaker.sol#L1623-L1627

Vulnerability details

Impact

This example shows us one tricky in math floor function.

1.51 + 1.51 - 1.00 - 1.00 - 1.00 > 0 f(1.51) + f(1.51) - f(1.00) - f(1.00) - f(1.00) < 0 // f is math floor function

To make it easy to describe, let's say that timelockMultiplier is 100.

points calculation part of stake/withdraw LP are on Line 1155 and Line 1623.

uint256 points = amount * 100 / 1e18 * timelockMultiplier / _DIVISOR;

So, amount = 1.51 * 10**16 => points = 1 amount = 1.00 * 10**16 => points = 1

An example of a malicious user's activity.
StepActivitylpPosition.amountlpPosition.points
(orginal)00
Step 1Stake 1.51 * 10**16 LP token1.51 * 10**161
Step 2Stake 1.51 * 10**16 LP token3.02 * 10**162
Step 3Withdraw 1.00 * 10**16 LP token2.02 * 10**161
Step 4Withdraw 1.00 * 10**16 LP token1.02 * 10**160
Step 5Withdraw 1.00 * 10**16 LP token0.02 * 10**16max(uint256) - 1

After Step 5, the points should be -1. Since the datatype is unsinged integer, it will be INFINITY.

This can cause several critical attacks. In line 1388-1392, which is calculating user share amount in function getPoolReward

uint256 share = points * _PRECISION / pool.totalPoints * totalReward; uint256 daoShare = share * pool.daoTax / (100 * _DIVISOR); share /= _PRECISION; daoShare /= _PRECISION; return ((share - daoShare), daoShare);

(We can assume that pool.totalPoints is not negative because we have other users' sharing) In case of points = max(uint256) - 1, share can be unexpected value. The malicious user can getReward huge amount of asset.

Proof of Concept

https://github.com/code-423n4/2023-03-neotokyo/blob/dfa5887062e47e2d0c801ef33062d44c09f6f36e/contracts/staking/NeoTokyoStaker.sol#L1155 https://github.com/code-423n4/2023-03-neotokyo/blob/dfa5887062e47e2d0c801ef33062d44c09f6f36e/contracts/staking/NeoTokyoStaker.sol#L1623-L1627 https://github.com/code-423n4/2023-03-neotokyo/blob/dfa5887062e47e2d0c801ef33062d44c09f6f36e/contracts/staking/NeoTokyoStaker.sol#L1388-L1392

Tools Used

(Nothing, just looking code on VSCode)

We can recalculate lpPosition.points based on newAmount in _withdrawLP.

Original
uint256 points = amount * 100 / 1e18 * lpPosition.multiplier / _DIVISOR; // Update the caller's LP token stake. lpPosition.amount -= amount; lpPosition.points -= points; // Update the pool point weights for rewards. pool.totalPoints -= points;
Fixed
uint256 points = amount * 100 / 1e18 * lpPosition.multiplier / _DIVISOR; // Update the caller's LP token stake. lpPosition.amount -= amount; uint256 newPoints = lpPosition.amount * 100 / 1e18 * lpPosition.multiplier / _DIVISOR; pool.totalPoints -= lpPosition.points - newPoints; lpPosition.points = newPoints;

Also, need to fix in the same way in _stakeLP.

#0 - c4-judge

2023-03-16T06:38:21Z

hansfriese marked the issue as satisfactory

#1 - c4-judge

2023-03-16T06:38:33Z

hansfriese marked the issue as duplicate of #450

#2 - c4-judge

2023-03-17T01:09:04Z

hansfriese marked the issue as duplicate of #261

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