sherlock-audit / 2024-10-ethos-network-judging

0 stars 0 forks source link

aycozyOx - Lack of Access Control in registerAddress Function of EthosProfile.sol #283

Open sherlock-admin2 opened 2 weeks ago

sherlock-admin2 commented 2 weeks ago

aycozyOx

Medium

Lack of Access Control in registerAddress Function of EthosProfile.sol

Summary

The registerAddress function in EthosProfile.sol is used by a profile owner to add other addresses to their profile as long as they have not reached the maximum amount applicable per user. The issue lies in the fact that the function lacks proper access control and can allow a malicious actor to register addresses to a profile up to the maximum limit. This can prevent the profile owner from adding any more addresses.

Root Cause

The root cause of this issue is the absence of access control mechanisms in the registerAddress function. The function allows any external caller to register an address to a profile without verifying the caller’s authorization

Internal pre-conditions

-The function is called with a valid addressStr, profileId, randValue, and signature. -The profile identified by profileId is verified and not archived or mocked.

External pre-conditions

No response

Attack Path

-A malicious actor calls the registerAddress function with a valid addressStr, profileId, randValue, and signature. -The function registers the address to the profile without verifying the caller’s authorization. -The malicious actor repeats this process until the maximum number of addresses is registered to the profile. -The profile owner is unable to add any more addresses due to the maximum limit being reached.

Impact

The profile owner is prevented from adding new addresses to their profile, leading to a denial of service.

PoC

Here is the registerAddress() in EthosProfile , which doesn't check if caller is the owner of the profile Id thereby allowing malicious caller to possibly add non-valid address to a different Profile up to the max. All checks in the function fails to check if the caller is a registered address to the profile Id

https://github.com/sherlock-audit/2024-10-ethos-network/blob/main/ethos/packages/contracts/contracts/EthosProfile.sol#L374-L410

function registerAddress(
    address addressStr,
    uint256 profileId,
    uint256 randValue,
    bytes calldata signature
  ) external whenNotPaused onlyNonZeroAddress(addressStr) {
    (bool verified, bool archived, bool mock) = profileStatusById(profileId);
    if (!verified) {
      revert ProfileNotFound(profileId);
    }
    if (archived || mock) {
      revert ProfileAccess(profileId, "Profile is archived");
    }
    // you may restore your own previously deleted address,
    // but you cannot register an address that has been deleted by another user
    if (profileIdByAddress[addressStr] != profileId && isAddressCompromised[addressStr]) {
      revert AddressCompromised(addressStr);
    }
    (bool addressAlreadyRegistered, , , uint256 registeredProfileId) = profileStatusByAddress(
      addressStr
    );
    if (addressAlreadyRegistered && registeredProfileId != profileId) {//
      revert ProfileExistsForAddress(addressStr);
    }

    validateAndSaveSignature(
      _keccakForRegisterAddress(addressStr, profileId, randValue),
      signature
    );

    profiles[profileId].addresses.push(addressStr);
    profileIdByAddress[addressStr] = profileId;

    checkMaxAddresses(profileId);

    emit AddressClaim(profileId, addressStr, AddressClaimStatus.Claimed);
  }

Mitigation

Implement the addressBelongsToProfile() in the registerAddress function to validate the identity of the caller

  function registerAddress(
    address addressStr,
    uint256 profileId,
    uint256 randValue,
    bytes calldata signature
  ) external whenNotPaused onlyNonZeroAddress(addressStr) { 

  ++  Bool profileOwner   =   addressBelongsToProfile(msg.sender, profileId);
  ++  if  (!profileOwner) {revert Notvalidsender}

    (bool verified, bool archived, bool mock) = profileStatusById(profileId);
    if (!verified) {
      revert ProfileNotFound(profileId);
    }
    if (archived || mock) {
      revert ProfileAccess(profileId, "Profile is archived");
    }