Solana Vulnerability Explained

Revival attacks on Solana programs

As the blockchain world expands, Solana has stepped into the spotlight as a compelling alternative to Ethereum. But behind its lightning-fast transactions and low fees lies a hidden world of unique security challenges.

One such vulnerability that we frequently encounter during our security audits on the Solana blockchain is known as the “Revival Attack“. This post will dive deep into how these attacks work, their impact, and how to prevent them.

What is a Revival Attack?

A Revival Attack occurs when a malicious actor prevents an account from being properly closed on the Solana blockchain, allowing them to reuse the account in ways the program didn’t intend. This vulnerability stems from the way Solana handles account closure and garbage collection.

On Solana, accounts need to maintain a minimum balance of lamports (Solana’s native token) to remain “rent-exempt” and avoid being garbage collected (i.e. deleted) by the runtime. When a program wants to close an account, it typically:

1. Transfers out all lamports from the account
2. Expects the Solana runtime to garbage collect the account

However, there’s a critical detail: garbage collection only happens after a transaction completes. This creates a window of opportunity for attackers.

revival attacks solana programs vulnerability smart contarct

The Vulnerability

Let’s look at a vulnerable implementation of an account closure:

revival attack

This code appears to close the account by removing its lamports, but it has a critical flaw: the account can be “revived” within the same transaction. An attacker could send a transaction with these 2 instructions:

  • Call to close_account
  • Transfer lamports to account_to_close

At the end of the transaction account_to_close will still be “rent-exempt” so it will not be deleted by the garbage collector.

Example: Staking Vault Exploitation

To illustrate how a Revival Attack might work in a Solana protocol, let’s consider a staking vault program. This program allows users to deposit their tokens into a vault, which would then be staked on their behalf.

Users could then redeem their staked tokens, along with any rewards earned, by calling a redeemStakedTokens instruction.

Here’s a simplified version of the vulnerable redeemStakedTokens instruction:

revival attack solana programs

By transferring out lamports from the user_stake PDA, the protocol expects it to be deleted at the end of the end of the transaction. It can actually be revived and reused!

The Exploit

An attacker could exploit this vulnerability using the following transaction structure:

revival attack solana programs

By bundling these instructions in a single transaction, the attacker can:

  1. Redeem their staked tokens and rewards
  2. Prevent the stake account from being garbage collected by refunding it
  3. Repeat the process multiple times, effectively draining the staking pool

The Impacts

The impacts of Revival Attacks can be severe:

  • Multiple redemption of one-time rewards
  • Bypass of critical business logic
  • DOS of the protocol by putting an account in an unusable state

Prevention

To properly close accounts and prevent Revival Attacks, implement all three of these measures:

  1. Zero out the account data:

revival attack solana programs

2.   Set a closed account discriminator:

revival attack solana programs

All functions that should not support an account being revived should then check that imputed accounts discriminators.

3.   Transfer out all lamports:

revival attack solana programs

The Anchor Solution

If you’re using the Anchor framework, you can use the close constraint, which handles all these steps automatically:

Note that since Anchor 0.30.0 the close constraint no longer adds a CLOSED_ACCOUNT_DISCRIMINATOR. It only:

  • Sends the lamports to the specified account
  • Assigns the owner to the System Program
  • Resets the data of the account

Thus, the account could be reinitialized later or revived in the same transaction but with zeroed out data.

Additional Considerations

While you can’t prevent an attacker from refunding an account with lamports, proper implementation of all three closure steps ensures that:

1. The account data is wiped, preventing reuse of old state
2. The closed discriminator prevents the program from accepting the account in future transactions
3. You program should be exploit-proof at this stage but users could still put some accounts in a state that is not usable but not garbage collected at the same time. If that’s something you do not want in your program, Solana course book recommends to implement a Manual Force Defund function.

Conclusion

Revival Attacks represent a subtle but significant vulnerability in Solana programs. As security researchers at FuzzingLabs, we’ve seen this pattern repeatedly in bug bounty programs. Always implement proper account closure mechanisms, preferably using Anchor’s close constraint.

Remember: in blockchain security, the smallest oversight in account management can lead to significant exploits. Always thorough test your account closure logic, and consider how your program handles closed accounts that maintain their rent-exempt status.

Additional ressources

https://solana.com/developers/courses/program-security/closing-accounts

Mathieu Troullier / Patrick Ventuzelo

About Us

Founded in 2021 and headquartered in Paris, FuzzingLabs is a cybersecurity startup specializing in vulnerability research, fuzzing, and blockchain security. We combine cutting-edge research with hands-on expertise to secure some of the most critical components in the blockchain ecosystem.

Contact us for an audit or long term partnership!

Get Your Free Security Quote!

Let’s work together to ensure your peace of mind.

Keep in touch with us !

email

contact@fuzzinglabs.com

X (Twitter)

@FuzzingLabs

Github

FuzzingLabs

LinkedIn

FuzzingLabs

email

contact@fuzzinglabs.com

X (Twitter)

@FuzzingLabs

Github

FuzzingLabs

LinkedIn

FuzzingLabs