For the complete documentation index, see llms.txt. This page is also available as Markdown.

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 | Belong

  • 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?