4

Trying to understand how the #[account(close = <target_account>)] constraint works and it says:

Marks the account as closed at the end of the instruction’s execution (sets its discriminator to the CLOSED_ACCOUNT_DISCRIMINATOR) and sends its lamports to the specified account. Setting the discriminator to a special variant makes account revival attacks (where a subsequent instruction adds the rent exemption lamports again) impossible. Requires mut to exist on the account.

Also looked through the examples here: https://github.com/coral-xyz/sealevel-attacks/tree/master/programs/9-closing-accounts

Could someone provide an example of how an account revival attack can be used as an exploit and explain why it is insecure to close an account without setting the discriminator to a special variant?

2 Answers 2

2

In your Solana program the #[account] attribute for a data type assigns a unique 8-byte discriminator for accounts of that type. i.e:

#[account]
pub struct MyType {}

When an account of that type is initialized, that 8-byte discriminator is assigned to it. In subsequent validators, the Account<'info, MyType> checks that the account being passed in was indeed created by your program. It does this by comparing the discriminators.

Now how does this affect closing accounts?

Well when an account is closed, setting the account's discriminator to the CLOSED_ACCOUNT_DISCRIMINATOR ensures that if/when that account is reinitialized by some other user, it doesn't pass the discrimator check and thus is not considered valid by our program(since its discriminator was reset).

Imagine a scenario where users pay to create an account that grants them access to an nft mint. Let's say users pays 5 sol to create an account that lets them mint and their accounts are closed once their mint is completed.
If an account is closed without its discriminator being reset, attackers could simply revive the account and have it legally pass the discriminator checks in your mint instruction, letting them mint without having to pay the 5 sol to initialize the account(and set the valid discriminator) in the first place.

That is an account-revival attack.

1

The idea is just that you can send solana to the closed accounts and revive them. Program accounts that have had all their lamports removed don't get their ownership transferred to the system program until the end of the block. So, if your program assumes that an account has been closed but someone has managed to reactivate it, then some spooky stuff could happen to your code and potentially lead to an exploit. Anchor has the closed account discriminator so that even if someone put solana into the account, they won't be able to load the account in a function.

2
  • Does this mean that if the discriminator is not set, an exploit is only valid before the current block has been proccessed? Sep 22 at 23:45
  • No, once the account has been revived by passing it solana, that account continues to exist. The account only get destroyed at the end of the block if it has 0 lamports. However, with the new realloc functions anchor is adding a safer close function that will reassign ownership to the system program immediately.
    – Henry E
    Sep 23 at 8:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.