Skip to main content
Xcapit
Blog
·12 min read·Fernando BoieroFernando Boiero·CTO & Co-Founder

Smart Contract Security: 10 Common Vulnerabilities and How to Prevent Them

blockchaincybersecuritysmart-contracts

Since the DAO hack of 2016, smart contract vulnerabilities have cost the blockchain industry over $8 billion in stolen or frozen funds. The immutable nature of blockchain means that once a flawed contract is deployed, it cannot be patched — the code is law, bugs and all. Every vulnerability that makes it to mainnet is a potential exploit waiting to happen, and attackers are sophisticated, well-funded, and relentless.

Smart contract vulnerability landscape map
Common smart contract vulnerabilities: from reentrancy attacks to oracle manipulation

Understanding the most common vulnerability patterns is the first line of defense. This guide covers ten critical smart contract vulnerabilities, explains how each one works, and provides concrete prevention strategies that development teams should implement before any code reaches production.

1. Reentrancy Attacks

Reentrancy is the most infamous smart contract vulnerability, responsible for the original DAO hack that led to the Ethereum hard fork. It occurs when a contract makes an external call to another contract before updating its own state. The called contract can then call back into the original function before the first execution completes, creating a recursive loop that drains funds beyond what should be allowed.

The classic example is a withdrawal function that sends ETH to a user before setting their balance to zero. An attacker deploys a contract with a fallback function that calls withdraw again when it receives ETH. The result is that the attacker can withdraw their balance multiple times before the contract ever records the deduction.

Modern reentrancy attacks have evolved beyond the single-function pattern. Cross-function reentrancy exploits shared state between different functions in the same contract. Cross-contract reentrancy targets state shared across multiple contracts in a protocol. Read-only reentrancy manipulates view functions that external contracts rely on for pricing or collateral calculations.

  • Follow the checks-effects-interactions pattern: validate conditions, update state, then make external calls — never the reverse
  • Use reentrancy guards (mutex locks) on all state-changing functions that make external calls
  • Audit for cross-function and cross-contract reentrancy, not just single-function patterns
  • Consider read-only reentrancy vectors if your contract exposes view functions used by other protocols
  • Use pull-over-push payment patterns where users withdraw funds rather than the contract sending them

2. Integer Overflow and Underflow

Integer overflow occurs when an arithmetic operation produces a value larger than the maximum a variable type can hold, causing it to wrap around to zero or a small number. Integer underflow is the reverse — subtracting from zero wraps to the maximum value. In financial contracts, this can mean a user with zero tokens suddenly has billions, or a massive balance becomes negligible.

Before Solidity 0.8.0, arithmetic operations did not check for overflow or underflow by default, making this one of the most common vulnerability classes. The BEC token exploit in 2018 used an integer overflow to generate billions of tokens from nothing, crashing the token's value to zero.

While Solidity 0.8+ includes built-in overflow checks, the danger has not disappeared. The unchecked block explicitly disables these protections for gas optimization. Additionally, type casting between different integer sizes (uint256 to uint128, for example) can silently truncate values. And protocols written in other languages like Vyper or Rust have their own overflow behaviors.

  • Use Solidity 0.8.0 or later to benefit from built-in overflow and underflow checks
  • Audit every use of the unchecked keyword to confirm overflow is truly impossible in that context
  • Be careful with type casting — explicitly validate that values fit in the target type before casting
  • Use SafeMath or equivalent libraries for contracts that must support older Solidity versions
  • Test boundary conditions: what happens at zero, at maximum values, and at type boundaries

3. Front-Running and MEV Attacks

Front-running occurs when an attacker sees a pending transaction in the mempool and submits their own transaction with a higher gas price to execute first. On public blockchains, all pending transactions are visible before they are included in a block, creating an information asymmetry that sophisticated actors exploit for profit. This is part of the broader Maximal Extractable Value (MEV) phenomenon.

The most common front-running pattern in DeFi is the sandwich attack. An attacker sees a large swap pending on a decentralized exchange, buys the target token first (pushing the price up), lets the victim's transaction execute at the inflated price, then sells immediately after for a profit. The victim gets fewer tokens than expected, and the attacker pockets the difference.

