File size: 3,992 Bytes
5710d63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
  "title": "Unrestricted Reward String in burn() Enables Log Poisoning and Off-Chain Manipulation",
  "description": "The burn() function accepts an arbitrary user-supplied string as the 'recompensa' parameter and emits it directly in the RewardRedeemed event without any validation, allowlist check, or length restriction. Any caller can pass malicious, misleading, or excessively long strings into the on-chain event log. Off-chain systems (loyalty backends, indexers, dashboards) that consume this event and trust the 'recompensa' field are vulnerable to: (1) log poisoning / event spoofing β€” a user can emit 'recompensa' values like 'Admin Grant: 1000 free coffees' that were never authorized; (2) denial-of-service on indexers via extremely large strings; (3) injection attacks if the string is rendered in a web UI without sanitization.",
  "recommendation": "Replace the free-form string parameter with an enumerated reward identifier (e.g. uint8 rewardId) and maintain an owner-controlled mapping of valid reward IDs to descriptions. This constrains what can appear in event logs to only administrator-approved values. Example refactor:\n\nsolidity\n// Owner-managed reward catalogue\nmapping(uint8 => string) public rewardCatalogue;\n\nfunction setReward(uint8 id, string calldata description) external onlyOwner {\n    rewardCatalogue[id] = description;\n}\n\nfunction burn(uint256 amount, uint8 rewardId) public {\n    require(amount > 0, \"CafeToken: quantidade invalida\");\n    require(bytes(rewardCatalogue[rewardId]).length > 0, \"CafeToken: recompensa invalida\");\n    require(balanceOf(msg.sender) >= amount, \"CafeToken: saldo insuficiente\");\n    _burn(msg.sender, amount);\n    emit RewardRedeemed(msg.sender, amount, rewardId);\n}\n\nThis ensures only legitimate, pre-approved rewards are ever recorded on-chain.",
  "severity": "medium",
  "codeSnippet": "function burn(uint256 amount, string memory recompensa) public {\n    require(amount > 0, \"CafeToken: a quantidade a queimar deve ser maior que zero\");\n    require(balanceOf(msg.sender) >= amount, \"CafeToken: saldo insuficiente para queimar tokens\");\n    _burn(msg.sender, amount);\n    emit RewardRedeemed(msg.sender, amount, recompensa); // <-- arbitrary user input emitted as event data\n}",
  "location": "L71-L81",
  "path": "contracts/CafeToken.sol",
  "judgeReview": {
    "review": "Confirmed valid finding. The vulnerability is real and the attack surface is well-defined. The burn() function places zero constraints on the 'recompensa' string before broadcasting it as an authoritative event. The severity is appropriately rated medium rather than high because: (a) no funds are directly at risk from the contract itself β€” a caller can only burn their own tokens; (b) the primary damage surface is off-chain systems and UX layers that consume events, not the on-chain state. However, in a loyalty program context where the event log IS the business record, the ability for any token holder to forge arbitrary reward redemption records is a meaningful integrity risk. The exploit is trivially reproducible with zero prerequisites beyond holding at least 1 CAFE token. The recommendation to use an enumerated reward catalogue with owner-gated registration is sound and idiomatic for this pattern.",
    "confidence": 0.91,
    "exploitablePaths": [
      "Attacker holds β‰₯1 CAFE token β†’ calls burn(1, 'Gold Member Upgrade: 500 free coffees') β†’ forged RewardRedeemed event is emitted and indexed by the loyalty backend as a legitimate redemption record",
      "Attacker calls burn(1, <64KB string>) repeatedly β†’ bloats event logs and causes out-of-memory or timeout failures in off-chain indexers processing the RewardRedeemed event stream",
      "Web dashboard renders recompensa field as raw HTML β†’ attacker passes '<script>...</script>' as recompensa β†’ stored XSS executes in the admin panel of any operator that displays redemption history without sanitization"
    ]
  }
}