DexLyn Disclosed Report

Unauthorized Announcements

Company
Created date
Feb 22 2025

Target

https://github.com/DexlynLabs/bridge-repo/tree/6a772c126e0879928021156ca91b89ac4f1bfcae/move

Vulnerability Details

In the announce function, any account can call the function and announce storage metadata for a validator without requiring the validator’s signature to be verified before storing the data.

public entry fun announce( account: &signer, validator: address, signature: vector<u8>, storage_location: String ) acquires ValidatorState { let validator_state = borrow_global_mut<ValidatorState>(@hp_validator);

// Ensure that the same storage metadata isn't being announced multiple times for the same validator. let replay_id = hash_concat( bcs::to_bytes(&validator), *string::bytes(&storage_location) ); assert!(!vector::contains(&validator_state.replay_protection, &replay_id), ERROR_ANNOUNCE_REPLAY); vector::push_back(&mut validator_state.replay_protection, replay_id);

// Verify that the signature matches the declared validator verify_validator_signed_announcement_internal( validator_state, validator, signature, storage_location );

// Store the announcement, Update storage locations if (!vector::contains(&validator_state.validators_list, &validator)) { vector::push_back(&mut validator_state.validators_list, validator); simple_map::add(&mut validator_state.storage_locations, validator, vector::empty<String>()); }; let locations = simple_map::borrow_mut<address, vector<String>>( &mut validator_state.storage_locations, &validator ); vector::push_back(locations, storage_location);

// emit events event::emit_event<AnnouncementEvent>( &mut validator_state.announcement_events, events::new_validator_announce_event( validator, storage_location ) ); }

The announce function is an entry function, meaning it can be called by any signer. However, it does not check whether account (the caller) is the validator. Instead, it allows any user to submit a validator’s storage location, relying only on verify_validator_signed_announcement_internal to check the signature.

Even though the signature is checked, it does not prevent unauthorized accounts from calling the function. An attacker could repeatedly attempt forged signatures and flood the contract with fake announcements.

Validation steps

Solution: Restrict Announcements to the Validator Modify the announce function to ensure that only the validator themselves can submit an announcement.

Fix: Add a Validator Check

public entry fun announce( account: &signer, validator: address, signature: vector<u8>, storage_location: String ) acquires ValidatorState { let validator_state = borrow_global_mut<ValidatorState>(@hp_validator);

// Ensure the caller is the validator assert!(signer::address_of(account) == validator, ERROR_INVALID_ACCOUNT);

// Ensure that the same storage metadata isn't being announced multiple times for the same validator. let replay_id = hash_concat( bcs::to_bytes(&validator), *string::bytes(&storage_location) ); assert!(!vector::contains(&validator_state.replay_protection, &replay_id), ERROR_ANNOUNCE_REPLAY); vector::push_back(&mut validator_state.replay_protection, replay_id);

// Verify that the signature matches the declared validator verify_validator_signed_announcement_internal( validator_state, validator, signature, storage_location );

// Store the announcement, Update storage locations if (!vector::contains(&validator_state.validators_list, &validator)) { vector::push_back(&mut validator_state.validators_list, validator); simple_map::add(&mut validator_state.storage_locations, validator, vector::empty<String>()); }; let locations = simple_map::borrow_mut<address, vector<String>>( &mut validator_state.storage_locations, &validator ); vector::push_back(locations, storage_location);

// emit events event::emit_event<AnnouncementEvent>( &mut validator_state.announcement_events, events::new_validator_announce_event( validator, storage_location ) ); } Why is this Fix Important? Prevents Unauthorized Announcements: Attackers can no longer submit announcements on behalf of a validator. Enhances Security with signer::address_of(account): Ensures the caller is the actual validator and not a random user. Reduces Potential DoS Attack Surface: Prevents spam submissions from unauthorized users.

CommentsReport History
Comments on this report are hidden
Details
Statedisclosed
Severity
None
Bounty
hidden
Visibilitypartially
VulnerabilityDoS with (Unexpected) revert
Participants (4)
company admin
triage team
triage team
author