NFT Security: Vulnerabilities, Real Exploits, and How to Prevent Them

Updated on October 27th, 2026
NFT security covers everything that keeps tokens and the money around them safe: the contracts that mint and trade NFTs, the signatures that list them, the apps that display them, and the channels projects use to talk to holders. Most large NFT losses came from logic and process flaws rather than broken cryptography. Listings stayed valid after sellers thought they were gone, fees changed after a sale was agreed, image files ran scripts, and official channels started posting phishing links.
This guide covers the vulnerabilities that matter for NFT collections and marketplaces today, from smart contracts to the web app and the community around it. It walks through two real vulnerabilities reported through HackenProof bug bounty programs and ends with a checklist for launching an NFT project or marketplace with security built in.
Why NFTs Are a Prime Target
NFTs concentrate unique, high-value assets, and every transfer is final. There are no chargebacks, and a stolen NFT can be resold within minutes or moved through cross-chain bridges, which are frequent targets in their own right.
The attack surface is also wide. A typical NFT project or marketplace combines on-chain contracts that hold approvals and settle trades, off-chain order books where listings live as signed messages, a web app and API that render user-uploaded media, metadata stored on IPFS or private servers, and community channels where holders take announcements at face value. A weakness in any one of these layers can be enough to move assets out of users' wallets, and each one gets its own section below.
NFT Token Standards and Their Attack Surface
The token standard an NFT uses shapes much of its attack surface, so it is the first thing an auditor looks at.
ERC-721
ERC-721 gives each token a unique ID. Its safeTransferFrom and _safeMint functions call onERC721Received on contract recipients, which hands control to external code in the middle of a transaction. Its setApprovalForAll lets a single operator move every token a user holds in a collection, which is why drainers target it.
ERC-1155
ERC-1155 is a multi-token standard: one contract can manage fungible and non-fungible tokens together and move them in batches. It has the same receiver callbacks as ERC-721 for single and batch transfers, and the same collection-wide approval model, so the same reentrancy and approval risks apply.
Solana NFTs and Metaplex
On Solana, NFTs are SPL tokens described by Metaplex metadata standards. Most bugs here come from missing account checks: a program that doesn't verify the mint, the metadata account, the signer, or whether a collection is actually verified can be fed accounts the attacker controls.
Off-Chain Metadata
An NFT's name, image, and traits usually live off-chain behind a tokenURI. If that URI points to a server the project controls rather than content-addressed storage such as IPFS or Arweave, whoever controls the server controls what buyers see, and can swap the artwork after the sale.
Smart Contract Vulnerabilities in NFT Contracts
NFT contracts are usually small, but they hold approvals over every token users list and often custody payments. The flaws below are specific to NFTs and sit on top of the general rules of smart contract security.
Reentrancy Through Transfer Callbacks
safeTransferFrom and _safeMint call back into the recipient contract before the transaction finishes. If the contract updates mint counters, balances, or payment state after that call, an attacker can re-enter and mint past a per-wallet limit or withdraw twice. Follow the checks-effects-interactions pattern and add a reentrancy guard to every function that transfers tokens or ETH.
Broken Access Control
Unprotected mint, setBaseURI, withdraw, fee or royalty setters let anyone take over a collection or its treasury. Upgradeable contracts add another trap: an implementation whose initializer was never called can be initialized by an attacker. Use role-based access, put admin keys behind a multisig, and add a timelock to sensitive changes.
Overflow and Underflow
Since Solidity 0.8.0, arithmetic is checked by default: an operation that goes past a type's limits reverts instead of silently wrapping around. The risk now lives in two places. One is code inside unchecked blocks, which developers often use to save gas on loop counters and token ID increments. The other is contracts compiled with versions older than 0.8.0. Explicit type conversions also deserve attention, since downcasting a uint256 to a smaller type truncates the value without reverting unless you use a safe-cast library.
Predictable Randomness
Trait reveals and raffles that draw randomness from block.timestamp, blockhash, or block.prevrandao can be predicted or influenced, so attackers mint only the rare tokens. Use a verifiable randomness source such as Chainlink VRF, or a commit-reveal scheme.
Mint and Supply Logic Flaws
Limits enforced only in the frontend, such as a maximum per wallet, are bypassed by calling the contract directly. Allowlist Merkle proofs that aren't bound to msg.sender can be reused by anyone who sees them. Enforce every limit on-chain and test the edges: the last token of the supply, refunds, and mints split across many wallets.
Over-Broad Marketplace Approvals
Users grant marketplace contracts setApprovalForAll on whole collections. If that contract has an exploitable function, or a new version ships while old approvals stay live, every approved collection is exposed. Keep the contract that holds approvals minimal and audited, and tell users to revoke approvals when you migrate.
Static analyzers catch some of these patterns early, and running Slither on your contracts is a cheap first pass. Logic flaws still need human review, which is what a smart contract audit is for, before launch and after every upgrade.
Signature, Listing, and Marketplace Logic Flaws
Most marketplaces keep listings and offers off-chain as signed messages, and the contract only verifies them when someone fills the order. That saves users gas, but it means a signature can stay executable long after the seller has forgotten about it. This is where many of the costliest NFT bugs live.
Signature Replay
If a signed order has no nonce, no expiry, and no on-chain cancellation, it can be filled again after the seller cancelled it or after the trade already happened. Every order needs a nonce the contract tracks, an expiry it enforces, and a way for users to invalidate it on-chain.
Missing Domain Separation
Without the chain ID and the verifying contract's address in the signed data, a signature made for one deployment can be reused on another chain or on a new version of the same contract. EIP-712 typed data with a proper domain separator closes this gap.
Stale Listings
An order that stays valid after the NFT leaves the seller's wallet and comes back can be filled at an old price. In January 2022, OpenSea users who moved NFTs to another wallet and back saw their old listings disappear from the interface, but the signed orders were never cancelled on-chain. They could still be filled by anyone calling the contract directly. Buyers used this to pick up Bored Apes and other blue-chip NFTs at months-old prices, including BAYC #9991 for 0.77 ETH, and OpenSea reimbursed about 750 ETH to affected users.
Weak Signature Verification
Hand-rolled ecrecover logic can accept malleable signatures, or check who signed an order without checking that the signer still owns the token when the order executes. Use an audited library such as OpenZeppelin's ECDSA and re-check ownership at execution.
Fee and Settlement Logic Flaws
Every economic parameter of a trade needs to be fixed at the moment the seller signs or lists. If a fee, royalty, or commission is read from mutable storage at settlement instead, whoever controls that storage can change the outcome of a sale that is already agreed. The Ternoa auction vulnerability below is exactly this bug: a marketplace owner could raise the commission to 100% after a listing went live.
Settlement code also breaks in quieter ways: fees and royalties that round in the attacker's favor, partial fills and bundles that pay out more than they collect, and loops that count the same item twice, as the Pandora harvest vulnerability shows.
How to prevent it:
- Sign orders with EIP-712, including chain ID, contract address, a nonce, an expiry, and every price, fee, and royalty value.
- Support on-chain cancellation and bulk nonce increments, so users can invalidate all old orders in one transaction.
- At execution, check that the signer still owns the token and that the order hasn't been filled or cancelled.
- Fuzz settlement logic with Foundry using repeated IDs, zero prices, maximum fees, and rounding edge cases.
Platform and Web Vulnerabilities
An NFT marketplace is also a web application that accepts files from strangers and shows them to everyone else. That makes it vulnerable to classic web attacks, with an NFT-specific twist.
Malicious NFT Media
Anyone can mint an NFT, and the marketplace then renders that NFT's image for every visitor. If the image is an SVG with embedded JavaScript and the marketplace serves it inline from its own domain, the script runs in each visitor's session. This is stored cross-site scripting delivered through a token.
HackenProof's team has reproduced this attack against a live marketplace. They minted an NFT from an SVG file carrying a blind-XSS payload. The site's WAF blocked the first attempt, but the marketplace's origin server was reachable directly, and its IP turned up in internet-scan data, so the payload went straight to the origin. Once the NFT was listed, the marketplace rendered the SVG on its pages, and visitors' session cookies were sent to the attacker's collector. Stolen sessions on a marketplace mean actions taken in users' names, which bug bounty programs typically rate High or Critical.
IPFS adds a related risk. Gateways can serve HTML and SVG content as-is, so a marketplace that embeds gateway content on its own origin inherits whatever scripts it contains. Cloud storage such as S3 is usually safer by default, but only if it is configured to serve static files with the right headers.
How to prevent it:
- Sanitize SVG uploads by stripping scripts, event handlers, and
foreignObject, or rasterize them to PNG before display. - Serve all user and NFT media from a separate, cookieless domain, and never inline gateway HTML on the main site.
- Send
Content-Security-Policy, correctContent-Type, andX-Content-Type-Options: nosniffheaders on media responses. - Mark session cookies
HttpOnly,Secure, andSameSite, so a script that does run cannot read them. - Accept traffic at the origin only from your CDN's IP ranges, so the WAF cannot be bypassed.
API, Indexer and Upload Flaws
The marketplace API is often the softest target, because it is built for the frontend and trusted by it:
- Broken object-level authorization: endpoints that edit profiles, listings or collection settings without checking that the caller owns them.
- Order data exposure: APIs that keep serving cancelled or inactive orders, which is how stale listings get found and filled.
- Indexer trust: collection names, images, and "verified" badges pulled from metadata without checks, letting copycat collections pass as the real one.
- File uploads: missing type, size, and content checks, uploaded files served from predictable paths, and error messages that disclose server paths.
- Missing rate limits on minting, bidding, and authentication endpoints.
Classic Web Vulnerabilities
The usual web risks still apply: cross-site scripting outside media, CSRF, SQL injection, insecure deserialization, misconfigured cloud storage, and unpatched dependencies. Many of these web2 bugs hide inside web3 products, and a contract audit won't find them. That is the job of regular penetration testing of the web app, API, and infrastructure.
User-Side Attacks: Phishing, Drainers, and Compromised Channels
A large share of NFT theft never touches a vulnerable contract. The user signs something they shouldn't, and the contracts do exactly what the signature allows. Users are responsible for their keys, but the marketplace decides how clearly risky actions are shown and how easy its brand is to impersonate.
Signature Phishing and Wallet Drainers
Drainer sites copy a real marketplace or mint page and ask the user to sign one of a few messages:
setApprovalForAll, which hands a whole collection to the attacker's address- An off-chain listing or offer that sells the user's NFTs for zero or near-zero, which the attacker then fills
- A token permit that authorizes spending without an on-chain approval transaction
Off-chain signatures cost no gas, so many users assume signing them is harmless. Marketplaces can push back by showing EIP-712 messages in plain language, warning before collection-wide approvals and zero-price listings, simulating what a transaction will do before the wallet prompt, and publishing one official domain that users can check.
Holders who follow basic crypto wallet security habits, such as keeping valuable NFTs in a different wallet from the one they mint and trade with, are much harder to drain.
Counterfeit Collections
Copycat collections reuse a popular project's name and artwork under a different contract. Show the contract address on every collection and item page, verify collections against the creator's official channels, and flag listings whose metadata closely matches a verified collection.
Compromised Community Channels
Attackers also go after the channels a project uses to talk to its holders. On June 4, 2022, the Discord account of a Yuga Labs community manager was compromised and used to post phishing links in the official Bored Ape Yacht Club and Otherside Discord servers. Yuga Labs estimated that about 200 ETH worth of NFTs was taken, and on-chain observers traced roughly 145 ETH to the attacker's wallets. Because the announcement came from an official server, holders had little reason to doubt it.
How to prevent it:
- Treat community channels as part of the attack surface, with the same access reviews as production systems.
- Require 2FA, ideally hardware keys, for every admin and moderator, and keep the number of people with admin rights small.
- Give bots and webhooks only the permissions they need, and audit which integrations can post to announcement channels.
- Publish a single official domain for mints and claims, and state publicly that the team never announces surprise mints or sends links by DM.
- Keep a playbook for compromise: lock announcement channels, revoke tokens and sessions, and warn users on every other channel within minutes.
Real NFT Vulnerabilities Found Through HackenProof Bug Bounties
Both bugs below were reported by independent researchers through bug bounty programs on HackenProof, validated, and fixed before any user lost funds. Neither was a classic contract bug: each broke the economic logic of a sale or payout.
| Case | Component | Root cause | Outcome |
|---|---|---|---|
Ternoa | Auction and marketplace pallet | Commission fee not locked at listing | Any listed NFT could be taken without paying the seller |
Pandora |
| State updated once after a loop instead of per item | Interest pool could be drained with one NFT |
Ternoa Auction Vulnerability
The platform. Ternoa is a blockchain built for secure data transmission. NFTs on Ternoa can carry encrypted data, with keys controlled on-chain and data stored off-chain with decentralized storage providers. NFT sales run through pallets, modules in the chain's own runtime, rather than through individual smart contracts. That makes a marketplace bug here a matter of layer 1 protocol security: a flaw in the pallet affects every marketplace built on the chain.
How it was found. In January 2024, security researcher @rapt00r reported the issue through Ternoa's bug bounty program on HackenProof.
The vulnerability. Ternoa lets anyone run a marketplace and set its commission. When an NFT was listed, the pallet did not store the marketplace's commission_fee with the listing. Instead, it read the current fee again during final payment, where it deducts the commission from the seller's proceeds and calculates the creator's royalty. Because the marketplace owner could change that fee at any time, they could set it equal to the full sale price after a listing went live.
The auction record created at listing stores no commission fee:
let auction_data = AuctionData {
creator: who.clone(),
start_block,
end_block,
start_price,
buy_it_price,
bidders,
marketplace_id,
is_extended: false,
};
Auctions::<T>::insert(nft_id, auction_data);How it could be exploited.
- The attacker creates a marketplace and attracts sellers with very low fees.
- A user lists a valuable NFT on it with a "buy now" price of 100,000 tokens.
- The attacker changes the marketplace's commission to 100,000 tokens, the full price.
- The attacker buys the NFT. The whole payment is routed back to them as commission.
- The attacker now holds the NFT, and the seller receives nothing.
The fix. Ternoa changed the marketplace pallet to capture the commission fee in the sale object at the moment of listing. Later changes to a marketplace's fee no longer affect existing listings. The fix was deployed to mainnet.
Impact. Ternoa confirmed that no user on any mainnet marketplace lost funds. The attack also carried a real cost: the attacker had to build a marketplace, attract valuable listings, and burn its reputation in a single use. The researcher was paid a bounty for the report.
Lesson for marketplace builders: bind every economic parameter of a sale, including price, commission, and royalties, to the listing itself. Anything read from mutable storage at settlement time can be changed by whoever controls that storage.
Pandora Harvest Vulnerability
The platform. Pandora is a gamified decentralized exchange on BNB Chain that combines an AMM, GameFi, and income-generating NFTs. One of its products, NFT Securities, pays holders a fixed interest that they claim periodically, which brings the payout risks familiar from DeFi security into an NFT product.
How it was found. An anonymous researcher reported the issue through Pandora's bug bounty program on HackenProof. The report was validated and fixed, and no user funds were affected.
The vulnerability. Holders claimed interest by calling harvest on the BondRouter contract with a list of their NFT Security IDs. The internal executeHarvest function looped over that list, calculated the interest owed for each ID, and added it to a running total. It recorded the claim by calling _updateLastHarvest only once, after the loop had finished. Nothing stopped the same ID from appearing in the list many times, and each repeat was paid in full. An attacker holding one valid NFT Security could drain the entire pool used to pay fixed interest to all holders.
How it could be exploited.
- Mint a BondNFT through Pandora's NFT Securities product.
- Wait until interest becomes claimable.
- Call
BondRouter.harvestwith a craftedClaimInfoobject containing a validbatchIdand the attacker's own NFT ID repeated many times, for example[1, 1, 1, 1, 1, 1, 1, 1]. - Receive interest multiplied by the number of repeats from a single NFT.
The researcher's Foundry test passes the same NFT Security ID four times in one claim:
function testHarvestMoreTokens() public {
console.log("[+] Sending the Transaction with the same token ID multiple times [+]");
console.log("\tAttacker Balance Before the exploit: ", token.balanceOf(attacker));
BondStruct.ClaimInfo[] memory claimInfo = new BondStruct.ClaimInfo[](1);
uint256[] memory nftIDs = new uint256[](4);
nftIDs[0] = 1;
nftIDs[1] = 1;
nftIDs[2] = 1;
nftIDs[3] = 1;
BondStruct.ClaimInfo memory claim = BondStruct.ClaimInfo({
batchId: 1,
ids: nftIDs
});
claimInfo[0] = claim;
vm.warp(500);
vm.prank(attacker);
router.harvest(claimInfo, attacker);
console.log("\tAttacker Balance After the exploit: ", token.balanceOf(attacker));
}The fix. Pandora moved the _updateLastHarvest call inside the loop, so each ID's last-harvest time is updated as soon as its interest is calculated. A repeated ID now earns nothing after its first appearance.
Before the fix, _updateLastHarvest runs once, after interest for every ID has been added up:
function _executeHarvest(uint256 _batchId, uint256[] memory _ids, address _to) internal {
require(_batchId > 0 && _batchId <= batchId, "BondRouter: !_batchId");
// calculate amount interest
BatchConfig memory _info = batchInfo[_batchId].config;
require(block.timestamp >= _info.startTime, "BondRouter: can not harvest now");
uint256 _amountSent = 0;
for (uint256 i = 0; i < _ids.length; i++) {
(uint256 _interestAmount,,) = _calAmount(_ids[i], _batchId, _info.startTime);
_amountSent += _interestAmount;
}
_payment(_info.currency, msg.sender, _amountSent);
_updateLastHarvest(bondNFTAddress, _ids); // state updated only after the loop
emit Harvest(msg.sender, _ids, _to, _amountSent, _batchId);
}After the fix, each ID's last-harvest time is updated inside the loop, so a repeated ID earns nothing after its first appearance:
function _executeHarvest(uint256 _batchId, uint256[] memory _ids, address _to) internal {
require(_batchId > 0 && _batchId <= batchId, "BondRouter: !_batchId");
// calculate amount interest
BatchConfig memory _info = batchInfo[_batchId].config;
require(block.timestamp >= _info.startTime, "BondRouter: can not harvest now");
uint256 _amountSent = 0;
for (uint256 i = 0; i < _ids.length; i++) {
(uint256 _interestAmount,,) = _calAmount(_ids[i], _batchId, _info.startTime);
_amountSent += _interestAmount;
_updateLastHarvest(_ids[i]); // state updated on every iteration
}
_payment(_info.currency, msg.sender, _amountSent);
emit Harvest(msg.sender, _ids, _to, _amountSent, _batchId);
}Lesson for marketplace builders: update state for each item before or as you pay for it, not after a batch. Reject duplicate IDs in input arrays, and include repeated and out-of-order IDs in your fuzzing and audit test cases.
Both bugs passed normal development and testing and were caught by outside researchers who looked at the business logic the way an attacker would. A bug bounty program keeps that kind of review running after launch.
How to Build a Secure NFT Marketplace
Security for an NFT marketplace works in layers: the contracts, the signing flow, the web app, the community, and the response when something slips through. This checklist covers each layer.
- Audit smart contracts before launch and after every change. Have the marketplace, collection, and payout contracts reviewed by experienced auditors, including the business logic around fees, royalties, and settlement, not just known vulnerability patterns. Re-audit every upgrade against the same smart contract audit methodology, and keep the contract that holds user approvals as small as possible.
- Bind every listing parameter to the signature. Sign orders with EIP-712 and include price, fees, royalties, nonce, expiry, chain ID, and contract address. Support on-chain cancellation and bulk invalidation of old orders, and check ownership again at execution.
- Implement secure wallet sign-in and account protection. On an NFT marketplace, the wallet is the user's identity, so sign-in security starts with how signatures are requested. Use Sign-In with Ethereum (EIP-4361) with a one-time nonce, your domain, and an expiry in the message, so a captured signature can't be replayed elsewhere. Keep sessions short-lived, stored in HttpOnly cookies, and tied to the signing address. Show users readable signing prompts rather than raw hex, and add explicit warnings before approvals such as
setApprovalForAll, which hands control of an entire collection to another address. For accounts that also use email or social login, require two-factor authentication on profile changes and payouts. - Isolate and sanitize user media. Sanitize or rasterize SVGs, serve all NFT media from a separate cookieless domain, and set strict content headers on every media response.
- Harden the web app, API, and infrastructure. Enforce HTTPS everywhere, check authorization on every object-level endpoint, rate-limit minting, bidding, and login, keep dependencies patched, and accept origin traffic only from your CDN. Run penetration tests before launch and after major releases, and plan the penetration testing cost into both milestones.
- Use trusted wallet and payment integrations. Integrate established wallet connectors and, for fiat purchases, payment providers that handle card data themselves, so your platform never stores it.
- Secure your community channels. Require hardware-key 2FA for admins and moderators, limit bot permissions, publish one official domain, and tell users you never send links by DM or announce surprise mints.
- Monitor and be ready to respond. Watch contracts for unusual withdrawals, fee or admin changes, and spikes in failed transactions. Flag stolen assets and wash trading. Build a pause mechanism into contracts where the design allows it, and keep an incident runbook with named owners.
- Educate users. Explain in plain language what each signature does, how to revoke approvals, and how to recognize the official domain and channels.
- Run a bug bounty program. Audits and pentests are snapshots in time; a bug bounty program keeps independent researchers testing the live product. Both cases in this guide were found that way. Scope and reward levels drive most of the cost of a bug bounty program.
Which Security Activity Catches What
Each method covers different ground, and the trade-offs between penetration testing and bug bounty explain why mature teams run both alongside audits.
| Risk | Smart contract audit | Penetration test | Bug bounty |
|---|---|---|---|
Reentrancy, access control, arithmetic | Primary | No | Ongoing |
Signature and listing logic | Primary | Partial | Ongoing |
Fee and settlement logic | Primary | No | Ongoing |
Malicious media and XSS | No | Primary | Ongoing |
API authorization and data exposure | No | Primary | Ongoing |
Infrastructure and WAF bypass | No | Primary | Ongoing |
Bugs introduced after launch | Only if re-audited | Only if retested | Continuous |
Conclusion
The NFT incidents that cost users the most came from logic users and developers took for granted: listings that never really expired, fees that could move after a sale, images that ran code, and announcements that came from a hijacked account. Securing an NFT project or marketplace means testing each of those layers before launch and keeping them under review afterwards.
Frequently Asked Questions
What is NFT security?
NFT security is the protection of everything an NFT project or marketplace relies on to mint and trade tokens safely: smart contracts, signed listings and offers, the web app and API, metadata and media storage, user wallets and community channels. A weakness in any of these layers can let attackers take NFTs or payments from users.
What are the most common NFT marketplace vulnerabilities?
The most damaging are flaws in signature and listing logic (replayable or stale orders), fee and settlement bugs, broken access control in contracts, reentrancy through transfer callbacks, stored XSS through NFT media, and signature phishing that tricks users into approving transfers.
How do attackers steal NFTs without hacking the smart contract?
Mostly through signatures. A user signs an approval or a near-zero listing on a phishing site, or an old listing they believed was cancelled stays valid on-chain, and the contract executes the trade exactly as signed. Compromised Discord and social accounts are a common way to deliver the phishing link.
Is a smart contract audit enough to secure an NFT marketplace?
No. An audit covers contract code at one point in time. The web app, API, media handling, and infrastructure need penetration testing, and code shipped after the audit needs ongoing review, which is what a bug bounty program provides.
Is integer overflow still a risk in NFT smart contracts?
Only in specific places. Since Solidity 0.8.0, arithmetic reverts on overflow by default. The risk remains inside unchecked blocks, in contracts compiled with older versions, and in explicit downcasts to smaller integer types, which truncate without reverting.
How does a bug bounty program help an NFT marketplace?
A bug bounty program gives an NFT project continuous testing by independent researchers who are paid only for valid findings. The Ternoa and Pandora vulnerabilities described in this guide were both found this way and fixed before any user lost funds.
More topics:





