DApps - Decentralized Applications

{{#include ../banners/hacktricks-training.md}}

What is a DApp?

A DApp is a decentralized application that runs on a peer-to-peer network, rather than being hosted on a centralized server. DApps are typically built on blockchain technology, which should allow for transparency, security, and immutability of data.

Web3 DApp Architecture

According to this post there are 3 different types of Web3 DApps architecture:

"API-less" DApps

These DApps are built on top of a blockchain and do not rely on any centralized APIs or backend. You could think that the blockchain is the actual backend of the application. They are fully decentralized and can be accessed directly through the blockchain.

In order to interact with the blockchain the client will usually use a wallet. The wallet will sign the transactions and send them to the blockchain. The client might also use a node to read data from the blockchain.

"API-Enabled" DApps

These DApps are built on top of a blockchain but also rely on centralized APIs usually to gather information. They are mostly decentralized because even if they rely on a centralized API, the core functionality of the DApp is still on the blockchain. The communication of the client with the blockchain is usually done through a wallet.

A good example of this type of DApp is a NFT minting application. The server allows images to be uploaded but the minting is done by the client through a wallet.

"Full-Scale" DApps

These DApps are built on top of a blockchain but also rely on centralized APIs and backend servers. They could be partially decentralized as the client might be able to perform operations on the blockchain using a wallet. However, usually the backend will also be able to perform operations on the blockchain.

A good example for this type of DApp is a cross-chain bridge where an offchain component is needed to communicate with both smart contracts in different blockchains to perform the transfer of assets.

Web2 vulnerabilities

Web2 vulnerabilities still affect these kinds of applications although their impact might vary:

  • Client side vulnerabilities have an increased impact as in the Web3 DApps the client is usually the one performing the operations on the blockchain through a wallet. This means that attacks like XSS that manage to execute JS code on the client side or that tampers with the content of the page can have a bigger impact as they can interact with the wallet and convince the user to perform undesired operations on the blockchain.
  • Note that usually even in these kinds of applications the client can still review the operations before signing them with the wallet. However, if the attacker is able to tamper with the content of the page, it can convince the user to sign a transaction that will perform an undesired operation on the blockchain.
  • Server side vulnerabilities are still present in the DApps that rely on a backend server. The impact of these vulnerabilities will depend on the architecture of the DApp. However, they could still be very problematic as an attacker might find in the backend keys of the company to access the funds of smart contracts, or could perform account takeover that might allow them to steal funds or NFTs from the users.
  • Supply chain compromises in frontend dependencies, wallet SDKs, analytics snippets, or CDN-hosted assets can be catastrophic in Web3. Tampering with a signing modal, a wallet connector, or transaction-building helper can silently replace calldata, destination addresses, spender addresses, or the operation type right before the wallet prompt appears.

Of course, if the DApp is not using a backend or the backend used only offers public chain data or static pages, the attack surface of the DApp is reduced.

Web3 attack surface

Even if in general a DApp has a reduced attack surface as several security checks are always done on the blockchain, there are still some attack vectors that can be exploited by an attacker.

It might be possible to group web3 DApps vulnerabilities in the following categories:

  • Mishandled On-Chain Transactions: incorrectly formatted or unrestricted transaction APIs, lack of response-waiting and block-confirmation logic, exposure of sensitive data, and improper handling of failed, reverted, or internally-typed transactions that allow malicious calldata injections.

  • Smart-Contract-Driven Backend Attacks: storing or syncing sensitive data between contracts and databases without validation, unchecked event emissions or contract addresses, and exploitable contract vulnerabilities that can poison backend logic.

  • Flawed Crypto-Asset Operations: misprocessing different token types (native vs. ERC-20), ignoring decimal precision, failed transfers or internal transactions, and accepting fake, deflationary, rebase, or slippage-prone tokens without validation, enabling payload injections via token metadata.

  • Wallet / Provider Trust Boundary Failures: trusting window.ethereum, WalletConnect session metadata, chain-switch prompts, or injected provider discovery without validation. A malicious extension, a spoofed wallet object, or a compromised dependency can alter provider methods, transaction parameters, icons, or wallet identity and route the user through an attacker-controlled signing flow.

  • Signature-Based Authorization Abuse: treating eth_sign, personal_sign, eth_signTypedData_v4, permit, or similar off-chain authorizations as "harmless". In practice, these signatures can grant allowances, approve relayers, authorize gasless actions, or satisfy backend workflows without the user ever seeing an on-chain approval transaction.

  • RPC / Indexer / Finality Assumptions: assuming that the latest log, receipt, or backend index is final. DApps that credit balances, mint rewards, unlock bridge assets, or update off-chain state from unconfirmed events can be abused during reorgs, inconsistent RPC views, or by poisoning custom indexer pipelines.

  • Bundler / Relayer / Paymaster Abuse: sponsored transactions and account abstraction move part of the trust boundary off-chain. Any API that accepts arbitrary UserOperation, pays gas on behalf of users, or relays signatures for execution becomes a target for gas-drain, policy-bypass, and replay-style abuse.

Frontend, provider and wallet discovery abuse

Modern DApps often assume that the wallet/provider layer is just plumbing, but this is an attack surface by itself:

  • Injected provider confusion: historically many DApps trusted whatever appeared in window.ethereum. With multiple extensions installed, the "last injector wins" problem and wallet impersonation become realistic attack paths.
  • EIP-6963 abuse cases: EIP-6963 improves multi-wallet discovery, but the spec explicitly calls out provider-object tampering, wallet imitation/manipulation, and even malicious SVG icons as security concerns. During pentests, check whether the DApp pins a provider by stable wallet metadata and whether two announced providers can reuse or tamper with the same identifiers.
  • WalletConnect / session phishing: if the application or wallet does not validate the real origin against the claimed metadata, a phishing domain can still present a believable session request. This is especially relevant when the workflow is "connect wallet first, inspect later".
  • Blind chain switching: some flows request wallet_switchEthereumChain and immediately build a transaction assuming the switch succeeded. If the application keeps stale provider state or mixes RPCs from different chains, users can sign transactions against the wrong network context.

When reviewing a DApp, treat the wallet connection layer exactly like an authentication boundary: inspect provider discovery, event handling (accountsChanged, chainChanged), transaction building after chain switches, and whether external wallet metadata is reflected in the DOM without sanitization.

{{#ref}}
../blockchain/blockchain-and-crypto-currencies/web3-signing-workflow-compromise-safe-delegatecall-proxy-takeover.md
{{#endref}}

Signature-based approvals and gasless abuse

In many DApps, the most dangerous action is no longer an on-chain approve, but an off-chain signature that a relayer or router later cashes in.

  • permit / signature approvals: ERC-2612 allows changing allowance with a signed message. That means a phishing page or a compromised frontend can ask the victim to sign an approval that is later submitted by any relayer.
  • Relayer optionality: the ERC-2612 spec explicitly notes that a relayer gets a free option to submit or withhold a signed permit until deadline, so backend workflows that assume "signature received == action already happened" are wrong.
  • Typed-data UX gaps: wallets may render the domain and field names but still fail to explain the actual effect of the signature, especially when nested calldata, routers, or multicalls are involved.
  • Permit-drainer pattern: instead of asking for a visible approve, drainers increasingly prefer typed-data signatures that authorize a spender or relay path, because they are faster, cheaper, and less suspicious to the victim.

From a tester perspective, search the frontend and backend for permit, permit2, eth_signTypedData, personal_sign, isValidSignature, and any API that stores signatures for later broadcast. Confirm that nonces, deadlines, chain/domain separation, spender binding, and intended execution path are actually validated server-side or on-chain.

Off-chain state, finality and indexer desynchronization

DApps usually do not read raw chain state directly for every action. They rely on RPC providers, webhooks, indexers, caches, and message queues. This introduces new exploitation opportunities:

  • Reorg-sensitive accounting: if the backend credits a deposit, bridge transfer, or in-game balance on the first seen event instead of after enough confirmations/finality, the user may get spendable off-chain credit from a transaction that disappears after a reorg.
  • RPC split-brain: one component may simulate a call with one RPC endpoint while another component watches logs or broadcasts through a different provider. Inconsistent mempool visibility, lagging nodes, or chain-specific quirks can create race conditions or logic bypasses.
  • Indexer poisoning: custom indexers that trust every emitted event or decode logs without validating the emitting contract/address can import attacker-controlled data into databases, leaderboards, bridge pipelines, or reward systems.
  • Unsafe replay of backend actions: webhook-driven bots, settlement workers, or queue consumers often assume event processing is idempotent. If the same event can be replayed after retries or reorg rewinds, value-moving actions may execute more than once.

When attacking this layer, review how the DApp handles confirmations, reorg rollbacks, duplicate deliveries, and contract-address allowlists in event consumers. Recent production guidance for blockchain indexers stresses that forward-filling indexers must be reorg-aware and able to rewind to the common ancestor instead of treating the current tip as immutable.

Some examples from this post:

Wasting Funds: Forcing backend to perform transactions

In the scenario Wasted Crypto in Gas via Unrestricted API, the attacker can force the backend to call functions of a smart contract that will consume gas. The attacker, just sending an ETH account number and with no limits, will force backend to call the smart contract to register it, which will consume gas.

DoS: Poor transaction handling time

In the scenario Poor Transaction Time Handling Leads to DoS, it is explained that because the backend will keep the HTTP request open until a transaction is performed, a user can easily send several HTTP requests to the backend, which will consume all the resources of the backend and will lead to a DoS.

Backend<-->Blockchain desync - Race condition

In the scenario Poor Transaction Time Handling Leads to Race Condition, it is explained that in a game it was possible for the user to send a withdrawal request to the backend which would send the user coins, but while the transaction was still being processed, the user was able to use those coins to purchase items in the game, getting them for free.

Another example could be to use the same coins to purchase different items as the backend is immediately giving the item to the user without waiting for the transaction to be confirmed and therefore waiting for the user balance in the blockchain to be reduced.

Smart contract address validation

In the scenario Bridge Backend Lacks Smart Contract Address Validation it is explained how the backend was not properly checking the address of the smart contract, so it was possible for an attacker to deploy a fake smart contract, put funds on it, send the transaction to the backend and the backend would think that the user sent funds to the real smart contract and would give the user the tokens.

Mishandling of Asset Classes

In the scenario Mishandling of Asset Classes, it is explained that the backend confused a scam NFT in an address with 1 MATIC, therefore allowing an attacker to send hundreds of scam NFTs to the address and get 1 MATIC from the platform for each of them.

More recent DApps may expose relayer, bundler, or paymaster APIs to sponsor user actions. These systems introduce a classic Web2 abuse surface around a Web3 execution engine:

  • If the backend signs or sponsors arbitrary calldata or arbitrary UserOperation objects, an attacker may convert the API into a gas-burning primitive or a privileged execution oracle.
  • If validation only checks "this looks like our frontend submitted it", XSS or a compromised dependency can turn the victim browser into a signer for attacker-chosen sponsored actions.
  • If the system trusts that a later execution revert will undo the economic impact, it may still be exploitable because gas is already spent or the sponsor balance was already charged.

Keep this page generic, but note that account-abstraction-specific bugs are covered in more detail here:

{{#ref}}
../blockchain/blockchain-and-crypto-currencies/erc-4337-smart-account-security-pitfalls.md
{{#endref}}

References

{{#include ../banners/hacktricks-training.md}}