-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Description
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.