Skip to content

Latest commit

 

History

History
115 lines (84 loc) · 3.31 KB

File metadata and controls

115 lines (84 loc) · 3.31 KB

Square Pistachio Unicorn

High

attestationById is not kept in sync with attestationByHash

Summary

EthosAttestation uses two mappings attestationById which maps id -> Attestation and a convenience mapping attestationByHash which maps hash -> Attestation. Sponsor confirmed (private thread) that Attestation modification should be applied to both structs/mappings.

However attestationById is not being updated, including during claim/archive/restore operations.

Root Cause

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

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

attestationByHash[attestationHash].archived = true;

emit AttestationArchived(
  profileId,
  attestation.service,
  attestation.account,
  attestation.attestationId
);

And all other attestationByHash modifications:

https://github.com/sherlock-audit/2024-10-ethos-network/blob/main/ethos/packages/contracts/contracts/EthosAttestation.sol#L304 https://github.com/sherlock-audit/2024-10-ethos-network/blob/main/ethos/packages/contracts/contracts/EthosAttestation.sol#L313 https://github.com/sherlock-audit/2024-10-ethos-network/blob/main/ethos/packages/contracts/contracts/EthosAttestation.sol#L389

Internal pre-conditions

No response

External pre-conditions

No response

Attack Path

No response

Impact

Incorrect state/accounting. attestationById and attestationByHash are both publicly exposed, and users/contracts are expected to access them to get correct Attestation data.

PoC

// EthosAttestation.test.ts
it.only('archived_should_sync', async () => {
  const {
    OWNER,
    PROFILE_CREATOR_0,
    ethosProfile,
    ethosAttestation,
    SERVICE_X,
    ACCOUNT_NAME_BEN,
    ATTESTATION_EVIDENCE_0,
    EXPECTED_SIGNER,
    OTHER_0,
  } = await loadFixture(deployFixture);

  await ethosProfile.connect(OWNER).inviteAddress(PROFILE_CREATOR_0.address);
  await ethosProfile.connect(PROFILE_CREATOR_0).createProfile(1);
  const randValue = '123';

  await ethosProfile.connect(OWNER).inviteAddress(OTHER_0.address);
  await ethosProfile.connect(OTHER_0).createProfile(1);
  const other0profileId = String(await ethosProfile.profileIdByAddress(OTHER_0.address));
  const signature = await common.signatureForCreateAttestation(
    other0profileId,
    randValue,
    ACCOUNT_NAME_BEN,
    SERVICE_X,
    ATTESTATION_EVIDENCE_0,
    EXPECTED_SIGNER,
  );
  await ethosAttestation
    .connect(OTHER_0)
    .createAttestation(
      other0profileId,
      randValue,
      { account: ACCOUNT_NAME_BEN, service: SERVICE_X },
      ATTESTATION_EVIDENCE_0,
      signature,
    );

  const attestationHash = await ethosAttestation.getServiceAndAccountHash(
    SERVICE_X,
    ACCOUNT_NAME_BEN,
  );

  await ethosAttestation.connect(OTHER_0).archiveAttestation(attestationHash);

  // ---

  const attestation_by_hash = await ethosAttestation.attestationByHash(attestationHash);

  const attestation_by_id = await ethosAttestation.attestationById(attestation_by_hash.attestationId);

  expect(attestation_by_hash.createdAt).to.be.equal(attestation_by_id.createdAt);  // ok
  expect(attestation_by_hash.archived).to.be.equal(attestation_by_id.archived);    // fails.
});

Mitigation

Update both mappings.