Skip to content

Instantly share code, notes, and snippets.

@greenido
Created November 4, 2024 23:31
Show Gist options
  • Save greenido/41fdc120bad04f3d44b9651688e57515 to your computer and use it in GitHub Desktop.
Save greenido/41fdc120bad04f3d44b9651688e57515 to your computer and use it in GitHub Desktop.
Chrome Extension example: Drive-by downloads
// DriveByProtector.js
class DriveByProtector {
constructor() {
this.suspiciousPatterns = {
fileTypes: /\.(exe|msi|dmg|pkg|deb|bat|cmd|sh|ps1|vbs|jar|dll|scr)$/i,
mimeTypes: [
'application/x-msdownload',
'application/x-msdos-program',
'application/x-executable',
'application/x-dosexec',
'application/octet-stream',
'application/x-javascript',
'application/java-archive'
],
suspiciousStrings: [
'eval(',
'document.write(',
'fromCharCode',
'String.fromCharCode',
'unescape(',
'escape(',
'atob(',
'btoa('
]
};
this.initializeProtection();
}
initializeProtection() {
// Initialize all protection mechanisms
this.monitorDOMChanges();
this.monitorNetworkRequests();
this.preventAutoDownloads();
this.monitorScriptExecution();
this.protectAgainstExploits();
this.monitorIframeActivity();
}
monitorDOMChanges() {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (this.isSuspiciousElement(node)) {
this.handleSuspiciousElement(node);
}
});
});
});
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
}
isSuspiciousElement(node) {
if (node.nodeName) {
const suspiciousElements = ['IFRAME', 'OBJECT', 'EMBED', 'APPLET'];
return suspiciousElements.includes(node.nodeName);
}
return false;
}
monitorNetworkRequests() {
// Monitor XHR
const originalXHR = window.XMLHttpRequest;
window.XMLHttpRequest = function() {
const xhr = new originalXHR();
const originalOpen = xhr.open;
const originalSend = xhr.send;
const self = this;
xhr.open = function() {
this._url = arguments[1];
return originalOpen.apply(this, arguments);
};
xhr.send = function() {
xhr.addEventListener('load', function() {
self.checkResponse(this);
});
return originalSend.apply(this, arguments);
};
return xhr;
};
// Monitor Fetch
const originalFetch = window.fetch;
window.fetch = function() {
return originalFetch.apply(this, arguments)
.then(response => {
this.checkResponse(response);
return response;
});
};
}
checkResponse(response) {
const contentType = response.getResponseHeader?.('content-type') ||
response.headers?.get('content-type');
if (contentType && this.suspiciousPatterns.mimeTypes.includes(contentType)) {
this.logThreat({
type: 'suspicious_download',
url: response._url || response.url,
contentType: contentType
});
return false;
}
return true;
}
preventAutoDownloads() {
// Override download attribute
document.addEventListener('click', (event) => {
const link = event.target.closest('a');
if (link?.hasAttribute('download')) {
event.preventDefault();
this.handleDownloadAttempt(link);
}
}, true);
// Monitor Blob URLs
const originalCreateObjectURL = URL.createObjectURL;
URL.createObjectURL = (blob) => {
if (this.isSuspiciousBlob(blob)) {
this.logThreat({
type: 'suspicious_blob',
mimeType: blob.type,
size: blob.size
});
return null;
}
return originalCreateObjectURL(blob);
};
}
isSuspiciousBlob(blob) {
return this.suspiciousPatterns.mimeTypes.includes(blob.type) ||
blob.size > 10 * 1024 * 1024; // Files larger than 10MB
}
monitorScriptExecution() {
// Monitor eval
const originalEval = window.eval;
window.eval = (code) => {
if (this.containsSuspiciousCode(code)) {
this.logThreat({
type: 'suspicious_eval',
code: code.substring(0, 100) // Log first 100 chars
});
return null;
}
return originalEval(code);
};
// Monitor new Function
const originalFunction = window.Function;
window.Function = function() {
const code = Array.from(arguments).join('');
if (this.containsSuspiciousCode(code)) {
this.logThreat({
type: 'suspicious_function',
code: code.substring(0, 100)
});
return function() {};
}
return originalFunction.apply(this, arguments);
};
}
containsSuspiciousCode(code) {
return this.suspiciousPatterns.suspiciousStrings.some(pattern =>
code.includes(pattern));
}
protectAgainstExploits() {
// Freeze important objects
Object.freeze(Object.prototype);
Object.freeze(Array.prototype);
Object.freeze(Function.prototype);
// Prevent prototype pollution
const preventPollution = (obj) => {
if (obj.constructor === Object) {
Object.seal(obj);
}
return obj;
};
// Override JSON parse
const originalParse = JSON.parse;
JSON.parse = function() {
const obj = originalParse.apply(this, arguments);
return preventPollution(obj);
};
}
monitorIframeActivity() {
// Monitor iframe creation
const createElementOriginal = document.createElement;
document.createElement = function() {
const element = createElementOriginal.apply(this, arguments);
if (arguments[0].toLowerCase() === 'iframe') {
this.handleIframeCreation(element);
}
return element;
};
// Monitor postMessage
window.addEventListener('message', (event) => {
this.handlePostMessage(event);
}, true);
}
handleIframeCreation(iframe) {
iframe.sandbox = 'allow-scripts'; // Restrict iframe capabilities
iframe.addEventListener('load', () => {
try {
// Check if iframe content is accessible
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
this.scanDocument(iframeDoc);
} catch (e) {
// Cross-origin iframe - can't access content
this.logThreat({
type: 'suspicious_iframe',
src: iframe.src
});
}
});
}
handlePostMessage(event) {
if (this.containsSuspiciousCode(event.data)) {
this.logThreat({
type: 'suspicious_postMessage',
origin: event.origin,
data: event.data.substring(0, 100)
});
event.preventDefault();
}
}
handleSuspiciousElement(element) {
const elementInfo = {
type: element.nodeName,
src: element.src || element.data,
id: element.id,
className: element.className
};
this.logThreat({
type: 'suspicious_element',
element: elementInfo
});
// Optionally remove or sanitize the element
element.remove();
}
handleDownloadAttempt(link) {
const url = link.href;
const filename = link.download || url.split('/').pop();
if (this.isSuspiciousDownload(url, filename)) {
this.logThreat({
type: 'blocked_download',
url: url,
filename: filename
});
return;
}
// Show confirmation dialog
if (confirm(`Do you want to download ${filename}?`)) {
window.location.href = url;
}
}
isSuspiciousDownload(url, filename) {
return this.suspiciousPatterns.fileTypes.test(filename) ||
url.includes('data:') ||
url.includes('blob:');
}
scanDocument(doc) {
// Scan for suspicious elements
const suspiciousElements = doc.querySelectorAll('iframe, object, embed, applet');
suspiciousElements.forEach(element => {
this.handleSuspiciousElement(element);
});
// Scan inline scripts
const scripts = doc.querySelectorAll('script');
scripts.forEach(script => {
if (this.containsSuspiciousCode(script.textContent)) {
this.logThreat({
type: 'suspicious_inline_script',
content: script.textContent.substring(0, 100)
});
script.remove();
}
});
}
logThreat(threat) {
// Add common threat information
threat.timestamp = new Date().toISOString();
threat.url = window.location.href;
threat.userAgent = navigator.userAgent;
// Log to console
console.warn('Security Threat Detected:', threat);
// Send to security monitoring system
this.reportThreat(threat);
// Trigger any registered callbacks
this.onThreatDetected?.(threat);
}
reportThreat(threat) {
// Implementation depends on your security infrastructure
// Example:
fetch('/api/security/threats', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(threat)
}).catch(error => console.error('Failed to report threat:', error));
}
}
// Initialize protection
const protector = new DriveByProtector();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment