Arbitrary authority modification in SetAuthority
Description
The SetAuthority path does not strictly validate the offsets and instruction indexes used by the secp256k1 native program. As a result, the data covered by secp signature verification and the data subsequently read by the contract may potentially come from different instructions.
An attacker can make secp verify a legitimate but business-irrelevant signed data segment, while having the contract read, and use another segment of data that is not protected by the signature — thereby bypassing signature binding and changing the target account's authority to an attacker-specified value.
fn process_set_authority_instruction<'a>(
program_id: &Pubkey,
token_account_info: AccountInfo<'a>,
authority_account_info: AccountInfo<'a>,
sysvars_instruction_info: AccountInfo<'a>,
recent_blockhashes_account_info: AccountInfo<'a>,
) -> ProgramResult {
...
// is that instruction is `new_secp256k1_instruction`
if instruction.program_id != secp256k1_program::id() {
msg!("Incorrect program id for secp256k1 instruction");
return Err(ClaimableProgramError::Secp256InstructionLosing.into());
}
// Parse the secp256k1 instruction
let offsets = SecpSignatureOffsets::try_from_slice(
&instruction.data[1..(1 + SIGNATURE_OFFSETS_SERIALIZED_SIZE)],
)?;
let eth_address_signer = &instruction.data
[offsets.eth_address_offset as usize..(offsets.eth_address_offset as usize + size_of::<EthereumAddress>())];
let message = &instruction.data
[offsets.message_data_offset as usize
..(offsets.message_data_offset + offsets.message_data_size) as usize
];
...Impact
An attacker can construct a signature unrelated to the target account, execute SetAuthority on any claimable token account, and specify an arbitrary new_authority. This allows them to take over subsequent asset-transfer rights of the account, ultimately resulting in asset loss.
Recommendations
Validate the secp offsets and indexes in the set_authority instruction. Also validate that there is only one secp instruction.
Remediation
This issue has been acknowledged by Audius, and a fix was implemented in commit 76d5ee21↗.