The DelegateTokenRegistryHelpers contract relies on the DelegateRegistry to store important access control data. However, the registry uses predictable hashes and storage locations that an attacker may be able to collide. This could lead to delegation data being overwritten and corrupted.
Vulnerability Details
The DelegateRegistry uses the keccak256 hash of predictable parameters like contract address and token ID to derive a storage location for each delegation. For example:
The delegateFrom, delegateTo, tokenContract, and tokenId values are all predictable. This allows an attacker to potentially provide colliding values that hash to the same storage location. If two delegations resolve to the same hash, the newer one will overwrite the older one.
For example, an attacker could create a fake delegation with:
By overwriting the target delegation, the attacker could gain access to assets.
Impact
This vulnerability could allow an attacker to overwrite arbitrary delegation storage slots if they can find colliding parameter values. They could then replace existing delegations with their own fake data and gain unintended access to assets.
Code Snippet
The loadTokenHolder function uses the predictable registryHash to load delegation data:
Contract calls loadTokenHolder() for delegation A, gets B's data
// Contract tries to load original delegation
address originalDelegate = DelegateTokenRegistryHelpers.loadTokenHolder(
delegateRegistry,
registryHashA // which now matches registryHashB!
);
// originalDelegate is now the attacker's fake delegate from B
Tools Used
Manual code review
Recommended Mitigation Steps
Use unpredictable salts/nonces in hashes
// Add salt param to prevent collisions
bytes32 saltedHash = keccak256(abi.encodePacked(
salt,
delegateFrom,
delegateTo,
...
));
Lines of code
https://github.com/code-423n4/2023-09-delegate/blob/a6dbac8068760ee4fc5bababb57e3fe79e5eeb2e/src/libraries/DelegateTokenRegistryHelpers.sol#L16-L22
Vulnerability details
Summary
The
DelegateTokenRegistryHelpers
contract relies on theDelegateRegistry
to store important access control data. However, the registry uses predictable hashes and storage locations that an attacker may be able to collide. This could lead to delegation data being overwritten and corrupted.Vulnerability Details
The DelegateRegistry uses the keccak256 hash of predictable parameters like contract address and token ID to derive a storage location for each delegation. For example:
The delegateFrom, delegateTo, tokenContract, and tokenId values are all predictable. This allows an attacker to potentially provide colliding values that hash to the same storage location. If two delegations resolve to the same hash, the newer one will overwrite the older one.
For example, an attacker could create a fake delegation with:
delegateFrom: 0x1234...5678 delegateTo: 0x1234...5679 tokenContract: 0x0000000000000000000000000000000000000123 tokenId: 1
This could collide with and overwrite a target delegation with:
delegateFrom: 0x1234...5678 delegateTo: 0x1234...5678 tokenContract: 0x0000000000000000000000000000000000000001
tokenId: 2
By overwriting the target delegation, the attacker could gain access to assets.
Impact
This vulnerability could allow an attacker to overwrite arbitrary delegation storage slots if they can find colliding parameter values. They could then replace existing delegations with their own fake data and gain unintended access to assets.
Code Snippet
The loadTokenHolder function uses the predictable registryHash to load delegation data:
Proof of Concept
Attacker finds collision between legitimate delegation A and fake delegation B
Attacker creates fake delegation B, overwriting A's slot
Contract calls loadTokenHolder() for delegation A, gets B's data
Tools Used
Manual code review
Recommended Mitigation Steps
Use unpredictable salts/nonces in hashes
Validate registryHash integrity
Handle collisions gracefully
Use enumeration instead of direct access
Assessed type
Other