Dria

Swan
NFTHardhat
21,000 USDC
View results
Submission Details
Severity: high
Invalid

Summary : The issue here is that the BuyerAgent._computePhase function uses a weak Pseudo-Random Number Generator (PRNG) to calculate the roundTime variable.

Vulnerability Details : The line roundTime = elapsedTime % cycleTime uses the modulo operator to generate a random-like value, but this is not a secure way to generate randomness. The modulo operator can produce predictable and repeating patterns, which can be exploited by an attacker.

In a blockchain context, it's essential to use a secure and unpredictable source of randomness to prevent attacks. A weak PRNG can lead to vulnerabilities such as:

  1. Predictable outcomes: An attacker can predict the outcome of the random number generation, allowing them to manipulate the system.

  2. Replay attacks: An attacker can replay a previous random number, allowing them to reuse a previous outcome.

/// @notice Function to compute the current round, phase and time until next phase w.r.t given market parameters.
/// @param params Market parameters of the Swan.
/// @param elapsedTime Time elapsed that computed in 'getRoundPhase()' according to the timestamps of each round.
/// @return round, phase, time until next phase
function _computePhase(SwanMarketParameters memory params, uint256 elapsedTime)
internal
pure
returns (uint256, Phase, uint256)
{
uint256 cycleTime = _computeCycleTime(params);
uint256 round = elapsedTime / cycleTime;
uint256 roundTime = elapsedTime % cycleTime;
// example:
// |-------------> | (roundTime)
// |--Sell--|--Buy--|-Withdraw-| (cycleTime)
if (roundTime <= params.sellInterval) {
return (round, Phase.Sell, params.sellInterval - roundTime);
} else if (roundTime <= params.sellInterval + params.buyInterval) {
return (round, Phase.Buy, params.sellInterval + params.buyInterval - roundTime);
} else {
return (round, Phase.Withdraw, cycleTime - roundTime);
}
}

Impact : Proof of Concept :

pragma solidity ^0.8.0;
contract WeakPRNG {
uint256 public cycleTime = 10;
uint256 public elapsedTime = 0;
function computeRoundTime() public returns (uint256) {
uint256 roundTime = elapsedTime % cycleTime;
return roundTime;
}
function incrementElapsedTime() public {
elapsedTime++;
}
}

**Attacker Contract: **

pragma solidity ^0.8.0;
contract Attacker {
WeakPRNG public weakPRNG;
constructor(address _weakPRNG) public {
weakPRNG = WeakPRNG(_weakPRNG);
}
function exploit() public {
// Predict the roundTime value
uint256 predictedRoundTime = weakPRNG.elapsedTime() % weakPRNG.cycleTime();
// Increment elapsedTime to repeat the roundTime value
weakPRNG.incrementElapsedTime();
// Verify that the predicted roundTime value is correct
assert(weakPRNG.computeRoundTime() == predictedRoundTime);
// Use the predicted roundTime value to manipulate the system
// ...
}
}

Exploitation Steps:

  1. Deploy the WeakPRNG contract and note its address.

  2. Deploy the Attacker contract, passing the address of the WeakPRNG contract as a constructor argument.

  3. Call the exploit() function on the Attacker contract.

  4. The Attacker contract will predict the roundTime value using the weak PRNG.

  5. The Attacker contract will increment the elapsedTime value to repeat the roundTime value.

  6. The Attacker contract will verify that the predicted roundTime value is correct.

  7. The Attacker contract can now use the predicted roundTime value to manipulate the system.

Exploitation Example:

Suppose the WeakPRNG contract is used to determine the winner of a lottery. The Attacker contract can predict the roundTime value and use it to manipulate the lottery outcome.

  1. The WeakPRNG contract generates a roundTime value of 3.

  2. The Attacker contract predicts the roundTime value and increments the elapsedTime value to repeat the value.

  3. The WeakPRNG contract generates a new roundTime value of 3.

  4. The Attacker contract uses the predicted roundTime value to manipulate the lottery outcome, ensuring that they win the lottery.

This proof of concept demonstrates how an attacker can exploit the weak PRNG in the WeakPRNG contract to predict and manipulate the roundTime value.

Using a weak PRNG can have serious consequences in a blockchain context:

  1. Predictable outcomes: An attacker can predict the outcome of the random number generation, allowing them to manipulate the system.

  2. Replay attacks: An attacker can replay a previous random number, allowing them to reuse a previous outcome.

  3. Front-running: An attacker can use their knowledge of the predictable random number to front-run transactions, allowing them to gain an unfair advantage.

Tools Used : Slither, VS code

Recommendations : To address these issues, you can use a secure PRNG that introduces randomness and entropy into the system. Here are some potential solutions:

Chainlink VRF: Use a decentralized randomness beacon like Chainlink's Verifiable Random Function (VRF) to generate secure randomness.

uint256 roundTime = ChainlinkVRF.getRandomNumber();

RANDAO: Use a RANDAO (Random Number Distribution Algorithm) to generate secure randomness.

uint256 roundTime = ChainlinkVRF.getRandomNumber();
Updates

Lead Judging Commences

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.