Company Simulator

First Flight #51
Beginner FriendlyDeFi
100 EXP
View results
Submission Details
Impact: low
Likelihood: low
Invalid

L04. Fake revenue accounting due to unvalidated msg.value

Root + Impact

Description

  • Normal behavior: When the CustomerEngine initiates a sale, the CompanyGame contract should receive ETH equal to the sale price and record that actual ETH as revenue so company_balance always reflects real on-chain funds.

  • Problem: The CompanyGame sell_to_customer function computes revenue from the requested item count and adds that computed value to self.company_balance without validating or using msg.value. This allows company_balance to be increased on-chain accounting without any ETH being forwarded, producing fake revenue.

if self.inventory >= requested:
self.inventory -= requested
@> revenue: uint256 = requested * SALE_PRICE
@> self.company_balance += revenue # company_balance increased from computed value, not msg.value
if self.reputation < 100:
self.reputation = min(self.reputation + REPUTATION_REWARD, 100)
else:
self.reputation = 100
log Sold(amount=requested, revenue=revenue)

Risk

Likelihood:

  • A deployment where ITEM_PRICE in CustomerEngine and SALE_PRICE in CompanyGame differ because of configuration or version mismatch.

  • The owner sets CUSTOMER_ENGINE to an address that does not forward ETH correctly (an EOA, a misconfigured contract, or a contract with a bug in forwarding/refund logic).

Impact:

  • Company accounting (company_balance) can be inflated without corresponding ETH on-chain, enabling payouts and share-price calculations to be based on fictitious funds.

  • Withdrawals and investor redemptions may later attempt to transfer ETH the contract does not hold, causing reverts, insolvency or real fund loss if cover-up injections are made opportunistically.

Proof of Concept

# Steps to reproduce (example sequence performed by owner or test script)
# 1) Deploy CompanyGame (owner deploys)
company = deploy CompanyGame()
# 2) Owner sets CUSTOMER_ENGINE to an address they control (contract or EOA)
# This example assumes owner mistakenly sets engine to an EOA or a contract that does not forward ETH.
company.set_customer_engine(engine_address)
# 3) From the configured engine_address, call sell_to_customer without sending ETH:
# Because CompanyGame only checks msg.sender == CUSTOMER_ENGINE, the call succeeds.
# On-chain effect: company_balance increases by requested * SALE_PRICE, but contract balance (address(this).balance) remains unchanged.
# Pseudo-call performed by engine_address:
CompanyGame(company_address).sell_to_customer( requested=5 ) # msg.value == 0
# Observed state:
# - company.company_balance increased by 5 * SALE_PRICE
# - address(company).balance == 0 (no ETH received)
# - Logged Sold event shows revenue == computed revenue (but not actual funds)
# Written explanation:
# The call above demonstrates that CompanyGame credits its internal accounting using the expression
# `requested * SALE_PRICE` instead of the actual amount of ETH received (msg.value). Because the caller is
# the configured CUSTOMER_ENGINE, the authorization check passes and the function proceeds. No check of msg.value
# is present, so company_balance diverges from the real contract balance. Subsequent operations that rely on
# company_balance (share price, payouts) will use inflated values.

Recommended Mitigation

Credit CompanyGame only with actual ETH received and validate the forwarded payment matches the expected sale amount.

- revenue: uint256 = requested * SALE_PRICE
- self.company_balance += revenue
+ # require the caller to forward the expected ETH and credit only the real amount received
+ assert msg.value == requested * SALE_PRICE, "Incorrect payment forwarded"
+ self.company_balance += msg.value
Updates

Lead Judging Commences

0xshaedyw Lead Judge
5 days ago
0xshaedyw Lead Judge 3 days ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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