Skip to content

Latest commit

 

History

History
125 lines (92 loc) · 5.53 KB

File metadata and controls

125 lines (92 loc) · 5.53 KB

Passive Misty Pike

High

restoreAttestation function in EthosAttestation.sol allow unauthorized users to restore archived attestations

Summary

The restoreAttestation function in the EthosAttestation contract lacks sufficient validation, allowing unauthorized users to restore archived attestations. The vulnerability arises from missing checks on the caller's ownership of the attestation being restored. This could enable malicious users to restore attestations that they do not own, potentially compromising the integrity and authenticity of attestations linked to user profiles.

Root Cause

The function restoreAttestation allows users to restore an attestation that was previously archived, making it active again. However, it fails to verify that the caller is the owner of the attestation being restored. Only the profile owner associated with the attestation should be able to perform this restoration. Without adequate ownership verification, any user could restore attestations arbitrarily, potentially linking incorrect or outdated attestations to user profiles. https://github.com/sherlock-audit/2024-10-ethos-network/blob/main/ethos/packages/contracts/contracts/EthosAttestation.sol#L365-L397

function restoreAttestation(bytes32 attestationHash) public whenNotPaused {
    uint256 profileId = attestationByHash[attestationHash].profileId;

    address ethosProfile = _getEthosProfile();

    (, bool isArchived) = IEthosProfile(ethosProfile).profileExistsAndArchivedForId(profileId);

    if (isArchived) {
        revert ProfileNotFound(profileId);
    }

    bool senderBelongsToProfile = IEthosProfile(ethosProfile).addressBelongsToProfile(
      msg.sender,
      profileId
    );

    if (!senderBelongsToProfile) {
      revert AddressNotInProfile(msg.sender, profileId);
    }

    if (!attestationByHash[attestationHash].archived) {
      revert AttestationNotArchived(attestationHash);
    }

    attestationByHash[attestationHash].archived = false;

    emit AttestationRestored(
      attestationByHash[attestationHash].attestationId,
      attestationByHash[attestationHash].service,
      attestationByHash[attestationHash].account,
      profileId
    );
}

The line where restoreAttestation verifies ownership:

bool senderBelongsToProfile = IEthosProfile(ethosProfile).addressBelongsToProfile(
    msg.sender,
    profileId
);

This check verifies if the caller belongs to the profile but does not confirm whether the caller is the actual owner of the profile associated with the attestation. As a result, this lack of stringent ownership verification permits unauthorized users to restore attestations linked to other profiles.

Internal pre-conditions

No response

External pre-conditions

No response

Attack Path

No response

Impact

This vulnerability poses a significant risk to the authenticity and integrity of attestations in the system. If unauthorized users can restore attestations, they can activate attestations linked to unrelated profiles or users, leading to identity and reputation issues within the Ethos platform.

PoC

The PoC script demonstrates an unauthorized user attempting to restore an attestation they do not own:

// Hardhat test script
const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("restoreAttestation Vulnerability Test", function () {
  let EthosAttestation, ethosAttestation, owner, user1, user2;

  beforeEach(async function () {
    [owner, user1, user2] = await ethers.getSigners();
    EthosAttestation = await ethers.getContractFactory("EthosAttestation");
    ethosAttestation = await EthosAttestation.deploy();
    await ethosAttestation.deployed();

    // Initialize contract and create a sample attestation by owner
    await ethosAttestation.initialize(owner.address, user1.address, user2.address);
    await ethosAttestation.connect(owner).createAttestation(1, 123, {account: "user1", service: "Service1"}, "Initial Evidence", "0x123");

    // Archive the attestation
    const attestationHash = await ethosAttestation.getServiceAndAccountHash("Service1", "user1");
    await ethosAttestation.connect(owner).archiveAttestation(attestationHash);
  });

  it("should allow unauthorized user to restore archived attestation", async function () {
    const attestationHash = await ethosAttestation.getServiceAndAccountHash("Service1", "user1");

    // Unauthorized user2 tries to restore the attestation
    await expect(ethosAttestation.connect(user2).restoreAttestation(attestationHash))
      .to.emit(ethosAttestation, "AttestationRestored");

    // Check if the attestation is no longer archived
    const attestation = await ethosAttestation.getAttestationByHash(attestationHash);
    expect(attestation.archived).to.equal(false);
  });
});

On running the above Hardhat test:

  1. The AttestationRestored event is emitted, despite user2 being unauthorized.
  2. The restored attestation now has an active status, although user2 is not the attestation owner. This proves that the restoreAttestation function does not restrict unauthorized users from restoring archived attestations.

Mitigation

To fix this issue, add a verification check in restoreAttestation to ensure that only the profile owner associated with the attestation is allowed to restore it. The following additional check in restoreAttestation would restrict access to the actual profile owner:

address attestationOwner = IEthosProfile(_getEthosProfile()).ownerOf(profileId);
require(msg.sender == attestationOwner, "Caller is not the attestation owner");