این کد یک حمله ساده را نشان میدهد که ترکیبی از استفاده پس از آزادسازی حافظه (Use-After-Free) و آلودگی پروتوتایپ (Prototype Pollution) در محیط Node.js است. با استفاده از WeakMap و TypedArray، میتوانیم به حافظه آزاد شده دسترسی پیدا کرده و سپس پروتوتایپ را آلوده کنیم و باعث تغییرات ناخواسته در تمامی اشیاء جدید سیستم شویم.
استفاده پس از آزادسازی حافظه:
در مرحله اول، یک TypedArray ایجاد و در WeakMap ذخیره میشود. پس از حذف مرجع محلی به این آرایه، حافظه برای جمع آوری زباله (Garbage Collection) آماده میشود. این به ما امکان میدهد که پس از آزادسازی حافظه دوباره به داده ها دسترسی پیدا کنیم و شرایط Use-After-Free را فعال کنیم.
آلودگی پروتو تایپ:
پس از دسترسی مجدد به حافظه آزاد شده، از این وضعیت برای اجرای حمله آلودگی پروتوتایپ استفاده میکنیم. با تزریق یک ویژگی جدید به Object.prototype جهانی، تمامی اشیاء سیستم میتوانند به این ویژگی جدید دسترسی پیدا کنند که یک آسیب پذیری امنیتی جدی ایجاد میکند.
server:
const express = require(‘express’);
const bodyParser = require(‘body-parser’);
const app = express();
let weakMap = new WeakMap(); // A WeakMap to hold weak objects
let objReference = null;
let isProcessing = false; // A variable to manage the Race Condition
app.use(bodyParser.json());
app.post(‘/trigger_gc’, (req, res) => {
// Step 1: Create a TypedArray and store it in WeakMap
let typedArray = new Uint8Array(10); // A typed array with binary data
for (let i = 0; i < typedArray.length; i++) {
typedArray[i] = i * 10; // Initialize the array with values
}
weakMap.set(typedArray, ‘weak_value’); // Store the array in WeakMap
objReference = typedArray; // Keep a reference to the array
// Step 2: Release the local reference
typedArray = null; // Remove the local reference
// Check if the prototype has already been polluted
if (Object.prototype.pollutedProperty) {
console.log(‘Prototype already polluted:’, Object.prototype.pollutedProperty);
} else {
console.log(‘Prototype not polluted yet.’);
}
res.status(200).send(‘TypedArray created and ready for GC’);
});
app.post(‘/access_after_gc’, (req, res) => {
setTimeout(() => {
if (!isProcessing) {
isProcessing = true;
// Thread 1: Simulate accessing the array after GC
setTimeout(() => {
if (objReference && weakMap.has(objReference)) {
let data = Array.from(objReference).toString();
// Inject into prototype (Prototype Pollution)
Object.prototype.pollutedProperty = ‘Prototype Pollution Detected’;
// Check the prototype after injection
console.log(‘Prototype polluted with:’, Object.prototype.pollutedProperty);
// Send the response to the client
res.status(200).send(‘Use-After-Free and Prototype Pollution detected: ‘ + data);
} else {
res.status(200).send(‘TypedArray has been garbage collected.’);
}
isProcessing = false;
}, 1000); // Delay to simulate thread 1
// Thread 2: Modify the array data
setTimeout(() => {
if (objReference) {
objReference[0] = 231; // Change the value of the TypedArray
console.log(‘Data changed to:’, Array.from(objReference));
}
}, 500); // Delay for thread 2
} else {
res.status(200).send(‘Race condition in progress…’);
}
}, 2000);
});
// Start the server on port 4000
app.listen(4000, () => {
console.log(‘Server running on port 4000’);
});
const axios = require(‘axios’);
async function testUseAfterFreeWithPrototypePollution() {
try {
// Step 1: Send a request to create a TypedArray
let response = await axios.post(‘http://localhost:4000/trigger_gc');
console.log(‘Server response (create TypedArray):’, response.data);
// Step 2: Attempt to access the TypedArray after GC and check for Prototype Pollution
response = await axios.post(‘http://localhost:4000/access_after_gc');
console.log(‘Server response (access after GC):’, response.data);
// Check if the prototype has been polluted
if (Object.prototype.pollutedProperty) {
console.log(‘Prototype Pollution Detected:’, Object.prototype.pollutedProperty);
} else {
console.log(‘No Prototype Pollution Detected.’);
}
// Create a new object to check if the prototype has been updated
const newObj = {};
if (newObj.pollutedProperty) {
console.log(‘New Object Detected Prototype Pollution:’, newObj.pollutedProperty);
} else {
console.log(‘New Object has no Prototype Pollution.’);
}
} catch (error) {
console.error(‘Error:’, error.message);
}
}
// Run the test
testUseAfterFreeWithPrototypePollution();
این کد یک حمله ساده را نشان میدهد که به مهاجم اجازه میدهد از ضعف در مدیریت حافظه جاوا اسکریپت برای اجرای هر دو نوع حمله استفاده پس از آزادسازی حافظه (Use-After-Free) و آلودگی پروتو تایپ (Prototype Pollution) بهره برداری کند. با آلوده کردن پروتوتایپ، این کد تمام اشیاء موجود در سیستم را تحت تأثیر قرار میدهد که میتواند یک ریسک امنیتی جدی ایجاد کند.
نتیجه گیری:
این تنها یک نسخه ابتدایی از چنین حملهای است. از آنجا که ضعفهای موجود در مدیریت حافظه و پروتوتایپهای جاوا اسکریپت میتوانند آسیبپذیریهای جدی ایجاد کنند، اتخاذ بهترین روشها برای مدیریت حافظه و جلوگیری از آلودگی پروتوتایپ ضروری است.