Company Simulator

First Flight #51
Beginner FriendlyDeFi
100 EXP
View results
Submission Details
Severity: medium
Valid

M01. Predictable Pseudo-Randomness in Customer Demand

Root + Impact

Description

  • The trigger_demand function normally generates a pseudo-random number to determine the quantity of items a customer requests. This randomness is intended to simulate variable customer demand based on the company's reputation.

  • The specific issue is that the pseudo-randomness relies on block.timestamp and msg.sender, which can be predicted or manipulated by miners or front-running users. This allows attackers to forecast the demand and exploit the system.

seed: uint256 = convert(
keccak256(
concat(
convert(@>block.timestamp@>, bytes32), convert(@>msg.sender@>, bytes32)
)
),
uint256,
)

Risk

Likelihood:

  • Users observing the blockchain can calculate the seed for their own addresses at the current timestamp.

  • Automated scripts can front-run transactions to influence which pseudo-random number is generated.

Impact:

  • Attackers can consistently request the maximum allowed items, gaining more benefits than intended.

  • Reputation-based reward mechanisms can be unfairly exploited, breaking the intended simulation dynamics.

Proof of Concept

Explanation: The attacker computes the exact requested items for their transaction before sending it, ensuring maximum items requested and exploiting the game's pseudo-random logic.

import boa
from eth_utils import to_wei
# Set up environment
OWNER = '0xowner...' # replace with actual owner
PATRICK = '0xpatrick...' # attacker
FUND_VALUE = to_wei(20, 'ether')
# Fund and produce items
boa.env.set_balance(OWNER, to_wei(10, 'ether'))
with boa.env.prank(OWNER):
industry_contract.fund_cyfrin(0, value=to_wei(10, 'ether'))
industry_contract.produce(10)
# Attacker predicts pseudo-random demand
current_timestamp = boa.env.block_timestamp
msg_sender = PATRICK
seed_input = current_timestamp.to_bytes(32, 'big') + bytes.fromhex(msg_sender[2:].zfill(64))
import hashlib
seed = int.from_bytes(hashlib.sha256(seed_input).digest(), 'big')
rep = industry_contract.reputation()
base = seed % 5
extra_item_chance = 1 if (seed % 100) < (rep - 50) else 0
requested = min(base + 1 + extra_item_chance, 5)
# Trigger demand using predicted value
with boa.env.prank(PATRICK):
customer_engine_contract.trigger_demand(value=requested * to_wei(0.02, 'ether'))
# Assert inventory reduced accordingly
assert industry_contract.inventory() == 10 - requested, f'Inventory should reduce by {requested}'

Recommended Mitigation

Use a verifiable randomness source, such as Chainlink VRF, or a commit-reveal scheme to generate unpredictable demand.

- seed: uint256 = convert(keccak256(concat(convert(block.timestamp, bytes32), convert(msg.sender, bytes32))), uint256)
+ seed: uint256 = requestRandomnessFromVRF() # replace with VRF-generated randomness
Updates

Lead Judging Commences

0xshaedyw Lead Judge
10 days ago
0xshaedyw Lead Judge 8 days ago
Submission Judgement Published
Validated
Assigned finding tags:

Medium – Predictable Seed

Demand randomness is grindable via timestamp and sender, enabling biased outcomes and reputation manipulation.

Support

FAQs

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