57236 sc medium accesstoken collection front running attack permanent ownership hijack

Submitted on Oct 24th 2025 at 16:01:27 UTC by @failsafe_intern for Audit Comp | Belongarrow-up-right

  • Report ID: #57236

  • Report Type: Smart Contract

  • Report severity: Medium

  • Target: https://github.com/immunefi-team/audit-comp-belong/blob/main/contracts/v2/utils/SignatureVerifier.sol

  • Impacts:

    • Theft of unclaimed royalties

    • Unintended alteration of what the NFT represents (e.g. token URI, payload, artistic content)

Description

Vulnerability Overview

Factory.produce() validates AccessTokenInfo signatures but the signed message omits the intended creator/owner and includes no nonce or expiration. Any party with a valid signature can front-run the legitimate creator and deploy the collection under their own ownership, permanently blocking the rightful deployment due to deterministic salt collision.

Root Cause

SignatureVerifier.sol:51-72 - checkAccessTokenInfo signature omits creator:

function checkAccessTokenInfo(address signer, AccessTokenInfo memory accessTokenInfo) external view {
    require(
        signer.isValidSignatureNow(
            keccak256(
                abi.encodePacked(
                    accessTokenInfo.metadata.name,
                    accessTokenInfo.metadata.symbol,
                    accessTokenInfo.contractURI,
                    accessTokenInfo.feeNumerator,
                    block.chainid
                )
            ),
            accessTokenInfo.signature
        ),
        InvalidSignature()
    );
}

Missing from hash: intended creator address, nonce, expiration timestamp.

Factory.sol:175-239 - produce sets creator = msg.sender after signature validation:

Attack Flow

1

Step

Attacker obtains a valid signature (mempool observation, leaked backend data, or social engineering) containing (name, symbol, contractURI, feeNumerator, chainid).

2

Step

Attacker front-runs the legitimate creator transaction by calling Factory.produce() first.

3

Step

Signature validation passes because only metadata/feeNumerator are checked (LINE 191).

4

Step

Factory sets creator = msg.sender (LINE 228) — attacker address becomes collection owner.

5

Step

Attacker's collection deployed to deterministic address based on keccak256(abi.encode(name, symbol)). Legitimate creator's transaction reverts with TokenAlreadyExists() (LINE 195).

6

Step

Permanent hijack: Same (name, symbol) cannot be redeployed; brand is captured by attacker.

Impact

Impact Category: Unintended alteration of what the NFT represents (e.g. token URI, payload, artistic content)

Operational Damage:

  • Complete ownership takeover: Attacker controls collection royalties, minting, metadata, and all creator functions

  • Brand capture: Legitimate creator permanently blocked from using their own collection name/symbol

  • Royalty theft: Attacker receives all secondary sale royalties intended for legitimate creator

  • Reputation damage: Fake collection under attacker control tarnishes brand

  • No recovery: Deterministic deployment prevents legitimate redeployment without contract upgrade

Business Impact:

  • Creators lose anticipated royalty revenue (potentially significant for popular collections)

  • Platform reputation damaged when creators discover collections hijacked

  • Backend signature credentials must be immediately rotated if compromised

  • Legal/customer support costs to resolve ownership disputes

https://gist.github.com/Joshua-Medvinsky/35c232c0b9da435fd072f742319671c0

Proof of Concept

GitHub Gist POC: https://gist.github.com/Joshua-Medvinsky/35c232c0b9da435fd072f742319671c0

Test Results: ✅ 7/7 tests passed (front-running, permanent blocking, royalty theft demonstrated)

Prerequisites

Attack Execution

1

Capture Valid Signature

2

Front-Run with Higher Gas

3

Verify Hijack

4

Legitimate Creator Blocked

5

Demonstrate Royalty Theft

Expected vs Actual Behavior

Expected: Signature should authorize collection creation only for a specific creator address.

Actual: Signature authorizes collection creation for ANY caller, enabling first-come-first-served ownership race.

Immediate Fix (Contract Upgrade Required)

Modify checkAccessTokenInfo to include the intended creator in the signed payload:

Update Factory.produce to bind the signature to the caller:

Add nonce protection:

  • Add state variable in Factory:

  • Add nonce to AccessTokenInfo struct:

  • Include nonce in signature hash:

  • Validate and increment in produce():

Add expiration timestamp:

  • Add to AccessTokenInfo struct:

  • Validate in produce():

Defense-in-Depth Recommendations

  1. Commit-Reveal Scheme: Creator commits to deployment before revealing signature

  2. Allowlist: Maintain on-chain registry of creator addresses authorized to deploy

  3. Two-Step Deployment: Creator registers intent on-chain before final deployment

  4. Backend Rate Limiting: Issue signatures only after verifying requester identity

  5. Signature Revocation: Backend maintains revocable signature registry

References

  • Vulnerable Function: checkAccessTokenInfo (SignatureVerifier.sol:51-72)

  • Exploitable Function: produce (Factory.sol:175-239)

  • Ownership Assignment: AccessToken.initialize (AccessToken.sol:86-101)

  • Deterministic Salt: _metadataHash (Factory.sol:193)

Was this helpful?