How to prevent the Wormhole hack (2022)

How to prevent the Wormhole hack (2022)

February 4, 2022

What are sysvar accounts?

  • used for classic operations needed within the solana runtime

What is Instructions sysvar?

  • needed if an instruction needs to reference instructions in the same transaction

How the Solana wormhole hack could have been prevented (and some hypotheticals). It’s been mentioned that anchor could have helped prevent it, but I’m not actually sure.

1. Check the id!

2. Upgrade version and method (the patch)

Methods 3 - 5 would only have worked if Instructions inherited the Sysvar trait (which it doesn’t)

3. Other native library methods

4. Anchor

Bonus (for other sysvar accounts): Use get() instead of passing in accounts

1. Check the id!

The id needed to be checked, period. It doesn’t matter where.

There’s nothing wrong with using solana-program 1.7. A check as simple as

if !check_id(instruction_sysvar_account_info.key) {
    return Err(ProgramError::UnsupportedSysvar);
}

somewhere in the call hierarchy would have sufficed.

Looking through the audius project which actually still had solana-program versions of 1.7, they added checks throughout so 1.7 wasn’t a problem.

2. Upgrade Solana-program version

The patch was obviously just to upgrade solana-program, and use a checked version of the method load_instruction_at_checked here.

Methods after this would only work is Instructions implemented Sysvar

3. Other native library methods

Using from_account_info would also work, because id gets checked!

let instruction_sysvar_info = next_account_info(account_info_iter)?;
let instructions = Instructions::from_account_info(&instruction_sysvar_info)?;

4. Anchor

As some others have noted, using Anchor would have saved the day. But how?

Casting as a Sysvar

pub struct Example<'info> {
    pub instruction_info: Sysvar<'info, Instructions>
}

This would call try_accounts which would eventually call from_account_info (as discussed above) from solana-program which does check ids!

Bonus. Use get() instead of passing in accounts!

This doesn’t work for the instruction sysvar account currently (not sure why), but for other sysvar accounts the best mitigation actually is right in the official solana docs, and in the Sysvar account comments in anchor. Don’t pass in the sysvar account, and just retrieve it directly in the instruction!