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.phantomaccédés sans action utilisateur - Pas de requêtes vers des endpoints
web3,wallet,cryptoen arrière-plan - Proxy sur
window.ethereumpour 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