#47198 [SC-Critical] The operator can perform unauthorized fund transfers.
Description
Brief/Intro
Vulnerability Details
fn register_sub_operator(
ref self: ContractState,
vault: ContractAddress,
sub_operator: ContractAddress,
nonce: felt252,
expiry: u64,
signature: Array<felt252>,
) {
let caller: ContractAddress = get_caller_address();
let vault_operator: ContractAddress = self.get_operator(vault);
assert!(caller == vault_operator, "Caller is not vault operator");
assert!(starknet::get_block_timestamp() < expiry, "Expired sub-operator signature");
self.nonces.use_checked_nonce(sub_operator, nonce);
// Build hash for calling `is_valid_signature`
let message = SubOperatorRegistrationMessage {
vault, sub_operator, nonce, expiry: expiry.try_into().unwrap(),
};
let hash = message.get_message_hash(sub_operator);
let is_valid_signature_felt = ISRC6Dispatcher { contract_address: sub_operator }
.is_valid_signature(hash, signature);
// Check either 'VALID' or True for backwards compatibility for legacy accounts before
// SNIP-6
let is_valid_signature = is_valid_signature_felt == starknet::VALIDATED
|| is_valid_signature_felt == 1;
assert!(is_valid_signature, "Invalid sub-operator signature");
// Get current number of sub-operators for this operator
let current_len: u8 = self.operator_sub_operators_len.read(vault_operator);
// Check if sub_operator is already registered
let other_vault_operator = self.get_parent_operator(sub_operator);
// Re-emit event if sub-operator is already registered
// This is to ensure replayability of events if downstream services fail to save mapping
if other_vault_operator == vault_operator {
self
.emit(
SubOperatorRegistered {
vault: vault, operator: vault_operator, sub_operator: sub_operator,
},
);
return;
}
assert!(other_vault_operator.is_zero(), "Sub-operator already registered to a vault");
// Assert that we haven't reached the maximum number of sub-operators
let max_sub_operators: u8 = self.max_sub_operators.read();
assert!(current_len < max_sub_operators, "Max sub-operators reached");
// Add sub-operator to the list
self.operator_to_sub_operators.write((vault_operator, current_len), sub_operator);
// Increment the count of sub-operators for this operator
self.operator_sub_operators_len.write(vault_operator, current_len + 1);
// Maintain reverse mapping
self.sub_operator_to_operator.write(sub_operator, vault_operator);
...Impact Details
References
Proof of Concept
Proof of Concept
Previous#46997 [SC-Medium] The vault performs an unsafe conversion on the getAccountValue result.Next#47257 [SC-Insight] Lack of position quantity limit for a single account.
Was this helpful?