Front-running also affects token launches, NFT mints, liquidations, oracle updates, and governance votes. Any transaction where the outcome depends on execution order is potentially vulnerable.

  • Implement slippage protection with user-defined maximum acceptable price impact on all swap functions
  • Use commit-reveal schemes for operations where transaction content should be hidden until execution
  • Consider private transaction submission via Flashbots Protect or similar MEV protection services
  • Design mechanisms that are order-independent where possible — batch auctions instead of first-come-first-served
  • Set reasonable deadlines on transactions to prevent them from being held and executed at disadvantageous times
  • For governance, use time-locked proposals with snapshot voting to prevent flash-loan vote manipulation

4. Oracle Manipulation

Oracles provide external data to smart contracts — most commonly price feeds for DeFi protocols. If an attacker can manipulate the price data that a contract relies on, they can trick the protocol into making decisions based on false information. This is one of the most financially devastating attack vectors, responsible for hundreds of millions in losses.

The most common oracle manipulation technique uses flash loans to temporarily distort on-chain prices. An attacker borrows a massive amount of tokens, uses them to move the price on a DEX, triggers a vulnerable protocol that reads that DEX price, then profits from the mispriced operation — all in a single transaction. Because flash loans require no collateral, the attacker risks nothing.

Even well-known oracle providers are not immune. Chainlink feeds can have stale data if update conditions are not met. Uniswap TWAP oracles can be manipulated with sustained capital over multiple blocks. Custom oracle implementations often have centralization risks or update delays that create exploitation windows.

  • Use time-weighted average prices (TWAP) instead of spot prices to resist single-block manipulation
  • Implement price deviation checks that reject updates beyond a reasonable threshold
  • Use multiple independent oracle sources with a median or consensus mechanism
  • Add freshness checks — reject price data older than a defined threshold
  • Implement circuit breakers that pause operations during extreme market volatility
  • Never use a single DEX pool as your sole price source for critical financial operations

5. Access Control Flaws

Access control vulnerabilities occur when critical functions lack proper permission checks, allowing unauthorized users to execute privileged operations. This includes unprotected initialization functions, missing role checks on admin operations, and improperly implemented ownership transfers. The Parity multi-sig wallet hack, which froze over $150 million in ETH, was caused by an unprotected initialization function that anyone could call.

The danger is compounded by the public nature of blockchain. Every function in a contract is visible and callable by anyone unless explicitly restricted. Unlike traditional software where the server controls access, smart contracts must enforce their own permissions entirely through code. There is no firewall, no network segmentation, and no server-side validation — only what the contract itself checks.

Proxy patterns introduce additional access control complexity. The proxy admin, the implementation owner, and the upgrade authority may be different roles, and confusion between them creates vulnerabilities. Transparent proxy patterns help by separating admin calls from user calls, but misconfiguration remains a common source of bugs.

  • Use established libraries like OpenZeppelin's AccessControl or Ownable for permission management
  • Implement the principle of least privilege — each role should have only the permissions it needs
  • Use two-step ownership transfers (propose and accept) to prevent accidental transfers to wrong addresses
  • Add time-locks to critical parameter changes so the community can review and react before changes take effect
  • Require multi-signature approval for admin operations — never use a single EOA for protocol governance
  • Audit proxy patterns carefully, especially the relationship between proxy admin and implementation owner

6. Flash Loan Attacks

Flash loans allow anyone to borrow an unlimited amount of tokens with zero collateral, as long as the loan is repaid within the same transaction. While they are a legitimate DeFi innovation for arbitrage and refinancing, they have become the primary funding mechanism for sophisticated exploits. Flash loans effectively give every attacker the capital of a whale, removing the financial barrier to executing market manipulation attacks.

A typical flash loan attack combines multiple vulnerabilities in a single atomic transaction. The attacker borrows millions of dollars worth of tokens, uses them to manipulate a price oracle, exploits a protocol that relies on that oracle, extracts profit, repays the loan with interest, and keeps the difference. If any step fails, the entire transaction reverts and the attacker loses only the gas fee.

Flash loan attacks are particularly dangerous because they are risk-free for the attacker and can be executed by anyone with the technical knowledge to construct the transaction. The 2023 Euler Finance exploit used a flash loan to manipulate collateral pricing, resulting in a $197 million loss — the largest flash loan attack to date.

  • Design protocols to be flash-loan-resistant by assuming any user could have unlimited capital in a single transaction
  • Use TWAP oracles and multi-block price averages that cannot be manipulated within a single transaction
  • Implement minimum lock periods for deposits before they can be used as collateral or voting power
  • Add same-block operation restrictions — prevent deposit and borrow in the same transaction where appropriate
  • Stress-test your protocol with flash loan simulation tools to identify potential attack vectors
  • Consider whether your protocol's invariants hold when a user has unlimited temporary capital

