Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield
Description
Brief/Intro
The NFT factory's signature verification mechanism only validates immutable metadata (name, symbol, contract_uri, royalty_fraction) while blindly trusting caller-supplied mutable parameters like payment_token, mint_price, max_total_supply, transferrable flag, and referral_code. An attacker holding a legitimate platform signature can replay it to deploy the same collection with malicious economics—redirecting mint revenues to an attacker-controlled ERC-20, manipulating prices to bypass fee expectations, or diverting referral rewards. This enables theft of platform fees and user funds.
Vulnerability Details
The signature validation flow in the NFT factory has a critical gap between what gets signed off-chain and what gets enforced on-chain.
What's signed (src/snip12/produce_hash.cairo:7-33):
What's actually deployed (src/nftfactory/nftfactory.cairo:275-307): The produce function accepts an InstanceInfo struct containing both signed and unsigned fields. After verifying the signature covers only the four fields above, the factory proceeds to deploy the NFT contract using all parameters from the caller-supplied struct, including:
payment_token: arbitrary ERC-20 address
mint_price and whitelisted_mint_price: arbitrary prices
max_total_supply: supply cap
transferrable: transfer restrictions
referral_code: revenue routing
These mutable parameters are written directly into both the NFT contract storage and the factory's nftInfo mapping without any validation against the signature. The platform's off-chain signature approval process presumably checks these fields before signing, but that check becomes meaningless since the signature doesn't bind them.
A legitimate creator who receives a valid signature from the platform can simply call produce again with the same signed fields (name, symbol, uri, royalty) but substitute malicious values for the unsigned parameters. The signature remains valid, and the factory deploys a new collection with attacker-chosen economics.
Impact Details
Direct financial impact:
Revenue theft: An attacker deploys using their own ERC-20 as payment_token, capturing all mint revenues that should go to the platform
Fee bypass: Setting mint_price to 0 or 1 wei circumvents expected platform fees while the metadata appears legitimate
Referral manipulation: Using a different referral_code redirects commission payments away from intended promoters
Operational impact:
The platform's off-chain approval process becomes security theater—signatures can authorize deployments the platform never approved
Users minting from these collections lose funds to attacker-controlled tokens
The platform's reputation suffers when legitimate-looking collections turn out to be scams
Attack scenario:
1
Attacker obtains a valid signature for a collection
Example: signature for collection "Art Project" with 10% royalty.
Attacker reuses the signature but tampers with mutable params
Call produce with same name/symbol/uri but payment_token = AttackerToken, mint_price = 1 wei, etc.
4
Factory validates signature and deploys
Signature verification passes because it only covered immutable fields.
5
Users mint with attacker-controlled token
Users send AttackerToken (valueless) instead of expected USDC.
6
Platform and users lose funds/revenue
Platform receives no revenue; users pay attacker-controlled token; reputation damaged.
The selected impact is "Theft of unclaimed royalties" at the High severity level, as this allows unauthorized redirection of platform fees and mint revenues.
The test test_produce_signature_not_binding_mutable_params demonstrates the exploit:
Run with:
The test passes, proving that a valid signature successfully deploys a collection with attacker-controlled payment_token, mint_price, and other critical parameters. Both the NFT contract storage and factory's nftInfo mapping reflect the malicious values.
Recommended fix: Extend ProduceHash to include all mutable deployment parameters so the off-chain signature binds everything that will be written to on-chain storage. Example extension: