Explorer
KNOW-PAT-073

Fingerprinting de wallets crypto navigateur - Detection et protection

Domaine
cybersecu
Type
pattern
Priorité
P2

Parent : [[INDEX-CYBERSECU]]

Fingerprinting de wallets crypto navigateur - Detection et protection

Problème

Un script sci.js s'exécute dans le navigateur à chaque visite. Déguisé en ImageCarouselManager (une classe carousel factice de 213 lignes, jamais instanciée), il scanne la présence de wallets crypto : Phantom (Solana), MetaMask (Ethereum), Trust Wallet, Coinbase Wallet, WalletConnect. Si un wallet est détecté, les informations sont envoyées au backend via Web3stats.php.

Contexte archive Ygg

  • Source : https://yggleak.top/fr/home/ygg-dossier
  • Fichiers : sci.js, Web3stats.php
  • Controller : CodeIgniter, clé d'accès d3ShGIbbX3
  • Fonction : collect() stocke le type de wallet, le user-agent, l'IP et le session_id
  • Tableau de bord : /web3stats?key=d3ShGIbbX3
  • But : Identifier, parmi les 6,6 millions d'utilisateurs, lesquels possèdent un wallet crypto et lequel

Technique de l'attaquant

1. Script sci.js (lignes 443-489)

const isPhantomInstalled = window.phantom?.solana?.isPhantom;
const provider = window.ethereum || window.web3?.currentProvider;

if (isPhantomInstalled || provider) {
  const walletType = isPhantomInstalled ? 'Phantom' : detectWalletType(provider);
  await sendWeb3Info(walletType); // POST → /web3stats/collect
}

function detectWalletType(provider) {
  const walletTypes = {
    isPhantom: 'Phantom',
    isMetaMask: 'MetaMask',
    isTrust: 'Trust Wallet',
    isCoinbaseWallet: 'Coinbase Wallet',
    isWalletConnect: 'WalletConnect'
  };
  return Object.entries(walletTypes)
    .find(([key]) => provider[key])?.[1] || 'Unknown Wallet';
}

2. Backend Web3stats.php

// collect() stocke le type de wallet, user-agent, IP, session_id
// Tableau de bord accessible à /web3stats?key=d3ShGIbbX3

Observation : Le script ne vole rien directement. Il identifie, parmi les 6,6 millions d'utilisateurs, lesquels possèdent un wallet crypto et lequel. C'est une technique de reconnaissance pour cibler les victimes.

Solution protectif — Détection et protection contre le fingerprinting wallets

1. Détecter le fingerprinting de wallets

// detect-wallet-fingerprinting.js — Détecter si un site scanne les wallets
(function() {
  'use strict';
  
  const originalPhantom = window.phantom;
  const originalEthereum = window.ethereum;
  const originalWeb3 = window.web3;
  
  let accessCount = {
    phantom: 0,
    ethereum: 0,
    web3: 0
  };
  
  // Proxy sur window.phantom
  Object.defineProperty(window, 'phantom', {
    get() {
      accessCount.phantom++;
      if (accessCount.phantom > 0 && accessCount.phantom <= 3) {
        console.warn(`[WALLET-DETECT] window.phantom accédé ${accessCount.phantom} fois`);
        // Stack trace pour identifier le script
        console.trace();
      }
      return originalPhantom;
    },
    set(value) {
      originalPhantom = value;
    },
    configurable: true
  });
  
  // Proxy sur window.ethereum
  Object.defineProperty(window, 'ethereum', {
    get() {
      accessCount.ethereum++;
      if (accessCount.ethereum > 0 && accessCount.ethereum <= 3) {
        console.warn(`[WALLET-DETECT] window.ethereum accédé ${accessCount.ethereum} fois`);
        console.trace();
      }
      return originalEthereum;
    },
    set(value) {
      originalEthereum = value;
    },
    configurable: true
  });
  
  // Surveiller les requêtes POST vers des endpoints suspects
  const originalFetch = window.fetch;
  window.fetch = function(...args) {
    const url = args[0];
    const options = args[1] || {};
    
    if (options.method === 'POST' && 
        (url.includes('web3') || url.includes('wallet') || url.includes('crypto'))) {
      console.warn(`[WALLET-DETECT] Requête POST suspecte: ${url}`);
      console.warn(`  Body: ${options.body}`);
    }
    
    return originalFetch.apply(this, args);
  };
  
  // Surveiller XMLHttpRequest
  const originalXHROpen = XMLHttpRequest.prototype.open;
  XMLHttpRequest.prototype.open = function(method, url, ...args) {
    if (method === 'POST' && 
        (url.includes('web3') || url.includes('wallet') || url.includes('crypto'))) {
      console.warn(`[WALLET-DETECT] XHR POST suspect: ${url}`);
    }
    return originalXHROpen.call(this, method, url, ...args);
  };
})();