7. Denial of Service (DoS)

Denial of service in smart contracts occurs when an attacker can prevent legitimate users from interacting with a contract. Unlike traditional web DoS attacks that overwhelm servers with traffic, smart contract DoS exploits logic flaws that permanently or temporarily block critical functions.

The most common pattern is the unbounded loop DoS. If a contract iterates over an array that grows without limit — such as a list of token holders for dividend distribution — an attacker can add enough entries to make the gas cost of iteration exceed the block gas limit. The function becomes permanently uncallable, and any funds locked behind it become inaccessible.

Another common vector is the unexpected revert DoS. If a contract sends ETH to a list of addresses and one of those addresses is a contract that reverts on receive, the entire batch operation fails. An attacker can exploit this to block auction settlements, governance vote tallying, or any operation that must process a list of addresses.

  • Avoid unbounded loops — use pagination patterns or set hard limits on array sizes
  • Prefer pull-over-push payment patterns: let users withdraw rather than pushing payments to them
  • Do not make critical operations depend on external calls succeeding — handle failures gracefully
  • Use gas-limited external calls (call with stipend) to prevent called contracts from consuming all gas
  • Implement emergency recovery mechanisms that can bypass blocked functions when needed
  • Test with adversarial scenarios: what happens if one participant acts maliciously?

8. Logic Bugs in Business Logic

Logic bugs are vulnerabilities where the code does exactly what it was written to do, but what it was written to do is not what the developers intended. These are the hardest bugs to catch because automated tools look for known vulnerability patterns, while logic bugs are unique to each protocol's specific business rules.

Common examples include incorrect fee calculations that allow users to avoid paying fees under specific conditions, reward distribution formulas that can be gamed by depositing and withdrawing at strategic times, and liquidation mechanisms that fail to account for edge cases in collateral ratios. The Compound Finance governance incident, where a bug in the reward distribution logic caused $90 million in tokens to be distributed incorrectly, was a pure logic bug — the code executed flawlessly, but the formula was wrong.

Business logic vulnerabilities are amplified by composability. When your protocol interacts with other protocols, the combined behavior may produce outcomes that neither protocol anticipated. A lending protocol and a yield aggregator might each be secure individually, but their interaction creates an exploitable feedback loop.

  • Write comprehensive specifications before coding — document every formula, every edge case, and every assumption
  • Implement property-based testing that verifies invariants hold across thousands of random scenarios
  • Use formal verification for critical mathematical properties like token conservation and solvency
  • Test with realistic economic scenarios, not just unit tests — simulate market stress conditions
  • Have domain experts (not just security researchers) review the business logic against the specification
  • Document all protocol assumptions explicitly and test what happens when those assumptions are violated

9. Signature Replay Attacks

Signature replay occurs when a valid signed message can be reused in a context where it should not be accepted. If a contract accepts a signed authorization to transfer tokens but does not track which signatures have been used, the same signature can be submitted multiple times to drain the user's entire balance. Replaying signatures across chains is another variant — a signature valid on Ethereum can be replayed on Polygon or BSC if the contract does not include chain-specific data in the signed message.

This vulnerability appears frequently in meta-transaction systems, gasless transactions, off-chain order books, and any system that uses EIP-712 typed data signing. The permit function (EIP-2612) is a common implementation that allows token approvals via signatures instead of on-chain transactions — and is a common target for replay attacks when implemented incorrectly.

The risk is heightened after chain forks. When Ethereum transitioned to proof-of-stake and the proof-of-work fork chain continued as ETHW, signatures created on one chain were valid on both. Protocols that did not include chain ID in their signed data were vulnerable to cross-chain replay.

  • Include a nonce in every signed message and track used nonces on-chain to prevent reuse
  • Include the chain ID (EIP-155) in all signed data to prevent cross-chain replay
  • Include the contract address in the signed data to prevent replay across different contract instances
  • Follow EIP-712 for typed structured data signing — it includes domain separators that prevent most replay vectors
  • Implement signature expiration with deadlines to limit the window of potential replay
  • After chain forks, audit all signature-based functions for cross-chain replay vulnerability

10. Uninitialized Storage and Proxy Pitfalls

Uninitialized storage vulnerabilities occur when contract variables are not properly set during deployment or initialization, leaving them with default values (zero for integers, empty for addresses) that create exploitable conditions. This is especially dangerous with proxy patterns, where the implementation contract's constructor is never called — the proxy calls an initializer function instead, and if that function can be called by anyone, an attacker can take ownership of the contract.

