Skip to content

account reload after cpi doesn't check program ownership #3830

@robre

Description

@robre

Anchor's account reload function, which is supposed to be called after CPI, just takes the accounts new data and puts them into the deserialized structure.
When doing so, it does not check if the account's program ownership is still the same as before, which can lead to (mostly theoretical) bugs.

For example:

Program A has a token account. The token account is not a PDA, but has a private key. Treasury, the auth of this token account, is a PDA of program A, which can sign for it. {TOKENPROGRAM}[auth=treasury, 10 units, USDC]

Program A does some arbitrary CPI:

  • Call Program X, sign with treasury, pass the token account
  • After CPI, reload token account, check that the spending limit wasn't overdrafted

Now if we build a program X which does the following:

  • drain the token account
  • close the token account
  • assign token account to program X, and put the following data: {programX}[auth=treasury, 10 units, USDC]

To make this work, the attacker calls the transaction and passes the token accounts original private key signature into the transaction, so that we can then reassign this account after it has been closed.

Putting everything together lets us drain the token account despite a spending limit which relies on reloading the token account after the CPI.

This bug can theoretically occur in smart wallets/multisigs/daos/flashloan programs, programs with arbitrary cpi basically. I've seen a similar bug happen once.


Suggestion: on reload, check that the accounts program ownership matches the original program ownership for that account.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions