57796 sc medium signature hashing collision in signatureverifier lets attacker deploy forged accesstoken credittoken metadata critical unintended alteration of what the nft represents
Submitted on Oct 28th 2025 at 22:51:15 UTC by @Codexstar for Audit Comp | Belong
Impacts: Unintended alteration of what the NFT represents (e.g. token URI, payload, artistic content)
Description
Brief / Intro
The platform authorizes collection deployments via signatures checked in SignatureVerifier. These hashes are built with abi.encodePacked over multiple dynamic strings, which is ambiguous. Different (name, symbol, contractURI) (or (name, symbol, uri)) tuples can collide to the same bytes, so a valid signature issued for one tuple can be replayed to deploy a collection with different, forged metadata. This allows unauthorized alteration of what the NFT represents (branding, symbol, contract URI), meeting the Critical impact category.
Vulnerability Details
The authorization hashing for AccessToken and CreditToken uses abi.encodePacked across multiple dynamic strings:
Factory trusts those verifications and proceeds with deployment:
Why exploitable: abi.encodePacked concatenates dynamic strings without boundaries. Example: "abc"||"x"||"" equals "ab"||"cx"||"". So a signature for (name='abc', symbol='x', contractURI='') also verifies for (name='ab', symbol='cx', contractURI=''). The factory then deploys the collection with the forged metadata.
Impact Details
Matches in-scope Critical impact “Unintended alteration of what the NFT represents (e.g. token URI, payload, artistic content)”.
Attacker can deploy unauthorized collections with altered name, symbol, contractURI/uri.
Step 1 — Obtain a legitimate platform signature for tuple A
Example for AccessToken: (name='abc', symbol='x', contractURI='', feeNumerator=f, chainId).
2
Step 2 — Craft tuple B that collides under packed encoding
Example: (name='ab', symbol='cx', contractURI='', feeNumerator=f, chainId) because abi.encodePacked('abc','x','',f,chainId) == abi.encodePacked('ab','cx','',f,chainId).
3
Step 3 — Call Factory.produce with tuple B and reuse signature for tuple A
Use the signature issued for tuple A but submit tuple B to the factory.
4
Step 4 — Signature verification passes and the factory deploys the collection
SignatureVerifier.checkAccessTokenInfo passes due to the collision; the factory deploys a collection with forged (name, symbol, contractURI).
5
Step 5 — The same applies to CreditToken
The same technique works against Factory.produceCreditToken by forging (name, symbol, uri) tuples.
Runnable PoC (optional)
test/v2/platform/signature-collision.test.ts performs these steps end-to-end, deploying the factory, reusing a valid signature, and asserting that the forged metadata is stored for the new collection.
PoC Code
Recommendation
Replace abi.encodePacked with abi.encode wherever signature hashes include dynamic types in SignatureVerifier.
Bind signatures to all critical parameters intended to be controlled by the platform (payment token, prices, supply caps, transferability, etc.).
Add nonce and expiry fields and bind the verifying contract address to prevent replay or cross-contract reuse.