The most high-profile uninitialized proxy attack occurred against Wormhole's implementation contract in 2022. The team had left the implementation contract uninitialized, allowing an attacker to call the initialize function, take ownership, and upgrade the contract to a malicious version — resulting in a $320 million loss.

Storage collision is a related risk in proxy patterns. If the proxy and implementation contracts use the same storage slots for different variables, writing to one corrupts the other. The EIP-1967 standard defines specific storage slots for proxy admin and implementation addresses to avoid this, but custom proxy implementations often get it wrong.

  • Always call the initializer in the same transaction as proxy deployment to prevent front-running
  • Use the initializer modifier from OpenZeppelin to ensure initialization functions can only be called once
  • Disable initializers in the implementation contract's constructor to prevent direct initialization attacks
  • Follow EIP-1967 storage slot conventions for proxy patterns to avoid storage collisions
  • Verify that all state variables have sensible initial values — never assume the default zero value is safe
  • Audit upgrade paths carefully: ensure that new implementation versions do not introduce storage layout conflicts

Building a Security-First Development Process

Preventing vulnerabilities is not just about knowing the patterns — it requires a development culture and process that makes security a first-class concern at every stage.

  • Write a detailed specification before writing code, including all edge cases, economic assumptions, and failure modes
  • Use established, audited libraries like OpenZeppelin instead of implementing standard patterns from scratch
  • Implement comprehensive test suites: unit tests for individual functions, integration tests for cross-contract interactions, and fuzz tests for boundary conditions
  • Run automated security analysis tools (Slither, Mythril, Echidna) as part of your CI/CD pipeline — not just before audits
  • Conduct internal security reviews with a checklist covering all ten vulnerability categories in this guide
  • Use formal verification for critical mathematical invariants like token conservation and collateral ratios
  • Deploy to testnets and run extended testing periods with monitoring before mainnet deployment
  • Implement upgradeability or circuit breakers that allow you to respond to discovered vulnerabilities
  • Establish a bug bounty program with meaningful rewards proportional to the value your contracts protect
  • Monitor your contracts post-deployment with automated alerting for unusual transaction patterns

When to Get a Professional Audit

A professional security audit should be considered mandatory for any contract that will handle significant value. But not all audits are equal, and timing matters. Auditing too early — before the code is stable — wastes money as findings become outdated with each change. Auditing too late — just before launch — leaves no time to properly address findings.

The ideal timing is after the codebase is feature-complete, after internal reviews and automated analysis have been conducted, and with enough buffer before launch to address findings and get a re-review. Plan for at least 4-6 weeks for the initial audit and 2-3 weeks for re-review of fixes.

When evaluating audit firms, look for teams with specific experience in your protocol's domain — DeFi lending, AMMs, bridges, NFT marketplaces, and governance systems each have unique vulnerability patterns. The best audit firms combine automated tooling, manual code review by multiple independent auditors, and economic attack modeling.

Consider engaging multiple audit firms for high-value protocols. Different auditors have different strengths and blind spots, and a critical vulnerability found by the second auditor that the first missed can save millions.

Smart contract security is not a one-time activity — it is an ongoing discipline that spans the entire development lifecycle. The ten vulnerabilities described in this guide account for the majority of funds lost in blockchain exploits, and each one is preventable with proper design, testing, and review.

Smart Contract Vulnerability Layers

At Xcapit, our cybersecurity team combines deep smart contract expertise with ISO 27001 certification and years of production blockchain experience. From security audits and penetration testing to building security-first development processes, we help blockchain projects protect their users and their reputation. Learn more about our cybersecurity services.

Share
Fernando Boiero

Fernando Boiero

CTO & Co-Founder

Over 20 years in the tech industry. Founder and director of Blockchain Lab, university professor, and certified PMP. Expert and thought leader in cybersecurity, blockchain, and artificial intelligence.

Let's build something great

AI, blockchain & custom software — tailored for your business.

Get in touch

Building on blockchain?

Tokenization, smart contracts, DeFi — we've shipped it all.

Related Articles

·10 min

Building DevSecOps Pipelines for Blockchain Projects

How to design and implement a DevSecOps pipeline purpose-built for blockchain development — covering smart contract static analysis, automated audit pipelines, secrets management, deployment automation, and post-deployment monitoring.