56869 sc medium hijacking deployment of accesstoken and stealing ownership to prevent further deployments
Submitted on Oct 21st 2025 at 11:39:54 UTC by @blackgrease for Audit Comp | Belong
Report ID: #56869
Report Type: Smart Contract
Report severity: Medium
Target: https://github.com/belongnet/checkin-contracts/blob/main/contracts/v2/platform/Factory.sol
Impacts
Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield
Unintended alteration of what the NFT represents (e.g. token URI, payload, artistic content)
Griefing (e.g. no profit motive for an attacker, but damage to the users or the protocol)
Description
Affected Files: Factory.sol and AccessToken.sol
The Factory::produce function creates a new AccessToken collection and an optional RoyaltiesReceiver (if feeNumerator > 0). The function requires a signature from an authorized signer on a hash of NFT name, symbol, contractURI, feeNumerator and block.chainid. However, the function is permissionless — anyone can call it with a valid signature. The deployment uses CREATE2 deterministic addresses and AccessToken is Ownable, so the caller becomes the owner of the deployed instance.
The salt hash is derived from token name and symbol; for a given name/symbol pair there cannot be duplicate AccessToken instances:
//--snip--
bytes32 hashedSalt = _metadataHash(accessTokenInfo.metadata.name, accessTokenInfo.metadata.symbol);
require(getNftInstanceInfo[hashedSalt].nftAddress == address(0), TokenAlreadyExists());
//--snipThe Issue
An attacker can front-run Factory::produce to take ownership of the AccessToken collection and gain full ownership of the instance. Because only one deployment exists for a given token name & symbol, the intended user's subsequent transaction will fail, causing a denial of service.
Factory::produce does not validate variables beyond those signed, allowing an attacker to alter values like feeNumerator. If feeNumerator > 0, this deploys a RoyaltiesReceiverV2 which the attacker can receive profits from.
By taking over the AccessToken and becoming owner, an attacker can:
prevent any other deployment of the same name/symbol pair (denial of service),
change minting prices,
receive royalties if
feeNumeratorwas set,upgrade
AccessTokento a malicious version and alter the NFT's behavior/content.
The legitimate user would need the Signer to sign new metadata, but the attack can be repeated, so the issue persists until fixed.
Full problematic code (excerpt)
Impact
Disruption of legitimate operations: further
AccessTokendeployments for the same name/symbol will revert (DoS).Loss of royalties for the intended recipient if an attacker set
feeNumerator.Business loss due to altered mint prices.
Griefing by making NFTs inaccessible or modifying mint economics.
Arbitrary upgrades of
AccessTokento change NFT behavior/content.
Likelihood of exploitation: High (no execution restrictions; attacker simply front-runs a transaction).
Mitigation
Control who can call Factory::produce ensuring the caller matches the intended input, or include the caller in the salt so the salt is unique per caller:
Suggested change:
This prevents an attacker from successfully front-running another caller for the same name/symbol.
Link to Proof of Concept
https://gist.github.com/blackgrease/46e26dc7097dc866d2311ce7711f4523
Proof of Concept
The PoC (in the gist) demonstrates:
Front-running an intended owner's transaction to claim
AccessTokenownership.Confirming the attacker is owner by executing
onlyOwnerfunctionality.Confirming that only one
AccessTokenexists per name/symbol pair by showing the legitimate user's deployment reverts.
Conversion to Foundry and running the PoC
Note: In case of issues running the PoC, the gist includes a Foundry stack trace file: "PoC_StackTrace_AddressHijackingAndAccessTokenOwnershipTakeoever.txt"
End of report.
Was this helpful?