Learn Ethical Hacking (#56) - Cryptocurrency Security - Attacking and Defending Digital Assets

avatar

Learn Ethical Hacking (#56) - Cryptocurrency Security - Attacking and Defending Digital Assets

leh-banner.jpg

What will I learn

  • Cryptocurrency attack surface -- wallets, exchanges, smart contracts, bridges, and the human layer that ties them all together;
  • Private key security -- how keys are generated, stored, and stolen, and why the entire security model reduces to one question;
  • Smart contract vulnerabilities -- reentrancy, integer overflow, access control failures, and flash loan attacks that drain millions in seconds;
  • Exchange security -- the long history of exchange hacks from Mt. Gox to Ronin Bridge, and why centralized custody is a single point of failure;
  • DeFi exploits -- oracle manipulation, sandwich attacks, approval phishing, and protocol-level vulnerabilities;
  • Wallet attacks -- phishing approval transactions, clipboard hijacking, and seed phrase theft;
  • Blockchain forensics -- tracing transactions, de-anonymizing users, and why "anonymous" is not the same as "private";
  • Defense: hardware wallets, multisig, smart contract auditing, and operational security for crypto holders.

Requirements

  • A working modern computer running macOS, Windows or Ubuntu;
  • Basic understanding of blockchain concepts;
  • Understanding of web security from episodes 11-28;
  • The ambition to learn ethical hacking and security research.

Difficulty

  • Intermediate

Curriculum (of the Learn Ethical Hacking Series):

Learn Ethical Hacking (#56) - Cryptocurrency Security - Attacking and Defending Digital Assets

Solutions to Episode 55 Exercises

Exercise 1: Data mapping for e-commerce (abbreviated).

#!/usr/bin/env python3
"""data_mapping.py -- personal data inventory for e-commerce"""

DATA_MAP = [
    {
        'touchpoint': 'Registration',
        'data': 'Name, email, password (bcrypt hash)',
        'storage': 'users DB (primary), daily backups (encrypted)',
        'access': 'Customer service, marketing (email only)',
        'retention': 'Account lifetime + 30 days after deletion',
        'legal_basis': 'Contract (Art 6(1)(b)) -- necessary to provide account',
    },
    {
        'touchpoint': 'Browsing',
        'data': 'Pages visited, click paths, session duration, IP address',
        'storage': 'Analytics DB, CDN logs, Google Analytics',
        'access': 'Marketing team, product team',
        'retention': '13 months (GA default), CDN logs 90 days',
        'legal_basis': 'Consent (Art 6(1)(a)) -- cookie banner opt-in required',
    },
    {
        'touchpoint': 'Ordering',
        'data': 'Items, shipping address, phone number, order history',
        'storage': 'Orders DB, warehouse system, backups',
        'access': 'Customer service, finance, warehouse staff',
        'retention': '7 years (tax obligation) for financial records',
        'legal_basis': 'Contract + Legal obligation (tax)',
    },
    {
        'touchpoint': 'Payment',
        'data': 'Card number, expiry, CVV (tokenized immediately)',
        'storage': 'Payment processor only (Stripe/Adyen) -- NOT in our DB',
        'access': 'None internally (tokenized at processor)',
        'retention': 'Processor handles per PCI DSS requirements',
        'legal_basis': 'Contract (Art 6(1)(b))',
    },
    {
        'touchpoint': 'Support',
        'data': 'Messages, attachments, ticket metadata',
        'storage': 'Helpdesk platform (Zendesk), email archive',
        'access': 'Customer service team',
        'retention': '3 years after ticket closure',
        'legal_basis': 'Legitimate interest (Art 6(1)(f)) -- service improvement',
    },
]

print("=== E-Commerce Data Map ===\n")
for entry in DATA_MAP:
    print(f"--- {entry['touchpoint']} ---")
    print(f"  Data: {entry['data']}")
    print(f"  Storage: {entry['storage']}")
    print(f"  Access: {entry['access']}")
    print(f"  Retention: {entry['retention']}")
    print(f"  Legal basis: {entry['legal_basis']}")
    print()

print("Top 3 privacy risks:")
print("  1. Browsing data in Google Analytics -- international transfer to")
print("     US servers, no DPIA conducted, consent mechanism may not meet")
print("     GDPR requirements (pre-ticked boxes)")
print("  2. Support tickets may contain payment data pasted by customers")
print("     (card numbers in free-text fields) -- unstructured PII exposure")
print("  3. Backups retain deleted user data beyond retention period --")
print("     'right to erasure' requests don't reach backup tapes")

The data mapping exercise is one of those things that sounds simple until you actually do it. Every organization thinks they know where their data lives. Then you start asking questions and discover that marketing has a copy in Mailchimp, the dev team has a staging database with production data from last year, and the helpdesk platform stores full conversation logs including whatever PII customers paste into the chat. The backup problem is especially tricky -- GDPR's right to erasure means you must delete data on request, but if that data exists in 47 backup tapes in an offsite vault, "deletion" becomes an operational nightmare.

Exercise 2: GDPR breach notification (abbreviated).

#!/usr/bin/env python3
"""breach_notification.py -- Article 33 + 34 response"""

ARTICLE_33 = {
    'filed': '14:30 UTC, 68 hours after discovery (within 72h)',
    'nature': 'Unauthorized access to customer database via unpatched '
              'Confluence server (CVE-2023-22515)',
    'data_affected': '100,000 records: names, email addresses, '
                     'bcrypt-hashed passwords',
    'likely_consequences': 'Credential stuffing risk (bcrypt hashes are '
                          'slow to crack but not impossible with GPU rigs). '
                          'Phishing risk using leaked email addresses.',
    'measures_taken': [
        'Confluence server isolated and patched within 2 hours of discovery',
        'All user passwords force-reset at application level',
        'Forensic investigation initiated (external IR firm engaged)',
        'Web application firewall rules updated to block Confluence exploits',
        'Full credential rotation for all service accounts on affected server',
    ],
    'dpo_contact': '[email protected], +31-20-555-0100',
}

print("=== Article 33 -- Supervisory Authority Notification ===\n")
print(f"  Filed: {ARTICLE_33['filed']}")
print(f"  Nature: {ARTICLE_33['nature']}")
print(f"  Data: {ARTICLE_33['data_affected']}")
print(f"  Consequences: {ARTICLE_33['likely_consequences']}")
print(f"  Measures:")
for m in ARTICLE_33['measures_taken']:
    print(f"    - {m}")

print("\n=== Article 34 -- Individual Notification ===\n")
print('  "We are writing to inform you that on [date], we discovered')
print('  unauthorized access to a database containing your name and')
print('  email address. Your password was stored in hashed form (bcrypt)')
print('  and we believe the immediate risk of compromise is low.')
print('  However, as a precaution:')
print('    - Change your password on our service immediately')
print('    - If you used the same password elsewhere, change it there too')
print('    - Be vigilant for phishing emails referencing our service')
print('  We have notified the relevant supervisory authority and are')
print('  conducting a full forensic investigation."')

The 68-hour filing is cutting it close but legal. The key thing most teams get wrong: they wait for the investigation to finish before filing. You do NOT need complete information at hour 72 -- you need a preliminary notification with what you know, and you commit to updates. Filing late with perfect information is worse than filing on time with partial information. The regulatary authority wants to know you're aware and acting, not that you have all the answers.

Exercise 3: AI chatbot DPIA (abbreviated).

#!/usr/bin/env python3
"""chatbot_dpia.py -- Data Protection Impact Assessment"""

DPIA = {
    'processing_description': 'AI-powered customer support chatbot processing '
        'customer queries via third-party API (US-based provider). Queries '
        'may contain: names, account numbers, order details, complaints, '
        'and potentially sensitive categories (health, financial).',
    'legal_basis': 'Legitimate interest (Art 6(1)(f)) for customer service '
        'improvement. Consent required for conversation log retention beyond '
        'immediate support purpose.',
    'risks': [
        ('Customer data sent to third-party AI API',
         'International transfer without adequate safeguards',
         'DPA with provider (SCCs + supplementary measures), '
         'PII detection + redaction before API call'),
        ('Conversation logs used for model training',
         'Purpose limitation violation',
         'Contractual prohibition on training, technical audit rights'),
        ('Free-text queries contain unpredictable PII',
         'Data minimization failure, scope creep',
         'Auto-redaction of detected PII patterns (SSN, card numbers, '
         'email) before processing'),
        ('AI may memorize personal data from conversations',
         'Storage limitation violation, right to erasure impossible',
         'Use stateless API mode (no conversation history at provider), '
         '30-day auto-delete of local logs'),
    ],
    'residual_risk': 'MEDIUM -- acceptable with mitigations in place. '
        'Free-text input means some PII will bypass auto-redaction. '
        'Regular review of redaction effectiveness required.',
}

print("=== DPIA: AI Customer Support Chatbot ===\n")
print(f"Processing: {DPIA['processing_description']}")
print(f"Legal basis: {DPIA['legal_basis']}")
print(f"Residual risk: {DPIA['residual_risk']}")
print("\nRisks and mitigations:")
for risk, impact, mitigation in DPIA['risks']:
    print(f"  Risk: {risk}")
    print(f"  Impact: {impact}")
    print(f"  Mitigation: {mitigation}")
    print()

The free-text input problem is what makes AI chatbot DPIAs so interesting. You can build the best PII redaction pipeline in the world and a customer will still type "my social security number is 123-45-6789 and my doctor said I have cancer" into the support chat. You cannot predict what personal data will appear in unstructured input, which means your data minimization strategy needs to be AGGRESSIVE -- redact known patterns, use stateless API modes, auto-delete logs, and accept that some PII leakage is a residual risk that needs monitoring ;-)


Episode 55 covered privacy and data protection -- the legal framework that gives individuals rights over their personal data and organizations obligations to protect it. We walked through the critical distinction between security (protecting data from unauthorized access) and privacy (protecting individuals from unauthorized USE of their data), GDPR's seven principles (lawfulness, purpose limitation, minimization, accuracy, storage limitation, integrity, and the accountability principle that trips everyone up), enforcement reality (Meta's 1.2 billion euro fine, Amazon's 746 million), the 72-hour breach notification clock, CCPA's private right of action ($100-750 per consumer per breach -- trial lawyers love this), DPIAs for high-risk processing, the pseudonymization vs anonymization distinction (Netflix and AOL proved that "removing names" is not anonymization), privacy by design (Apple proving you can have both privacy AND functionality), and the AI-privacy collision where employees paste customer data into chatbots without thinking about cross-border transfers.

Today we enter the most adversarial environment in all of computing.

For 55 episodes, every system we've attacked had some form of recourse. Credit card stolen? Chargeback. Database breached? Restore from backup. Wire transfer fraud? The bank might reverse it. The consequence of a security failure was always painful, but rarely permanent. Cryptocurrency removes that safety net entirely. Every transaction is final. Every smart contract is immutable. Every stolen private key means funds that are gone -- GONE -- with no customer service number to call, no chargeback to file, no regulator who can force a reversal.

This makes crypto security the ultimate stress test for everything we've learned. Web vulnerabilities become million-dollar exploits. Social engineering becomes wallet-draining phishing. Insider threats become rug pulls. Supply chain attacks become compromised wallet software. The attacks are the same. The stakes are orders of magnitude higher.

Here we go.

The Crypto Attack Surface

Before we look at individual attack categories, it helps to understand the full attack surface. Cryptocurrency sits at the intersection of cryptography, distributed systems, smart contracts, web applications, and human behavior -- which means attackers can target ANY of those layers:

#!/usr/bin/env python3
"""crypto_attack_surface.py -- mapping where the money gets stolen"""

ATTACK_SURFACE = {
    'layer_1_key_management': {
        'what': 'Private key generation, storage, and usage',
        'attacks': [
            'Weak random number generators (predictable keys)',
            'Seed phrase theft (photos, cloud backups, phishing)',
            'Clipboard hijacking (replacing addresses at paste time)',
            'Keyloggers and screen capture malware',
            'Physical theft of hardware wallets',
            'Social engineering ("support" asks for seed phrase)',
        ],
        'episode_connections': 'Episode 7 (passwords/keys), 8 (social engineering), '
                              '23 (client-side attacks), 45 (supply chain)',
    },
    'layer_2_smart_contracts': {
        'what': 'On-chain code that holds and moves funds',
        'attacks': [
            'Reentrancy (The DAO hack pattern)',
            'Integer overflow/underflow (pre-SafeMath)',
            'Access control failures (missing onlyOwner)',
            'Flash loan oracle manipulation',
            'Front-running / sandwich attacks',
            'Logic bugs in protocol mechanics',
        ],
        'episode_connections': 'Episode 22 (business logic flaws), 19 (deserialization -- '
                              'same concept: code execution via unexpected data paths)',
    },
    'layer_3_protocols': {
        'what': 'Bridges, DEXs, lending protocols, oracles',
        'attacks': [
            'Bridge validator compromise (Ronin)',
            'Oracle manipulation (price feed attacks)',
            'Governance attacks (flash loan voting)',
            'Liquidity pool drain (remove liquidity exploit)',
        ],
        'episode_connections': 'Episode 33 (AD attacks -- validator compromise is the '
                              'crypto equivalent of domain admin compromise)',
    },
    'layer_4_exchanges': {
        'what': 'Centralized exchanges holding customer funds',
        'attacks': [
            'Hot wallet key theft (insider or external)',
            'API key compromise (phishing, malware)',
            'Internal fraud (FTX-style misappropriation)',
            'Supply chain compromise of exchange software',
        ],
        'episode_connections': 'Episode 48 (insider threats), 45 (supply chain), '
                              '35 (cloud misconfig -- many exchanges run on AWS)',
    },
    'layer_5_human': {
        'what': 'Users, developers, protocol teams',
        'attacks': [
            'Approval phishing (unlimited token spend)',
            'Fake airdrops and token claim sites',
            'Rug pulls (dev drains liquidity)',
            'Impersonation scams on Discord/Telegram',
        ],
        'episode_connections': 'Episode 8 (social engineering), 46 (human factor), '
                              '39 (phishing infrastructure)',
    },
}

print("=== Cryptocurrency Attack Surface ===\n")
for layer, data in ATTACK_SURFACE.items():
    label = layer.replace('_', ' ').title()
    print(f"--- {label} ---")
    print(f"  Target: {data['what']}")
    print(f"  Attacks:")
    for attack in data['attacks']:
        print(f"    - {attack}")
    print(f"  Series connections: {data['episode_connections']}")
    print()

Notice how every attack category maps directly to techniques we've covered in this series. That is not a coincidence. Crypto did not invent new attack classes -- it created a new environment where existing attack classes have immediate, irrevocable financial consequences. A reentrancy bug in a traditional web application is a logic flaw. A reentrancy bug in a smart contract holding $100 million is a heist.

Private Key Security -- The One Question That Matters

Your cryptocurrency exists on the blockchain. Your private key is the ONLY thing that authorizes moving it. The entire security model of every blockchain reduces to one question: who has the private key?

#!/usr/bin/env python3
"""key_security.py -- how private keys are generated and stolen"""

KEY_FUNDAMENTALS = {
    'generation': {
        'what': 'A private key is a 256-bit random number. That is it.',
        'math': '2^256 possible keys -- more atoms in the universe than '
                'possible keys. Brute force is impossible IF the key was '
                'generated with proper randomness.',
        'critical_dependency': 'The ENTIRE system depends on the quality of '
            'the random number generator. A weak RNG = predictable keys = '
            'stolen funds. The Profanity vanity address generator used a '
            'weak RNG and attackers stole $160M from Wintermute in 2022 by '
            'predicting generated keys.',
    },
    'derivation': {
        'what': 'BIP-39 seed phrase (12 or 24 words) encodes the master key',
        'flow': 'Random entropy -> seed phrase -> master key -> '
                'HD derivation path -> individual account keys. One seed '
                'phrase generates an unlimited number of accounts across '
                'multiple blockchains.',
        'implication': 'Compromise the seed phrase = compromise EVERY account '
                      'derived from it. Every chain. Every token. Everything.',
    },
}

THEFT_VECTORS = [
    {
        'method': 'Malware (clipboard hijacker)',
        'how': 'Malware monitors clipboard for crypto address patterns '
               '(regex match: Bitcoin bc1/1/3 prefix, Ethereum 0x prefix). '
               'When detected, replaces with attacker address. User pastes, '
               'sends funds to attacker.',
        'prevalence': 'Extremely common. Dozens of active malware families.',
        'defense': 'ALWAYS verify the full destination address on hardware '
                  'wallet screen before confirming. Never trust clipboard.',
    },
    {
        'method': 'Seed phrase phishing',
        'how': 'Fake wallet app, fake browser extension, or fake support '
               'site asks user to "verify" or "recover" their wallet by '
               'entering their seed phrase. Instant drain.',
        'prevalence': 'The #1 crypto theft vector by volume. Metamask support '
                     'scams on Twitter/Discord are continuous.',
        'defense': 'NEVER enter seed phrase into any website. NEVER. '
                  'Legitimate wallets will never ask for your seed phrase.',
    },
    {
        'method': 'Supply chain (compromised wallet)',
        'how': 'Attacker compromises wallet software (Ledger supply chain '
               'attack 2023 -- malicious JavaScript injected via compromised '
               'NPM package). Or: fake wallet app in app store that looks '
               'identical to the real one.',
        'prevalence': 'Growing. The Ledger Connect Kit attack affected multiple '
                     'DeFi frontends simultaneously.',
        'defense': 'Download wallets ONLY from official sources. Verify '
                  'checksums. Hardware wallets provide isolation from '
                  'compromised host software.',
    },
    {
        'method': 'Weak randomness',
        'how': 'Key generated with predictable RNG (system clock, insufficient '
               'entropy, broken PRNG). Attacker regenerates possible keys and '
               'sweeps funds. The "blockchain bandit" stole ETH from accounts '
               'with weak keys -- literally scanning for private key = 1, 2, 3...',
        'prevalence': 'Rare for modern wallets, devastating when it happens.',
        'defense': 'Use established wallet software with audited key generation. '
                  'Hardware wallets generate keys on-device with hardware RNG.',
    },
]

print("=== Private Key Security ===\n")
for section, data in KEY_FUNDAMENTALS.items():
    print(f"--- {section.title()} ---")
    for key, value in data.items():
        print(f"  {key}: {value}")
    print()

print("=== Key Theft Vectors ===\n")
for vector in THEFT_VECTORS:
    print(f"--- {vector['method']} ---")
    print(f"  How: {vector['how']}")
    print(f"  Prevalence: {vector['prevalence']}")
    print(f"  Defense: {vector['defense']}")
    print()

The clipboard hijacker deserves special attention because it is devastatingly simple. A few lines of code that monitor the clipboard, match a regex pattern, and replace the address. The user copies a legitimate Bitcoin address from their exchange, pastes it into their wallet, hits send -- and the funds go to the attacker. The user never sees the swap because who actually compares a 34-character alphanumeric string character by character? This is exactly the kind of client-side attack we discussed in episode 23, but with irreversible financial consequences.

Smart Contract Vulnerabilities -- Bugs With Bounties

Smart contracts are programs that hold and transfer money. A bug in a traditional web application is a vulnerability. A bug in a smart contract is a vulnerability with a built-in bounty -- the bounty is the contract's entire balance.

// VULNERABLE: The DAO pattern -- withdraw function
// The external call BEFORE the state update creates the reentrancy window
function withdraw() public {
    uint balance = balances[msg.sender];
    // External call: sends ETH to msg.sender
    // If msg.sender is a contract, its receive() function executes
    (bool success, ) = msg.sender.call{value: balance}("");
    require(success);
    // State update AFTER external call -- the bug!
    balances[msg.sender] = 0;
}

// ATTACKER'S CONTRACT: re-enters withdraw() before balance is zeroed
contract Attacker {
    VulnerableContract target;

    receive() external payable {
        // This executes when target sends ETH
        // Balance has NOT been zeroed yet -- re-enter!
        if (address(target).balance > 0) {
            target.withdraw();  // drain again, and again, and again
        }
    }

    function attack() external payable {
        target.deposit{value: 1 ether}();
        target.withdraw();  // start the drain
    }
}

// FIXED: checks-effects-interactions pattern
function withdraw() public {
    uint balance = balances[msg.sender];
    // EFFECT: update state FIRST
    balances[msg.sender] = 0;
    // INTERACTION: external call LAST
    (bool success, ) = msg.sender.call{value: balance}("");
    require(success);
}
// Additional protection: use OpenZeppelin's ReentrancyGuard modifier

The DAO hack (June 2016) exploited this exact pattern to drain 3.6 million ETH -- worth about $60 million at the time. The consequences were so severe that the Ethereum community executed a hard fork to reverse the theft, which split the chain into Ethereum (ETH) and Ethereum Classic (ETC). A single reentrancy bug caused a philosophical schism in blockchain governance that persists to this day. That is the scale of consequences when smart contract bugs meet real money.

Flash Loan Attacks -- Borrow Billions, Pay Nothing

Flash loans are one of the most fascinating and dangerous innovations in DeFi. They let you borrow an unlimited amount of money with ZERO collateral -- the only condition is that you must repay the loan within the same transaction. If you don't repay, the entire transaction reverts as if it never happened:

#!/usr/bin/env python3
"""flash_loan_attacks.py -- borrowing billions to break protocols"""

FLASH_LOAN_MECHANICS = """
Traditional loan:
  1. Prove creditworthiness (credit check, collateral)
  2. Receive funds
  3. Use funds over weeks/months
  4. Repay with interest

Flash loan:
  1. Borrow ANY amount (no collateral, no credit check)
  2. Use funds within the SAME transaction
  3. Repay at end of transaction
  4. If not repaid: entire transaction reverts (never happened)
  Total time: one block (~12 seconds on Ethereum)

Why this is powerful for attackers:
  - Zero capital required (anyone can borrow $100M for one tx)
  - Zero risk (if attack fails, loan reverts, you lose only gas)
  - Can manipulate prices, drain pools, exploit logic bugs
  - All within a single atomic transaction
"""

NOTABLE_ATTACKS = [
    {
        'target': 'bZx Protocol',
        'year': 2020,
        'stolen': '$1M',
        'method': 'Flash loan -> manipulate price oracle on Uniswap -> '
                 'borrow against inflated collateral on bZx -> profit',
        'root_cause': 'bZx used spot price from Uniswap as oracle. '
                     'A large trade moves the spot price temporarily. '
                     'Flash loan provided the capital for that large trade.',
    },
    {
        'target': 'Cream Finance',
        'year': 2021,
        'stolen': '$130M',
        'method': 'Flash loan + reentrancy in token price calculation',
        'root_cause': 'Price oracle manipulation combined with reentrancy '
                     'in the lending pool contract. Two bugs chained together.',
    },
    {
        'target': 'Mango Markets (Solana)',
        'year': 2022,
        'stolen': '$114M',
        'method': 'Manipulated MNGO token price on thin-liquidity markets, '
                 'used inflated portfolio as collateral to borrow $114M',
        'root_cause': 'Oracle relied on low-liquidity markets where price '
                     'could be moved with relatively small capital. The '
                     'attacker (Avraham Eisenberg) publicly admitted it and '
                     'argued it was a "profitable trading strategy." He was '
                     'later arrested and convicted of fraud.',
    },
    {
        'target': 'Euler Finance',
        'year': 2023,
        'stolen': '$197M',
        'method': 'Flash loan + donation attack exploiting liquidation logic',
        'root_cause': 'Flawed health factor calculation allowed attacker to '
                     'create an under-collateralized position and then '
                     'liquidate it for profit. Funds were later returned '
                     'after negotiation with the attacker.',
    },
]

print(FLASH_LOAN_MECHANICS)
print("=== Notable Flash Loan Attacks ===\n")
for attack in NOTABLE_ATTACKS:
    print(f"[{attack['year']}] {attack['target']}: {attack['stolen']}")
    print(f"  Method: {attack['method']}")
    print(f"  Root cause: {attack['root_cause']}")
    print()

print("Defense against flash loan attacks:")
print("  1. Use time-weighted average price (TWAP) oracles, not spot prices")
print("  2. Chainlink decentralized oracle networks (multiple data sources)")
print("  3. Rate limiting on large operations within a single transaction")
print("  4. Separation of price-sensitive operations across multiple blocks")

The Mango Markets case is especially interesting because the attacker (Avraham Eisenberg) went on Twitter afterward and openly described what he did, arguing it was a legitimate "profitable trading strategy" -- just exploiting the protocol's rules as written. He was subsequently arrested by the FBI and convicted of commodities fraud and manipulation. The legal question of whether exploiting a smart contract's explicit logic constitutes fraud or just clever trading is still being sorted out across jurisdictions. From a security perspective, the lesson is clear: if your protocol can be drained by someone with enough capital to move the price feed, your protocol has a vulnerability regardless of what the lawyers eventually decide ;-)

Approval Phishing -- The Silent Drain

This is the most common attack on regular DeFi users, and it exploits a fundamental design pattern in ERC-20 tokens. When you interact with a DeFi protocol, you must first "approve" the protocol's contract to spend your tokens. Attackers create phishing sites that trick you into approving THEIR contract instead:

#!/usr/bin/env python3
"""approval_phishing.py -- the most common DeFi attack on users"""

ATTACK_FLOW = [
    'User sees "free airdrop" or "token claim" on Twitter/Discord',
    'User visits the phishing site (looks identical to real Uniswap/OpenSea)',
    'Site prompts wallet connection (MetaMask popup -- looks legitimate)',
    'Transaction popup: "Approve USDC spending" with amount: UNLIMITED',
    'User clicks "Confirm" without reading the details carefully',
    'Attacker contract now has unlimited access to user USDC balance',
    'Attacker calls transferFrom() to drain ALL approved tokens',
    'Funds gone. Irreversible. No recourse.',
]

DEFENSE = {
    'before_signing': [
        'READ the transaction details in your wallet popup',
        'Check the contract address you are approving',
        'Approve exact amounts, not unlimited (set custom approval)',
        'Bookmark legitimate DeFi sites -- never click links in DMs',
        'Use a separate "hot wallet" with limited funds for DeFi',
    ],
    'after_the_fact': [
        'Check existing approvals: revoke.cash or etherscan.io/tokenapprovalchecker',
        'Revoke approvals for contracts you no longer use',
        'Revoke unlimited approvals and re-approve with exact amounts',
        'Make approval review a monthly security habit',
    ],
    'recognition_signs': [
        'Unsolicited DM about airdrops, free tokens, exclusive access',
        'URL that looks almost right but is slightly wrong (uniswap.org vs unlswap.org)',
        'Urgency: "claim within 24 hours or lose your tokens"',
        'Approval request for tokens you did not initiate an action for',
    ],
}

print("=== Approval Phishing Attack Flow ===\n")
for i, step in enumerate(ATTACK_FLOW, 1):
    print(f"  {i}. {step}")

print("\n=== Defense ===\n")
for category, items in DEFENSE.items():
    label = category.replace('_', ' ').title()
    print(f"--- {label} ---")
    for item in items:
        print(f"  - {item}")
    print()

I cannot overstate how common this attack is. Every single day, hundreds of users lose funds to approval phishing. The social engineering is identical to what we discussed in episode 8 -- authority, urgency, scarcity. The technical mechanism is a legitimate blockchain feature (token approvals are how DeFi works) weaponized through deception. And the damage is amplified by the fact that most users approve UNLIMITED spending amounts because that's what DeFi frontends request by default. One bad approval gives the attacker access to your entire balance of that token, not just the amount you intended to swap.

Exchange Security -- The History of Losing Other People's Money

#!/usr/bin/env python3
"""exchange_hacks.py -- the track record that justifies self-custody"""

EXCHANGE_HACKS = [
    {
        'exchange': 'Mt. Gox',
        'year': 2014,
        'stolen': '850,000 BTC (~$450M at the time)',
        'cause': 'Long-running theft over years, catastrophic key management, '
                'undetected for months. Operated from Tokyo apartment.',
        'aftermath': 'Bankruptcy. Creditors waited 10 years for partial recovery. '
                    'The phrase "not your keys, not your coins" was born.',
    },
    {
        'exchange': 'Bitfinex',
        'year': 2016,
        'stolen': '120,000 BTC ($72M)',
        'cause': 'Multisig implementation flaw in BitGo integration. Attacker '
                'compromised signing keys.',
        'aftermath': 'Exchange survived. Issued BFX tokens to cover losses. '
                    'Stolen BTC recovered by DOJ in 2022 ($3.6B by then) -- '
                    'arrested a couple who laundered through Walmart gift cards.',
    },
    {
        'exchange': 'Coincheck',
        'year': 2018,
        'stolen': '$530M in NEM tokens',
        'cause': 'All NEM stored in a single hot wallet. No multisig. No cold '
                'storage separation for the majority of funds.',
        'aftermath': 'Exchange refunded all affected users from corporate reserves. '
                    'Monex Group acquired Coincheck afterward.',
    },
    {
        'exchange': 'Ronin Bridge (Axie Infinity)',
        'year': 2022,
        'stolen': '$625M',
        'cause': 'Social engineering via fake job offer on LinkedIn. North Korean '
                'Lazarus Group compromised 5 of 9 validator keys through a '
                'trojanized PDF sent during the fake interview process.',
        'aftermath': 'Attributed to DPRK state actors. Theft went undetected for '
                    '6 DAYS because monitoring failed to alert on validator key '
                    'usage patterns. US Treasury sanctioned related addresses.',
    },
    {
        'exchange': 'FTX',
        'year': 2022,
        'stolen': '~$8B missing',
        'cause': 'NOT a hack -- fraud. Customer deposits secretly lent to sister '
                'company Alameda Research for trading. When Alameda lost the money, '
                'customer funds were gone.',
        'aftermath': 'Sam Bankman-Fried convicted on 7 federal charges. Sentenced to '
                    '25 years. The largest financial fraud in crypto history.',
    },
]

print("=== History of Exchange Hacks ===\n")
for hack in EXCHANGE_HACKS:
    print(f"[{hack['year']}] {hack['exchange']}: {hack['stolen']}")
    print(f"  Cause: {hack['cause']}")
    print(f"  Aftermath: {hack['aftermath']}")
    print()

print("Pattern: centralized custody is a single point of failure.")
print("Self-custody (hardware wallet) eliminates exchange risk")
print("but introduces key management risk. Choose your risk model.")

The Ronin Bridge hack connects directly to what we covered in episodes 8 (social engineering) and 45 (supply chain attacks). Lazarus Group sent a fake job offer to a senior engineer at Sky Mavis (the company behind Axie Infinity). The engineer went through a fake interview process that included opening a trojanized PDF. That PDF compromised the engineer's machine, which had access to validator keys. Five of nine keys compromised, and $625 million stolen. The attack went undetected for SIX DAYS because the monitoring system did not alert on unusual validator signing activity. Every IR and monitoring lesson from episode 51 applies here -- if your $625M bridge has 9 validator keys and you're not alerting on unusual signing paterns, you've already lost.

Blockchain Forensics -- Following the Money

One of the most common misconceptions about cryptocurrency is that it's "anonymous." Bitcoin, Ethereum, and most major blockchains are NOT anonymous -- they are pseudonymous. Every transaction is permanently recorded on a public ledger. You don't need a warrant to read it. You don't need special access. Anyone with a browser can trace every transaction ever made.

#!/usr/bin/env python3
"""blockchain_forensics.py -- tracing transactions on public ledgers"""

FORENSIC_TECHNIQUES = {
    'cluster_analysis': {
        'what': 'Group addresses belonging to the same entity. If address A '
                'and address B appear as inputs in the same transaction, they '
                'are controlled by the same wallet (common-input-ownership '
                'heuristic).',
        'tools': 'Chainalysis, Elliptic (commercial). Breadcrumbs.app (free, basic).',
        'effectiveness': 'High for Bitcoin (UTXO model makes clustering easy). '
                        'Harder for Ethereum (account model, but still possible '
                        'via gas payment patterns).',
    },
    'exchange_identification': {
        'what': 'Exchanges have known addresses. When stolen funds move to an '
                'exchange deposit address, the attacker is identified via KYC. '
                'This is the most common resolution path for crypto theft.',
        'tools': 'Exchange address databases maintained by forensics companies. '
                'Most major exchange addresses are labeled on Etherscan.',
        'effectiveness': 'Very high. Exchanges cooperate with law enforcement. '
                        'Most attackers eventually need to convert to fiat.',
    },
    'timing_analysis': {
        'what': 'Transaction timestamps correlate with timezone and activity '
                'patterns. An attacker who always transacts between 9 AM and '
                '6 PM Moscow time is probably in that timezone.',
        'tools': 'Manual analysis of transaction timestamps.',
        'effectiveness': 'Supplementary. Useful for narrowing suspects when '
                        'combined with other techniques.',
    },
    'dust_attacks': {
        'what': 'Send tiny amounts (dust) to target addresses. When the target '
                'spends, the dust moves with their other UTXOs, linking '
                'previously unconnected addresses via the common-input heuristic.',
        'tools': 'Custom scripts. Attackers use this offensively (de-anonymize '
                'targets). Forensic analysts use it defensively (trace flows).',
        'effectiveness': 'Moderate. Sophisticated users recognize dust and '
                        'do not spend it. But most users are not sophisticated.',
    },
    'mixer_deanonymization': {
        'what': 'Tornado Cash (Ethereum) and CoinJoin (Bitcoin) attempt to break '
                'transaction links. Forensics companies claim partial '
                'de-anonymization via timing, amount, and behavioral analysis.',
        'tools': 'Chainalysis Reactor (claims Tornado Cash tracing). Academic '
                'research papers on CoinJoin deanonymization.',
        'effectiveness': 'Debated. Perfect mixing is theoretically untraceable. '
                        'In practice, user errors (depositing and withdrawing '
                        'the same amount, same timing) reduce anonymity.',
    },
}

print("=== Blockchain Forensic Techniques ===\n")
for name, data in FORENSIC_TECHNIQUES.items():
    label = name.replace('_', ' ').title()
    print(f"--- {label} ---")
    print(f"  What: {data['what']}")
    print(f"  Tools: {data['tools']}")
    print(f"  Effectiveness: {data['effectiveness']}")
    print()
# Basic Ethereum address investigation
# Every transaction is public -- no warrant needed

# Check address balance and transaction history
curl -s "https://api.etherscan.io/api?module=account&action=txlist&address=0xADDRESS&apikey=YOUR_KEY" | python3 -c "
import sys, json
data = json.load(sys.stdin)
txs = data.get('result', [])
print(f'Total transactions: {len(txs)}')
for tx in txs[:5]:
    direction = 'OUT' if tx['from'].lower() == '0xaddress' else 'IN'
    value_eth = int(tx['value']) / 1e18
    print(f'  {direction} {value_eth:.4f} ETH to/from {tx[\"to\"][:16]}...')
"

# Bitcoin address investigation
curl -s "https://blockchain.info/rawaddr/bc1qADDRESS" | python3 -c "
import sys, json
data = json.load(sys.stdin)
print(f'Total received: {data[\"total_received\"] / 1e8:.8f} BTC')
print(f'Total sent: {data[\"total_sent\"] / 1e8:.8f} BTC')
print(f'Transaction count: {data[\"n_tx\"]}')
"

The pseudonymity vs anonymity distinction is critical and mirrors what we discussed in episode 55 about pseudonymization under GDPR. Just as "removing names from a dataset" does not make it anonymous (Netflix and AOL proved this), using a cryptocurrency address instead of a name does not make transactions anonymous. The blockchain IS the transaction log. Every transfer, every swap, every bridge crossing is recorded permanently and publicly. Forensics companies like Chainalysis have built billion-dollar businesses on the fact that following money on a blockchain is, in many ways, easier than following money through the traditional banking system.

Defense -- Securing Crypto Assets

The defense strategy depends on who you are and what you're protecting:

#!/usr/bin/env python3
"""crypto_defense.py -- layered security for digital assets"""

INDIVIDUAL_DEFENSE = {
    'cold_storage': {
        'what': 'Hardware wallet (Ledger, Trezor) for long-term holdings',
        'why': 'Private key never touches an internet-connected device. '
               'Even if your computer is fully compromised, the attacker '
               'cannot extract the key from the hardware wallet.',
        'critical_rule': 'Verify EVERY transaction on the hardware wallet '
                        'screen before confirming. The hardware wallet is '
                        'your last line of defense against clipboard hijackers '
                        'and compromised frontends.',
    },
    'seed_security': {
        'what': 'Seed phrase stored offline, NEVER digitally',
        'why': 'No photos. No cloud backup. No notes app. No email draft. '
               'Write on paper or stamp on metal. Store in a secure location. '
               'Consider splitting across multiple locations (Shamir backup).',
        'critical_rule': 'No legitimate service, wallet, or support agent will '
                        'EVER ask for your seed phrase. If someone asks, it is '
                        'a scam. 100% of the time. No exceptions.',
    },
    'approval_hygiene': {
        'what': 'Regular review and revocation of token approvals',
        'why': 'Every DeFi interaction leaves a token approval that persists '
               'until you explicitly revoke it. Old approvals on abandoned or '
               'compromised contracts are attack vectors.',
        'critical_rule': 'Use revoke.cash monthly. Revoke all unlimited approvals. '
                        'Re-approve with exact amounts when needed.',
    },
    'wallet_separation': {
        'what': 'Separate wallets for different risk levels',
        'why': 'Vault wallet (hardware, cold): 90%+ of holdings, rarely used. '
               'Trading wallet (hot): small amounts for active DeFi. '
               'Burner wallet: for untrusted sites, airdrops, new protocols.',
        'critical_rule': 'Never connect your vault wallet to a random website. '
                        'If a site asks for wallet connection, use the burner.',
    },
}

PROTOCOL_DEFENSE = {
    'smart_contract_security': [
        'Use checks-effects-interactions pattern (prevent reentrancy)',
        'Use OpenZeppelin libraries (battle-tested, audited implementations)',
        'Multiple independent audits before mainnet deployment',
        'Formal verification for contracts holding >$10M',
        'Bug bounty program on Immunefi (align incentives with white hats)',
    ],
    'operational_security': [
        'Multisig treasury (3-of-5 or higher for protocol funds)',
        'Timelock on governance changes (24-48 hour delay minimum)',
        'Rate limiting on withdrawals (detect unusual drain patterns)',
        'Decentralized oracle networks (Chainlink, not spot price)',
        'Circuit breaker (pausable contracts for emergency shutdown)',
    ],
    'transparency': [
        'Proof of reserves (public attestation of solvency)',
        'Open-source contracts (verifiable on block explorer)',
        'Published audit reports (not just "audited by X")',
        'Real-time monitoring dashboards for protocol health',
    ],
}

print("=== Individual Defense ===\n")
for name, data in INDIVIDUAL_DEFENSE.items():
    label = name.replace('_', ' ').title()
    print(f"--- {label} ---")
    print(f"  What: {data['what']}")
    print(f"  Why: {data['why']}")
    print(f"  CRITICAL: {data['critical_rule']}")
    print()

print("=== Protocol Defense ===\n")
for category, items in PROTOCOL_DEFENSE.items():
    label = category.replace('_', ' ').title()
    print(f"--- {label} ---")
    for item in items:
        print(f"  - {item}")
    print()

The wallet separation strategy is essentially the principle of least privilege from episode 53 applied to personal finance. Your vault wallet (hardware, offline) is your Tier 0 system -- the domain controller equivalent. You do NOT expose it to random websites the same way you do not log into your domain controller from a regular workstation. Your burner wallet is the expendable workstation -- if it gets compromised, you lose the gas money in it, not your life savings.

The AI Slop Connection

AI is generating smart contract code that contains the exact vulnerabilities we discussed. AI assistants produce Solidity contracts with reentrancy bugs, missing access controls, and unchecked arithmetic -- because they learned from thousands of vulnerable contracts on GitHub. The developer deploys the AI-generated contract, funds it, and the attacker exploits the bug the AI introduced.

AI-generated Solidity problems:

1. Reentrancy:
   AI generates: external call before state update (The DAO pattern)
   Should be: checks-effects-interactions (state update first)

2. Access control:
   AI generates: function withdraw() public { ... }
   Should be: function withdraw() public onlyOwner { ... }

3. Integer handling:
   AI generates: unchecked arithmetic in Solidity <0.8.0
   Should be: SafeMath library or Solidity 0.8+ (built-in overflow checks)

4. Oracle usage:
   AI generates: use Uniswap spot price as oracle
   Should be: Chainlink TWAP oracle (resistant to flash loan manipulation)

5. Approval patterns:
   AI generates: approve(spender, type(uint256).max) -- unlimited approval
   Should be: approve(spender, exactAmount) -- minimum necessary

The pattern is identical to every other domain we have seen:
AI optimizes for "it compiles and deploys" not "it resists attack."
A contract that compiles is not a contract that is secure.

On the defensive side, AI-powered auditing tools (Slither, Mythril, Semgrep for Solidity) are getting better at catching known vulnerability patterns. But the arms race is asymmetric: AI generates vulnerable contracts faster than AI can audit them, and a single missed bug in a contract holding millions is all it takes. Human auditors remain essential for logic-level vulnerabilities that automated tools cannot reason about -- the business logic flaws from episode 22 are just as prevalent in smart contracts as they are in web applications.

What Comes Next

Cryptocurrency security brings everything we have learned in this series into the most adversarial, highest-stakes environment in computing. The attacks are familiar -- phishing, supply chain compromise, logic bugs, insider threats -- but the consequences are final and irrevocable. The defenses are also familiar -- least privilege, defense in depth, key management, monitoring -- but they must be applied with zero tolerance for failure. The single-mistake finality of crypto is a preview of what security looks like in other domains where digital systems control physical assets. When a software vulnerability can unlock a door, shut down a power grid, or disable a medical device, the same "one bug, one loss" dynamic applies -- and the stakes go from financial to physical. How do you secure systems that live inside cars, factories, hospitals, and homes?

Exercises

Exercise 1: Audit a simple Solidity smart contract for vulnerabilities. Use the "Damn Vulnerable DeFi" challenges (https://www.damnvulnerabledefi.xyz/) -- complete at least 2 challenges. Document: (a) the vulnerability in each contract, (b) your exploit code, (c) the fix. Focus on reentrancy and access control challenges. Save to ~/lab-notes/defi-audit.md.

Exercise 2: Check your own Ethereum wallet (or a test wallet) for token approvals using revoke.cash or Etherscan's token approval checker. Document: (a) how many active approvals exist, (b) which contracts have unlimited approval, (c) whether any approvals are for contracts you no longer use. Revoke unnecessary approvals. If you don't have an Ethereum wallet, create a test wallet on a testnet and interact with a test DEX to generate approvals. Save to ~/lab-notes/approval-audit.md.

Exercise 3: Research the Ronin Bridge hack (March 2022, $625M). Document: (a) the bridge architecture (validator key model -- how many validators, what threshold), (b) how the attacker obtained 5 of 9 validator keys (social engineering via fake job offer on LinkedIn), (c) how long the theft went undetected (6 days) and why, (d) what detection mechanisms failed, (e) what the bridge could have done differently (separate key storage, anomaly detection on validator signing, shorter validator key rotation). Save to ~/lab-notes/ronin-bridge-analysis.md.


Thanks for reading!

@scipio



0
0
0.000
2 comments
avatar

Thanks for your contribution to the STEMsocial community. Feel free to join us on discord to get to know the rest of us!

Please consider delegating to the @stemsocial account (85% of the curation rewards are returned).

Consider setting @stemsocial as a beneficiary of this post's rewards if you would like to support the community and contribute to its mission of promoting science and education on Hive. 
 

0
0
0.000