Full Report
Preventing the replay of previous transactions is important for the security of Solana and most blockchain systems. The obvious way would be to check if a signature had already been seen. However, this runs into scaling issues with over 150B transactions and signature malleability issues. So, something else needs to be done. The initial solution was to just not allow transactions that are too old. In particular, if the signature contains an out of date blockhash, then it can be safely ignored. This strategy doesn't work with offline signing though. If the transaction is signed offline then the blockhash may have expired. Since some users want to do things offline with their key, there needs to be another way. Durable Transaction Nonces are a number used once (nonce) stored on chain ahead of time. Instead of putting the blockhash, the nonce is used. After the nonce is used, a new value is generated and stored on chain for the account. Of course, this must be done in both failed and successful calls in order to prevent unintentional execution of the transaction later. This functionality is complicated and very nuanced. Most of the time, the Solana core expects all state to rollback for failure. For instance, writing to an account that you don't own will result in failure. The author points out that "Special cases lead to complexity, and complexity leads to bugs." which I couldn't agree with more! This is a little thing that if not done correctly could cause major havoc. There is a match expression written in Rust that checks three cases - tx succeeded with nonce, tx succeeded with hash and whether an error occurred. The success case for the nonce case actually takes in both succeeded and failed transactions with state writes! What does this mean? Even illegal state writes, such as cross account, can persist. It seems like illegal is different than a regular failure in this context - so, errors get funky leading to the bug. This completely breaks the entire security model of the system. One account can write to another account with arbitrary value. This is an absolute 100% game over, as far as Solana bugs go. At the point this bug was found, $10B was on Solana. I hope they got a huge bug bounty for this find! This is a crazy bug that destroys the entire run time. I think the authors make a really good point that sticks with me - "As a rule of thumb, we recommend that you double-check special cases and complex code". If there is interwoven logic with weird case statements, it's a great place to look for bugs. Subtle calling patterns and unexpected errors can break this code very quickly.
Analysis Summary
# Vulnerability: Total Loss of Funds via Durable Nonce State Commit Flaw
## CVE Details
- **CVE ID**: N/A (Discovered and patched via Bug Bounty program; no formal CVE assigned for this specific Solana Core vulnerability).
- **CVSS Score**: 10.0 (Critical - estimated based on impact)
- **CWE**: CWE-696: Incorrect Behavior Order / CWE-754: Improper Check for Unusual or Exceptional Conditions.
## Affected Systems
- **Products**: Solana Core (Validator Codebase)
- **Versions**: Versions prior to late November 2020.
- **Configurations**: Systems implementing Durable Transaction Nonces (DTNs) for offline signing or long-lived transactions.
## Vulnerability Description
The flaw resides in the handling of **Durable Transaction Nonces (DTNs)**, a mechanism designed to prevent replay attacks for offline-signed transactions. Typically, Solana rolls back state changes if a transaction fails. However, for a transaction using a nonce to be "consumed" (to prevent replaying the same nonce), the on-chain nonce value must be advanced regardless of whether the transaction logic succeeded or failed.
The bug was found in a Rust **match expression** within the runtime that handled three cases: successful transactions with nonces, successful transactions with hashes, and error cases. Due to complex interwoven logic, the code mistakenly allowed the execution of "illegal" state writes (such as writing to accounts not owned by the caller) to persist in the global state if they were wrapped in a specific failure context associated with the nonce advancement logic. Essentially, the "special case" for nonce advancement overrode the standard security runtime constraints that prevent unauthorized account modifications.
## Exploitation
- **Status**: PoC available (Authored for ALLES! CTF 2021). Not exploited in the wild prior to the patch.
- **Complexity**: Low (once the logic flaw is understood).
- **Attack Vector**: Network (unprivileged transaction submission).
## Impact
- **Confidentiality**: None
- **Integrity**: Critical (Complete loss of system integrity; ability to modify any account's data).
- **Availability**: Critical (Ability to delete account data or liabilities).
- **Summary**: This was a "Game Over" bug. An attacker could:
- Mint any amount of any token.
- Steal SOL or SPL tokens from any account.
- Change ownership of any NFT.
- Alter or delete financial records in lending protocols.
## Remediation
### Patches
- The vulnerability was fixed in Solana Core in late November 2020.
- **Commit**: hxxps://github[.]com/solana-labs/solana/commit/b70abdc6453bb306dc2bc164369b286553bf1902
- **Pull Request**: hxxps://github[.]com/solana-labs/solana/pull/13868
### Workarounds
- No known workarounds for older versions; immediate update to a patched validator version is mandatory.
## Detection
- **Indicators of Compromise**: Unexpected state changes in accounts (e.g., balance shifts or data modifications) occurring within a transaction that failed the standard Ownership or Validation checks.
- **Detection methods**: Audit of on-chain transactions specifically utilizing the System Program's "AdvanceNonceAccount" instructions in conjunction with illegal cross-program invocations or state writes.
## References
- **Neodyme Blog**: hxxps://neodyme[.]io/en/blog/nonce-upon-a-time/
- **Solana Security**: hxxps://github[.]com/solana-labs/solana/blob/master/SECURITY[.]md
- **Documentation**: hxxps://docs[.]solana[.]com/offline-signing/durable-nonce