Full Report
Story Protocol received two denial-of-service reports that would take down the chain via panics. Both of these slipped through the cracks of audit competitions. The first vulnerability was caused by a faulty patch of a previously known issue. During the Cantina competition, a vulnerability was reported in the upstream fork codebase of Omni Network. Execution payloads being given to the execution client would be processed by Story but not by GETH due to some weird unmarshalling issues. For instance, adding the same field multiple times in JSON could bloat the payload and be considered valid. The goal was to refactor the JSON into a stricter format like protobuf but it was too late to make such a big change before launch. To fix this vulnerability, Story decided to put a hard limit on block size at 4MB. If the block size was bigger than this, the code would panic. By sending 128KB per message and sending this over and over again, the block could be valid at 4MB and lead to a node crash. For the patch, there's a quick patch and a long term patch. For the quick patch, they edited CometBFT to limit the block size to 20MB and edited the prepare proposal code not to propose blocks larger than a threshold. From rigorous dynamic testing, they determined that block sizes larger than 20MB could not be created. In the long term, they're moving to using protobuf and restricting extra fields. The second vulnerability was a logic bug in handling multiple delegation withdrawals that probably requires more digging into the codebase to fully understand. When unstaking from a validator or rewarding a delegator, the tokens are burnt from the consensus layer's balances. This is to prevent double accounting. If there are unclaimed rewards, they are automatically sent to the delegator. The function ProcessUnstakeWithdrawals iterates over a list of unbonded entries. This loop fails to deal with the situation of multiple withdrawal requests coming from the same delegator. Via some funky state handling, this led to a panic from too many coins attempting to be burned. They decided that the loop was too complicated, since it was handling too many cases at once to be cheaper computationally. They changed the code to have two loops to simplify the code. The takeaways are interesting to me from the development team: Have more time between audits and launches. This is pretty obvious but hard to do in practice. Increase test coverage. Another classic thing. Try to handle more panics within the codebase to not fail. Sometimes, you want stuff to fail but not all the time. Reduce code complexity and maximize readability. A little bit of performance gain is probably not worth a big hack. My personal takeaway as a bug bounty hunter is that DoS bugs are way easier to find as most things. If these are paid out as criticals, then it seems like it's the best bang for your buck.
Analysis Summary
# Incident Report: Story Protocol Network Postmortem
## Executive Summary
Following its mainnet launch, Story Protocol identified and patched two critical Denial-of-Service (DoS) vulnerabilities reported via its bug bounty program. These vulnerabilities, which bypassed initial audit competitions, could have allowed an attacker to force network-wide node panics, effectively halting the blockchain. Both issues were rooted in complex state handling and inherited architectural weaknesses; both have since been remediated through software updates.
## Incident Details
- **Discovery Date:** July 2025 (Post-Mainnet Launch)
- **Incident Date:** Vulnerabilities present at launch; reported shortly thereafter.
- **Affected Organization:** Story Protocol (Story Foundation)
- **Sector:** Blockchain / Layer 1 Protocol
- **Geography:** Global / Decentralized
## Timeline of Events
### Initial Access
- **Date/Time:** July 01, 2025 (Public disclosure date)
- **Vector:** Public Bug Bounty Program (Cantina)
- **Details:** Security researchers identified two distinct vectors for causing a consensus-level "panic" (crash) across the validator network.
### Lateral Movement
- *N/A: This was a Denial-of-Service vulnerability, not an unauthorized access or data exfiltration event.*
### Data Exfiltration/Impact
- **Operational Impact:** Both vulnerabilities resulted in a network halt.
- **Vulnerability 1:** Allowed the creation of blocks exceeding a hard-coded 4MB safety limit, triggering a node panic.
- **Vulnerability 2:** Involved a logic flaw in unstaking/reward withdrawals where multiple requests from a single delegator caused incorrect coin burning calculations, leading to a state panic.
### Detection & Response
- **How it was discovered:** Reported by external security researchers (Jiri123 and Trust) via the Cantina bug bounty.
- **Response actions taken:**
- **Issue 1:** Implementation of a 20MB CometBFT hard limit and restricted block proposal thresholds.
- **Issue 2:** Refactoring of the `ProcessUnstakeWithdrawals` function to simplify logic and move from a single complex loop to two distinct loops.
## Attack Methodology
- **Initial Access:** Submission of malformed or specifically crafted transactions to public RPC endpoints.
- **Impact (Damage Methods):**
- **Payload Bloating:** Leveraging JSON unmarshalling inconsistencies (inherited from the Omni Network fork) to inject extra fields that GETH ignores but Story processes, leading to block sizes that exceed safety thresholds.
- **Logic Manipulation:** Triggering "funky state handling" by submitting multiple withdrawal requests for the same delegator in a single block to exploit internal accounting errors.
## Impact Assessment
- **Financial:** No funds were reported lost, though bug bounty payouts for "Critical" severity were issued.
- **Data Breach:** None.
- **Operational:** High potential for total business disruption (chain halt).
- **Reputational:** Minimal, due to proactive patching and transparent postmortem communication.
## Indicators of Compromise
- **Behavioral indicators:**
- Validators crashing with "panic" errors related to block size limits (>4MB).
- Consensus failures during `ProcessUnstakeWithdrawals` execution.
- Repeated transactions containing ~128KB of redundant calldata.
## Response Actions
- **Containment:** Quick patches were applied to the Consensus Client to prevent the proposal of over-limit blocks.
- **Eradication:** Versions 1.2.1 and subsequent patches were released to the validator set.
- **Recovery:** Full network stability restored following the adoption of patched releases by the majority of validators.
## Lessons Learned
- **Audit Gaps:** Audit competitions may miss critical DoS bugs if the timeframe between the audit and launch is too short to implement structural changes (e.g., Protobuf migration).
- **Code Inheritance:** Forking upstream code (Omni/Octane) carries the risk of inheriting latent vulnerabilities.
- **Complexity vs. Security:** Overly optimized, complex loops for state transitions (like unstaking) increase the risk of edge-case failures.
## Recommendations
- **Architecture:** Complete the migration from JSON to Protobuf for execution payloads to enforce strict schema validation.
- **Testing:** Increase test coverage specifically for "Panic" handling to ensure the chain fails gracefully rather than halting.
- **Process:** Allow for a longer "soak time" between final audits and mainnet deployment to implement high-effort architectural fixes.
- **Fuzzing:** Implement continuous differential and fuzzy testing on core consensus logic.