2. Protection côté utilisateur

// wallet-isolation.js — Isoler les wallets des sites web
(function() {
  'use strict';
  
  // Option 1 : Bloquer l'accès aux wallets depuis des sites non approuvés
  const APPROVED_DOMAINS = [
    'app.uniswap.org',
    'app.aave.com',
    'opensea.io',
    'app.1inch.io'
  ];
  
  const currentDomain = window.location.hostname;
  
  if (!APPROVED_DOMAINS.includes(currentDomain)) {
    // Masquer les wallets pour ce domaine
    Object.defineProperty(window, 'ethereum', {
      get() { return undefined; },
      configurable: false
    });
    
    Object.defineProperty(window, 'phantom', {
      get() { return undefined; },
      configurable: false
    });
    
    console.warn(`[WALLET-ISOLATION] Wallets masqués pour ${currentDomain}`);
  }
})();

3. Extension de navigateur pour la protection

// background.js — Extension Chrome/Firefox pour protéger les wallets
chrome.webRequest.onBeforeRequest.addListener(
  function(details) {
    // Bloquer les requêtes vers des endpoints de tracking web3
    const suspiciousUrls = [
      /web3stats/,
      /wallet.*collect/,
      /crypto.*track/,
      /phantom.*detect/,
      /metamask.*detect/
    ];
    
    if (suspiciousUrls.some(pattern => pattern.test(details.url))) {
      console.warn(`[WALLET-BLOCK] Requête bloquée: ${details.url}`);
      return { cancel: true };
    }
    
    return { cancel: false };
  },
  { urls: ["<all_urls>"] },
  ["blocking", "requestBody"]
);

4. Pour les développeurs de sites — Ne PAS faire de fingerprinting

// anti-pattern — Ne JAMAIS scanner les wallets sans consentement
// ❌ MAUVAIS (comme sci.js)
function detectWallet() {
  if (window.ethereum) {
    sendToServer({ wallet: 'MetaMask', user: getUserId() }); // ❌ TRACKING
  }
}

// ✅ BON — Demander le consentement explicite
async function connectWallet() {
  const userConsent = await showConsentModal(
    'Ce site souhaite se connecter à votre wallet pour [RAISON CLAIRE].'
  );
  
  if (userConsent) {
    const accounts = await window.ethereum.request({ 
      method: 'eth_requestAccounts' 
    });
    return accounts;
  }
  
  return null;
}

5. Détection côté serveur

# detect_web3_fingerprinting.py — Détecter les tentatives de tracking wallets
from flask import Flask, request
import re

app = Flask(__name__)

@app.before_request
def detect_wallet_tracking():
    # Détecter les requêtes vers des endpoints de tracking
    suspicious_endpoints = [
        r'/web3.*',
        r'/wallet.*collect',
        r'/crypto.*track',
        r'/api/wallet.*detect'
    ]
    
    path = request.path
    
    for pattern in suspicious_endpoints:
        if re.match(pattern, path):
            # Loguer mais ne pas bloquer (pour l'analyse)
            app.logger.warning(f"[WALLET-TRACK-DETECT] Endpoint suspect: {path} from {request.remote_addr}")
    
    # Détecter les payloads contenant des types de wallets
    if request.is_json:
        data = request.get_json()
        wallet_fields = ['walletType', 'wallet', 'phantom', 'metamask', 'trust']
        
        if any(field in str(data).lower() for field in wallet_fields):
            app.logger.warning(f"[WALLET-TRACK-DETECT] Payload suspect: {data}")

# Bloquer les endpoints de tracking
@app.route('/web3stats', methods=['GET', 'POST'])
def block_web3stats():
    return {"error": "Wallet fingerprinting is not allowed"}, 403

Checklist de validation

  • Pas de scan automatique des wallets sans consentement
  • Pas de window.ethereum / window.phantom accédés sans action utilisateur
  • Pas de requêtes vers des endpoints web3, wallet, crypto en arrière-plan
  • Proxy sur window.ethereum pour détecter les accès suspects
  • Extension navigateur bloquant les endpoints de tracking wallets
  • Consentement explicite avant toute connexion wallet
  • Site ne collecte pas le type de wallet, uniquement l'adresse (si nécessaire)
  • Pas de tableau de bord de tracking des wallets (/web3stats?key=...)
  • Clé d'API pour le tracking wallets non hardcodée
  • Anonymisation des données wallet (hash de l'adresse, pas l'adresse en clair)

Anti-pattern associé

  • KNOW-ANT-017 — Browser wallet fingerprinting without user consent

Références

  • Archive Ygg : sci.js, Web3stats.php (YGGLeak)
  • OWASP : Privacy Risks