ThetokenURI(uint256 tokenId)
function in the SoulboundProfileNFT
contract uses abi.encodePacked()
with multiple dynamic types (strings) to construct JSON metadata. This practice can lead to hash collisions when used with keccak256() or similar functions. The correct approach is to use abi.encode()
instead, which ensures proper 32-byte padding and prevents concatenation conflicts.
Code Snippet:
Why is this an issue?
abi.encodePacked()
concatenates variables without enforcing 32-byte padding.
Dynamic types (like string and bytes) can merge incorrectly, leading to different inputs producing the same hash.
This hash collision can cause unexpected behavior in contracts that rely on keccak256()
for unique identifiers.
PoC
Consider two different sets of inputs that could produce the same hash:
A malicious actor could exploit this to bypass validation checks or manipulate metadata references.
High Severity: Hash collisions can lead to unexpected behavior in metadata retrieval, potentially causing incorrect NFT attributes to be displayed.
Metadata Integrity Risk: If a function relies on keccak256(abi.encodePacked(...)) to generate a unique ID, two different sets of inputs may yield the same result, compromising NFT uniqueness.
Potential Exploits: Attackers could manipulate JSON metadata to misrepresent attributes, affecting the value and trust in the NFT collection.
Manual Review (to confirm hash collision risk)
Aderyn
To prevent hash collisions, replace abi.encodePacked()
with abi.encode()
or use explicit concatenation methods (bytes.concat())
.
Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelyhood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.